Autodesk Maya 环境设置

概述

为您和您的团队设置 Maya 环境。轻松交付您的工具并集成脚本。在团队设置中自动设置并添加回调以管理场景首选项和源代码控制。

什么是Maya环境?

Maya 具有特定的环境变量,用于定义查找脚本和资源的位置。

将您自己的路径添加到这些变量中,以便我们可以将脚本和资源存储在我们想要的任何位置。

Maya 环境脚本

修改 Maya 环境最常见的方法是通过首选项目录中的三个脚本文件,即 Maya.env、userSetup.mel 和 userSetup.py。Maya 在启动期间按上述顺序评估这些脚本,从而为我们的工具环境注入路径提供了机会。


“如果我想组织我的附加组件以便我可以轻松地打开和关闭东西怎么办?”

“作为一名技术艺术家,我不想在修改最终用户的 Maya 环境时处理预先存在的配置信息。”


幸运的是,上面提到的三个文件还有一个替代方案,即 Maya 模块。

                                                                                                                                                                                                                                            

Maya 模块

Maya 模块是我们为修改 Maya 环境而创建的 .mod 文件。首先,查看 Maya 的安装目录/模块。

– Windows 示例路径:“C:\Program Files\Autodesk\Maya2018\modules”。 

Fbx、Substance、Xgen 和 Houdini Engine 都使用这种方法在 Maya 中引导他们的环境。我们将在 Maya 首选项目录的模块目录中创建自己的 .mod 文件。

在模块文件中,您指定“Maya 站点”文件夹的路径。在右侧,您可以看到我们将使用的站点文件夹的结构。

在启动过程中,子文件夹名称“icons”、“shelves”和“scripts”会自动添加到 Maya 环境中。


手动实现一个模块

首先,转到 Maya 用户首选项目录并创建一个名为“modules”的文件夹。

将该目录放在根目录中,而不是放在 Maya 版本文件夹中,这会导致为所有版本的 Maya 加载所有模块。

如果您需要特定的版本控制,您还可以在任何给定的 Maya 版本首选项文件夹中创建一个模块目录。



接下来,在您刚刚创建的模块文件夹中创建一个 .mod 文件(命名为 LCG.mod)。将下面的内容放入 .mod 文件中,然后更改您认为合适的路径。

Maya module file contents

+ LCG 1.0 C:/Code/LCG/maya
PYTHONPATH += C:/Code/LCG/python
PYTHONPATH += C:/Code/LCG/python/lcg/lib
MAYA_PLUG_IN_PATH += C:/Code/LCG/python/lcg/maya/plugin
MAYA_SHELF_PATH += C:/Code/LCG/maya/shelves

第一行告诉 Maya 我们要添加 (+) 这个模块。我们给它一个名字和一个版本,以及我们“Maya 站点”的路径。Maya 在启动过程中会自动添加“icons”和“scripts”文件夹。为了让书架在 Maya 中自动加载,我们将路径添加到 MAYA_SHELF_PATH 环境变量。


验证 Maya 环境

启动 Maya,将下面的代码复制并粘贴到脚本编辑器中并运行它以验证路径是否已添加到您的环境中。

check Maya environment variable

import pymel.util

print '-------------Start Maya Script Paths----------------------'
for path in pymel.util.getEnv("MAYA_SCRIPT_PATH").split(';'):
    print 'Script path - %s' % path
print '-------------End Maya Script Paths----------------------\n'

print '-------------Start Maya Icon Paths----------------------'    
for path in pymel.util.getEnv("XBMLANGPATH").split(';'):
    print 'Icon path - %s' % path
print '-------------End Maya Icon Paths----------------------\n'  

print '-------------Start Maya Shelf Paths----------------------'      
for path in pymel.util.getEnv("MAYA_SHELF_PATH").split(';'):
    print 'Shelf path - %s' % path
print '-------------End Maya Shelf Paths----------------------\n'  

print '-------------Start Maya Python Paths----------------------'     
for path in pymel.util.getEnv("PYTHONPATH").split(';'):
    print 'Python path - %s' % path
print '-------------End Maya Python Paths----------------------\n' 

print '-------------Start Maya Plug in Paths----------------------'
for path in pymel.util.getEnv("MAYA_PLUG_IN_PATH").split(';'):
    print 'Plugin path - %s' % path
print '-------------End Maya Plug in Paths----------------------\n'  
  
import sys
print '-------------Start Sys Paths----------------------'
for path in sys.path:
    print 'Sys path - %s' % path
print '-------------End Sys Paths----------------------'

感谢 Maya 模块,我们在不处理 Maya.env、userSetup.mel 或 userSetup.py 的情况下附加到 Maya 环境。随意创建任意数量的模块。每个项目/团队/游戏/自由职业者一个 - 没有限制。


“我想为我的团队自动创建此设置?”

“我希望 Maya 站点和框架能够为团队的每个成员存储在任何随机位置。


通过 Maya Python 插件进行设置。它灵活、易于维护且易于实现。

自动安装模块

Maya Python 插件是我们从 Maya 插件管理器加载的 .py 文件。看看下面它是多么容易实现。

将代码复制并粘贴到 .py 文件中,然后从 Maya 插件管理器中加载它,当您在 Maya 插件管理器中选中加载复选框时,您将获得打印语句。

Python Maya plugin - barebones

import maya.api.OpenMaya as OpenMaya
import traceback


def maya_useNewAPI():
    """
    The presence of this function tells Maya that the plugin produces, and
    expects to be passed, objects created using the Maya Python API 2.0.
    """
    pass


class LCGInitialize(OpenMaya.MPxCommand):
    kPluginCmdName = "lcgInitialize"

    @staticmethod
    def cmdCreator():
        return LCGInitialize()

    def __init__(self):
        super(LCGInitialize, self).__init__()

    def doIt(self, argList):
        raise Exception('Plugin not supposed to be invoked - only loaded or unloaded.')



def initializePlugin(obj):
    """
    :param obj: OpenMaya.MObject
    :return: None
    """
    plugin = OpenMaya.MFnPlugin(obj, 'LCG United', '1.0', 'Any')
    try:
        plugin.registerCommand(LCGInitialize.kPluginCmdName, LCGInitialize.cmdCreator)
        load()

    except Exception, e:
        raise RuntimeError, 'Failed to register command: %s\nDetails:\n%s' % (e, traceback.format_exc())


def uninitializePlugin(obj):
    """
    :param obj: OpenMaya.MObject
    :return: None
    """
    plugin = OpenMaya.MFnPlugin(obj)
    try:
        teardown()
        plugin.deregisterCommand(LCGInitialize.kPluginCmdName)
    except Exception, e:
        raise RuntimeError, 'Failed to unregister command: %s\nDetails:\n%s' % (e, traceback.format_exc())


def load():
    """
    On initialization
    """
    print 'Executing load function'


def teardown():
    """
    On uninitialization
    """
    print 'Executing teardown function'

在加载功能中引入对 Maya 环境的修改,并在拆卸功能中卸载插件时进行必要的房屋清理。

我检测插件从哪里加载并从那里设置 Maya 环境……

让我们深入了解加载功能。


加载函数

插件将自动加载,因此每次启动 Maya 时都会添加或创建以下内容。


  • 插入 Maya 环境的路径

  • sys.path 的 Python 根路径

  • 用户本地首选项中的 .mod 文件

  • 场景保存回调之前 - 非常适合源代码控制

  • 打开场景回调后——管理场景配置

  • 新场景回调——管理场景配置

  • 覆盖增量保存场景(由于 AD 错误)


下面是我们框架的基本结构。如果您对如何构建框架并在自己的框架中动态解析某些 Python 模块有兴趣,请查看这篇文章。

让我们单步执行加载函数。阅读每个部分的评论,了解正在发生的事情。我在上面的框架结构的概述图像上标记了我们在下面的代码中定义的变量。

提示

由于下面的一些代码行相当长,请使用最右侧的“在新窗口中打开代码”按钮最大化,同时仍然可以看到框架结构。

Load Function

import maya.api.OpenMaya as OpenMaya
import traceback
import os
import sys
import inspect
import pymel.core.language
import pymel.util

pythonRoot = None
saveSceneJobId = -1
openSceneJobId = -1
newSceneJobId = -1

def load():
    """
    On initialization, this function gets called to:
    - Add plug in paths to the Maya environment
    - Add Python root path to sys.path
    - Create a mod file in user local that adds plug in path to this plug in to retain auto load
    - Create Maya before scene save callback
    - Create Maya after open scene callback
    - Create Maya new scene callback
    - Override incrementalSaveScene (due to AD bug)
    """
    
    # Declare globals so that we can modify the variables
    global pythonRoot
    global saveSceneJobId
    global openSceneJobId
    global newSceneJobId
    
    # This is where the plug in is currently being loaded from
    plugPath = inspect.getfile(inspect.currentframe())
    plugInRootFolderPath = os.path.abspath(os.path.join(plugPath, os.pardir)).replace('\\', '/')
    
    # Our framework's internal folder structure dictates that the python root folder path 
    # is 4 levels up from .../lcg/maya/plugin/LCG.py which is the "plugPath" variable
    pythonRoot = '/'.join(plugPath.split('/')[0:-4])

    # Check if the modules directory exists in the user preference directory (if it doesn't, create it)
    mayaModuleDirectoryPath = '%s/modules' % pymel.util.getEnv('MAYA_APP_DIR')
    if not os.path.exists(mayaModuleDirectoryPath):
        os.makedirs(mayaModuleDirectoryPath)
    
    # Define the module file path
    mayaModuleFile = '%s/LCG.mod' % mayaModuleDirectoryPath
    
    # Given where the plug in is loaded from this the root dir of our framework (LCG)
    toolRoot = '/'.join(plugPath.split('/')[0:-5])
    
    # Write our module file
    with open(mayaModuleFile, 'w') as moduleFile:
        # String for adding the Maya site
        output = '+ LCG 1.0 %s/maya' % toolRoot
        # Add string for Python paths
        output += '\r\nPYTHONPATH += %s' % pythonRoot
        output += '\r\nPYTHONPATH += %s/lcg/lib' % pythonRoot
        
        # Dynamically add all subdirectories containing py files of the root maya plugin Python folder
        for directory, subDirectories, filenames in os.walk(plugInRootFolderPath):
            if len([filename for filename in filenames if not '__init__' in filename and filename.lower().endswith('.py')]) == 0:
                continue
            plugInFolderPath = directory.replace('\\', '/')
            output += '\r\nMAYA_PLUG_IN_PATH += %s' % plugInFolderPath
            
            # First time that the module is written, the path will not yet be initialized - do it here
            if not plugInFolderPath in pymel.util.getEnv("MAYA_PLUG_IN_PATH"):
                pymel.util.putEnv("MAYA_PLUG_IN_PATH", [pymel.util.getEnv("MAYA_PLUG_IN_PATH"), plugInFolderPath])
        
        # Any shelf file in this directory will be automatically loaded on Maya startup        
        output += '\r\nMAYA_SHELF_PATH += %s/maya/shelves' % toolRoot
        moduleFile.write(output)

    # The very first time the plug in is loaded Python path will not be initialized - do it here
    if not pythonRoot in sys.path:
        sys.path.append(pythonRoot)
        sys.path.append('%s/lcg/lib' % pythonRoot)
    
    # Now our framework is available to Maya. 
    # Import modules to set up pre-save callback and scene configuration
    import lcg.maya.preSaveCallback
    import lcg.maya.sceneConfig
    import lcg.maya.about
 
    # Add pre save scene callback
    saveSceneJobId = OpenMaya.MSceneMessage.addCheckCallback(OpenMaya.MSceneMessage.kBeforeSaveCheck, lcg.maya.preSaveCallback.run, None)
    # Add after open scene callback (for scene configuration)
    openSceneJobId = OpenMaya.MSceneMessage.addCallback(OpenMaya.MSceneMessage.kAfterOpen, lcg.maya.sceneConfig.run, None)
    # Add after new scene callback (for scene configuration)
    newSceneJobId = OpenMaya.MSceneMessage.addCallback(OpenMaya.MSceneMessage.kAfterNew, lcg.maya.sceneConfig.run, None)
 
    # For incremental save we have to overload the global proc incrementalSaveScene because 
    # Autodesk/Maya removes the file from file system when saving an iteration and then puts it back
    # We need to trigger our pre-save callback before that happens. The Overload script sits in the module folder's scripts directory (LCG/maya/scripts)
    # I got this behavior logged as a bug in the middle of 2017
    try:
        # First make sure script path is added (first time plug in is loaded it will not be)
        scriptRootPath = '%s/maya/scripts' % toolRoot
        if not scriptRootPath in pymel.util.getEnv("MAYA_SCRIPT_PATH"):
            pymel.util.putEnv("MAYA_SCRIPT_PATH", [pymel.util.getEnv("MAYA_SCRIPT_PATH"), scriptRootPath])
        pymel.core.language.Mel.source('Overload_%s' % lcg.maya.about.version, language='mel')        pymel.core.language.Mel.source('Overload_%s' % lcg.maya.about.version, language='mel')
    except:
        pass

下面是您在 lcg.maya.preSaveCallback、lcg.maya.sceneConfig 和 lcg.maya.about 中放入的低级内容

具体来说,您在 preSave 和 sceneConfig 函数中放入的内容取决于每个项目。

预保存,我通常只实现 Perforce 签出,如果尚未签入,则将 Maya 文件添加到 Perforce。

每当打开文件并创建新文件时,sceneConfig 都会运行,因此您可以在此处实现设置单位和首选项等对整个团队来说应该是全局的东西……

Content of ags.maya.preSaveCallback

import pymel.core.system

def run():
	sceneName = pymel.core.system.sceneName()
	print('Run any code you want to execute before maya saves the scene here (maybe checking out the Maya file from source control if it is read only)')

Content of ags.maya.sceneConfig

import pymel.core.system

def run():
    print('Configuring Maya scene - %s' % pymel.core.system.sceneName())

Content of ags.maya.about

import pymel.core.general

# Creating this to have a hook for when AD messes up to properly update this function in future releases
version = str(pymel.core.general.about(version=True))

拆解功能

与加载函数相比,拆卸函数非常简单。

我们在拆解中删除了以下内容:

  • sys.path 的 Python 根路径

  • 场景保存回调前

  • 打开场景回调后

  • 新场景回调

  • 重新获取原始增量SaveScene

Teradown Function

import maya.api.OpenMaya as OpenMaya
import sys
import pymel.core.language

def teardown():
    """On uninitialization, this function gets called to:
    - Remove Python root path from sys.path
    - Remove Maya before scene callback
    - Remove Maya after open scene callback
    - Remove Maya new scene callback
    - Re-source the original incrementalSaveScene
    """
    
    # Remove our Python root from sys.path
    for sysPath in sys.path:
        if sysPath == pythonRoot:
            sys.path.remove(sysPath)
    
    # Remove Maya before scene callback
    OpenMaya.MSceneMessage.removeCallback(saveSceneJobId)
    # Remove Maya after open scene callback
    OpenMaya.MSceneMessage.removeCallback(openSceneJobId)
    # Remove Maya new scene callback
    OpenMaya.MSceneMessage.removeCallback(newSceneJobId)
 
    # Re-source the original incrementalSaveScene
    pymel.core.language.Mel.source('incrementalSaveScene', language='mel')


评论回复