Quantcast
Channel: mel wiki
Viewing all 610 articles
Browse latest View live

How do 'zombie' intermediate objects get created?

$
0
0
Creating history on a node (like a polygonal object) creates what's called an "Intermediate Object", but I prefer to call it the 'pre-deformed shape', since that's what it really is. For example, applying a deformer to a poly sphere will give this nodal connection:
itermediateObject mesh shape -> deformer node -> mesh shape 
With both the intermediateObject mesh shape and the mesh shape both children of the parental transform. The intermediate object is by default hidden both from camera view, and from the shape-node view in the Outliner. To make it visible, you need to select the transform and access 'Edit Deformers -> Display Intermediate Objects'. You may need to collapse/open any Outliner hierarchies at that point to make them show up.
If you 'delete history' on this mesh, it will bake the deformer into the mesh, and remove the intermediate object.

However, if you were to duplicate this mesh, you'll now have a duplicate of the intermediateObject mesh shape node as well, but no more connections to maintain the history: If you delete history on this dupe, the intermediateObject mesh shape will persist due to the lack of connections. And they start to pile up.

I've logged this as a bug in Maya 2015. To clean them up in the meantime is to find all the intermediate objects in the scene, and delete those that have no connections.

PyMel : Monkey-patch PyNodes

$
0
0
PyNodes are handy wrappers around Maya nodes. Sometimes I want to extend them, to be even more helpful, by adding my own methods per instance. The general term for this is 'monkey-patching'. Here's how to do it:

Say, you want to add your own method to return the worldspace bounding box for some mesh:
import types
import pymel.core as pm

node = pm.PyNode("myAwesomeMesh")

def bboxWorld(self):
    """
    Our method to monkeypatch in.
    """
    return self.getBoundingBox(space='world')

node.bboxWorld= types.MethodType( bboxWorld, node )
Now, we can call to node.bboxWorld() on that specific PyNode instance.
data = node.bboxWorld()
print type(data)
<class 'pymel.core.datatypes.BoundingBox'>

PyMel : Basics

$
0
0

High level notes as I learn PyMel

Main Documentation

Common PyMel objects (Found under the 'Classes' section of the pymel.core.general documentation):

Data Types pymel.core.datatypes (under 'Classes')
  • Includes Matrix, Vector, etc, plus many math related functions.
  • Can also be accessed as pymel.core.dt
Node Types :pymel.core.nodetypes
  • Wrappers around all of Maya's nodes: Joints, Transforms, Mesh, etc
  • Can also be accessed as pymel.core.nt
Component Types :pymel.core.general (under 'Classes')
  • Verts, edges, cv's etc.
When you see stuff like:
# Result: [nt.Transform(u'pPlane1'), nt.PolyPlane(u'polyPlane1')]
These are the 'node/component types'.
UI Types : pymel.core.uitype
  • Can also be accessed as pymel.core.ui

Calling to mel:
pymel.core.language.Mel

Building UI's:
Building User Interfaces
Using 'with'

Cheat Sheets
It looks like PyMel has taken certain commands, like file, and completely split them up into separate functions. This is darn confusing for the Mel vetern \ PyMel noob. As I figure this stuff out, I'll record it here:
CommandPyMel Location
maya.cmds.filepymel.core.system
maya.cmds.optionVarpymel.core.language.OptionVarDict
maya.mel.evalpymel.core.language.Mel.eval
From the PyMel Docs:
Note: All the functions in maya.cmds are in the pymel namespace, except the conflicting ones ( file, filter, eval, help, and quit). The conflicting commands can be found in the pymel.cmds namespace, along with all of the unaltered maya commands.

Importing and namespaces
Pymel has a variety of subpackages under the pymel root package. For example:
  • pymel.core
  • pymel.utils
  • pymel.tools
  • etc...
pymel.core specifically has many sub-packages:
  • pymel.core.animation
  • pymel.core.effects
  • pymel.core.general
  • etc...
And each of those sub-packages have a host of commands\classes to use.

What is interesting is that when doing an import like so:
import pymel.core as pm
It seems to also be doing this (based on my observations), on each of the sub packages:
from pymel.core.animation import *
So that in the pm namespace, you have access to all the commands in the sub-packages, without needing to add in the intermediate sub-package path.
Meaning, for example, you can do this:
import pymel.core as pm
stuff = mc.ls(selection=True)
Rather than having to do this:
import pymel.core as pm
pm.general.ls(selection=True)
That being said, there are certain sub-packages, like pymel.core.runtime, that aren't part of this automatc import: They're left out of it. You'd still need to import that package directly to access its contents.



How can I duplicate a hierarchy and rename them at the same time?

$
0
0
I often end up with long chains of joints, like so:
  • tentacleA1
    • tentacleA2
      • tentacleA3
        • etc...
Which I want to duplicate. Maya has an option to duplicate a node and give all children unique names. But what I want is to duplicate the hierarchy and rename the hierarchy in the process. So I'd get something like this:
  • tentacleB1
    • tentacleB2
      • tentacleB3
        • etc...
And, code:
import pymel.core as pm

def dupeAndRename(replaceThis, withThis):
    oldNames = [node.nodeName() for node in sel]
    new = pm.duplicate(sel)
    for i,n in enumerate(new):
        newName = oldNames[i].replace(replaceThis, withThis)
        n.rename(newName)
        kids = pm.listRelatives(n, type='transform')
        for k in kids:
            newKName = k.nodeName().replace(replaceThis, withThis)
            k.rename(newKName)
    return new
To use it, I'd just select the tentacleA1 root, and run the code like so:
sel = pm.ls(selection=True)
dupeAndRename("A", "B")

How can I get the boolean intersection of two bounding boxes?

$
0
0
PyMel makes acessing the bounding box data pretty easy actually. Otherwise you'd have to call to the API. From there, some simple Python min/max functions do the rest.
import pymel.core as pm

def intersectBBox(bboxA, bboxB):
    """
    Return the bounding box intersection of one bounding box with another.

    Parameters :
    bboxA, bboxB : PyMel BoundingBox.

    Return : None / PyMel BoundingBox : If the bounding boxes don't intersect,
        None is returned.  Otherwise a new BoundingBox instance that is the
        intersection of the two is returned.
    """
    if not bboxA.intersects(bboxB):
        return None

    thisMin = bboxA.min().cartesian()
    thisMax = bboxA.max().cartesian()
    otherMin = bboxB.min().cartesian()
    otherMax = bboxB.max().cartesian()
    bboxMinPt = pm.dt.Point(max([thisMin[0], otherMin[0]]),
                            max([thisMin[1], otherMin[1]]),
                            max([thisMin[2], otherMin[2]]))
    bboxMaxPt = pm.dt.Point(min([thisMax[0], otherMax[0]]),
                            min([thisMax[1], otherMax[1]]),
                            min([thisMax[2], otherMax[2]]))
    return pm.dt.BoundingBox(bboxMinPt, bboxMaxPt)
Example using it with poly cubes, to prove out the values:
import pymel.core as pm

c1 = pm.PyNode("pCube1")
c2 = pm.PyNode("pCube2")
bb1 = c1.getBoundingBox(space='world')
bb2 = c2.getBoundingBox(space='world')

intersect = intersectBBox(bb1, bb2)
print intersect.min().cartesian()
print intersect.max().cartesian()
print intersect.width(), intersect.height(), intersect.depth()
PyMel 2015 Docs for BoundingBox

How to find bounding box

$
0
0
Some good stuff from this post by Peter Shipkov :
http://groups.google.com/group/maya_he3d/browse_thread/thread/5a78a2fce5de57b4#

getattr:
getAttr someNode.boundingBox.boundingBoxMin;
getAttr someNode.boundingBox.boundingBoxMax;

Commands:
exactWorldBoundingBox("someNode");

xform -query -boundingBox someNode;

polyEvaluate -boundingBox; // whole object
polyEvaluate -boundingBox2d; // object uv's
polyEvaluate -boundingBoxComponent; // component level
polyEvaluate --boundingBoxComponent2d; // component level uv's

API:
OpenMaya.MFnDagNode.boundingBox() 
Returns MBoundginbBox

PyMel:
import pymel.core as pm
node = pm.PyNode("someNode")
bb = node.getBoundingBox(space='world')
Returns type BoundingBox

Sending variable data from Python to Mel

$
0
0
With the inclusion of PyMel, this has gotten a lot easier:

You want to create a mel global int named $foo, with the value of 23:
import pymel.core as pm
pm.language.melGlobals.initVar('int', 'foo')
pm.language.melGlobals['foo'] = 23
The type you pass in must be: 'string', 'string[]', 'int', 'int[]', 'float', 'float[]', 'vector', 'vector[]'
MelGlobals Docs.

Also see:


Older info:

I've recently ran across a nasty... bug, in the Maya-Python integration. It appears, that if you have a Python module return a list, Maya will convert it to an array. That's as expected. However, if the Python list is empty, Maya converts it to a string with two brackets inside of it: "[]", rather than converting it to an empty array.
I should note, as an update, that this initial presumption (that this is a 'bug') is wrong: Maya variables are always typed (string $foo = "word";), while Python's aren't, they're just pointers (foo = "word"). Maya has to auto-detect what type of data (string, float, etc) exists in the passed-in Python object and make a determination for what the conversion should be. If it just gets an 'empty list', it really has no idea what type of Maya array (float, string, int, vector) it should be.
Obviously, this can cause some real headaches if you're trying to capture expected data from Python in Maya.

I found a fix, that I'm... not.... very... happy with. Explained below the example.

Example: First, a Python module that will return different types of data, based on the passed in arg:
# Python code
def variableReturn(varType):
	if varType == "str":
		return "string"
	if varType == "listSingle":
		return ["value1"]
	if varType == "listMult":
		return ["value1", "value2"]
	if varType == "listNone":
		return [None]
	if varType == "listEmpty":
		return []
Now, some mel code to capture that data. I've already setup the Maya variable types to be the correct ones based on what the Python module spits out... but you can see that one of them is clearly wrong:
// Mel code:
string $str = python("variableReturn('str')");
string $lstSingle[] = python("variableReturn('listSingle')");
string $listMult[] = python("variableReturn('listMult')");
string $listNone[] = python("variableReturn('listNone')");
string $listEmpty = python("variableReturn('listEmpty')");

print ("Type of 'str' : " + `whatIs "$str"` + "\n");
print ("Type of 'listSingle' : " + `whatIs "$lstSingle"` + "\n");
print ("Type or 'listMult' : " + `whatIs "$listMult"` + "\n");
print ("Type of 'listNone' : " + `whatIs "$listNone"` +"\n");
print ("Type of 'listEmpty' : " + `whatIs "$listEmpty"` + "<--- aaaagh!!!\n");
Prints:
Type of 'str' : string variable
Type of 'listSingle' : string[] variable
Type or 'listMult' : string[] variable
Type of 'listNone' : string[] variable
Type of 'listEmpty' : string variable  <--- aaaagh!!!
As you can see, the workaround is to have your Python code return a list with a 'None' object in it:
# Python
return [None]
Presumably, you'd do some sort of test before hand in your Python code to know to return things this way:
# Python
if len(myVal) == 0:
    myVal = [None]
return myVal
Of course, in Maya you'd then have to detect if the first item of the list was empty, if it's size was 1:
// mel
string $result[] = python("myPyFunc()");
if(size($result) && $result[0] != ""){
    // start doing something...
}

Update:
After reading through the Maya docs on Python integration, this may make a bit of sense:
Python Return ValueMEL Conversion
stringstring
unicodestring
intint
floatfloat
list containing numbers, including at least one floatfloat[]
list containing only integers or longsint[]
list containing a non-numberstring[]
anything elsestring
I'm guessing an 'empty list' falls into the 'anything else' category, and thus converts it to a "string". Even though our above example returns an empty list, and we presume to have string data, it could actually contain float data, int data, etc. But how is Maya to know this if all it gets is an un-typed empty list? It doesn't, so it turns it into a string.

How can I show the Channel Box / Attribute Editor / Tool Settings?

$
0
0
I like to map hotkeys to the display of the Channel Box, Attribute Editor, and Tool Settings to force their visibility on, and docked. This is what I came up with:

Attribute Editor:
ToggleAttributeEditor;
string $component = getUIComponentDockControl("Attribute Editor", false);
dockControl -edit -visible true $component;
Channel Box:
ToggleChannelsLayers;
string $component = getUIComponentDockControl("Channel Box / Layer Editor", false);
dockControl -edit -visible true $component;
Tool Settings:
ToggleToolSettings;
string $component = getUIComponentDockControl("Tool Settings", false);
dockControl -edit -visible true $component;

All the 'Toggle' commands are actually Run Time Commands:
toggleUIComponentVisibility "Attribute Editor"; updateMainWindowComponentState()
if (`isUIComponentVisible("Channel Box / Layer Editor")`) {
	toggleUIComponentVisibility("Channel Box / Layer Editor");
} else {
	setChannelsLayersVisible( true );
}
; updateMainWindowComponentState()
if (`isUIComponentVisible("Tool Settings")`) {
	toggleUIComponentVisibility("Tool Settings");
} else {
	toolPropertyWindow -inMainWindow true;
}
; updateMainWindowComponentState()

How can I make a window dockable?

$
0
0
Starting with the introduction of Qt in Maya 2012, you can make your own custom windows dockable in the Maya UI.
The below examples will take a columnLayout (but any layout type will do) and turn it into a floating dock, that can then be dragged via the mouse and inserted into various parts of the Maya UI.
Docs: dockControl
# Python code
import maya.cmds as mc

mc.setParent('MayaWindow')
myLayout = mc.columnLayout()
# Add everything that should go in the dock here...

# Define the name for the docControl:
dcName = 'myDC'

# Just like you need to delete pre-existing windows before you make new ones,
# you need to delete pre-existing dockControls:
if mc.dockControl(dcName, query=True, exists=True):
    mc.deleteUI(dcName)

# Create the dockControl that will house the window:
mc.dockControl(dcName, allowedArea='right', area='right', floating=False, 
               content=myLayout, label='Awesome Dock', visible=True)

Maya.env save location

$
0
0
As of Maya 2015

Windows:
C:\Documents and Settings\<username>\My Documents\maya\<version>\Maya.env
C:\Documents and Settings\<username>\My Documents\maya\Maya.env
Mac:
/Users/username/Library/Preferences/Autodesk/maya/<version>\Maya.env
/Users/username/Library/Preferences/Autodesk/maya/Maya.env
Linux:
~/maya/<version>/Maya.env
~/maya/Maya.env
Note you could have both, or neither:
The Maya.envnot in a version directory applies to all versions of Maya installed. The one in a version directory only applies to that version.

How can I enforce Maya's frame rate?

$
0
0
Based on a new install of Maya, I believe the default frame rate is set to 'Film (24 fps)'. I work in games, where most of the time the frame rate should be set to 'NTSC (30 fps)'. Based on the tools we use, if an animator loads a scene up, and starts animating at 24 fps, then loads it into our animation sequencer that expects things to be at 30fps,... bad things will happen.

I've isolated two locations that need to be updated to ensure that whenever a scene is created \ opened, it will be a the right frame rate. For this example, we'll be using 'NTSC (30 fps)'.

Location #1: Maya Preferences:

Maya has its default preferences (Window -> Settings / Preferences -> Preferences -> Settings -> Working Units -> Time) that set the overall time for the scene. When you change the value, Maya calls to the currentUnit command to update the system. What's interesting, is that it doesn't seem to modify any optionVars to store these settings.
Look in the global proc prefWndUnitsChanged() in the script:
C:/Program Files/Autodesk/Maya<version>/scripts/startup/createPrefWndUI.mel
...to see what's really going on.

However, this value can be changed by incoming files with different values. So we need to do something else to ensue we have the correct settings when we open any scene...

Location #2: New Scene Options

When you make a new scene (File -> New -> Options), there are new scene options where you can set the framerate. These will override anything set in your Maya preferences (see #1, above). This is a main gotcha I see for most people: They don't realize that these need updated as well.
Changing the 'time' in these options does store the change in an optionVar.
Look in global proc setDefaultUnits() in the script:
C:/Program Files/Autodesk/Maya<version>/scripts/startup/performNewScene.mel}
... for more details.

The Solution:

You need these options to be set every time you access a new scene, or open an existing scene. The best way I've found to do this is add a scriptJob your userSetup.mel that executes on any file access:
// userSetup.mel

global proc setNTSC(){
	// Set our 'Maya preferences':
	currentUnit -t ntsc;
	// Set our 'new scene options':
	optionVar -sv workingUnitTimeDefault ntsc;
}

scriptJob -event SceneOpened setNTSC; 
scriptJob -event NewSceneOpened setNTSC;
Done: Anytime you open access a file, you're sure to know that it's at the correct framerate. With a bit more 'detection code', you could query what the values were before modification, and alert the user if any changes were taking place.

How can I enforce the file type?

$
0
0
string $format = "mayaAscii";
optionVar -sv defaultFileOpenType "mayaAscii";
optionVar -sv "defaultFileSaveType" $format;
file -type $format;
Adding this to your userSetup.mel file will make sure that things are saved in that format by default, and the new scene that Maya opens with is also of that format.
When doing a "Save As", this will set the "File Type" under "General Options" to the defined $format.

Optionally, if you really wanted to make sure your users always saved as a given format no matter what: Presuming you push a studio-wide userSetup.mel, in that file, you can set, via the file command, a -preSaveScript arg. That points to a script that will be ran before save. In that script, you can query for the file type, and if not the format you want, change the format, then save.

How can I have Maya always "Auto Frame" curves in the Graph Editor?

$
0
0
You can turn on "Auto Frame" in the Graph Editor, but when you restart Maya, it's off again. It doesn't seem to ever remember, which is quite bothersome. It even seems to store an optionVar, but upon restart, it seems reset.

Add this to your userSetup.mel file to always force it to be on:
global proc setAutoFrame()
	{
	animCurveEditor -edit -autoFit 1 graphEditor1GraphEd;
	optionVar -intValue graphEditorAutoFit 1;
	print ("userSetup.mel:		** Turned on Graph Editor 'auto fit'\n");
	}
scriptJob -event SceneOpened setAutoFrame;
Whenever a scene is opened, the scriptJob will run the setAutoFrame proc, resetting these values.
Also see:

How can I modify the Channel Box's marking menu?

$
0
0
The script located here:
../MayaX.X/scripts/startup/generateChannelMenu.mel
This script has all the UI code which builds the marking menu, so edit for your needs.
Of course, what would be safer would be to duplicate this script, rename it to something else (like say, myCustomGenerateChannelMenu.mel), and when Maya starts up, add this line to your userSetup.mel script:
source myCustomGenerateChannelMenu;
print "Custom generateChannelMenu.mel script sourced -- userSetup.mel\n";

How can I set Maya's framerate playback options?

$
0
0
In Maya's prefs, under Settings -> Timeline, there are options to set the 'Playback Speed' and 'Max Playback Speed'. My maya had a habit of forgetting this stuff. I wanted it to play every frame of the animation, but lock it to 30fps.

There are two steps to this: Update optionVars so the settings will persist between Maya sessions, and updating the values for the current Maya session. I figured out the optionVars by hunting around my userPrefs.mel file.
I added the below code to my userSetup.mel file:
optionVar -fv "timeSliderPlaySpeed" 0;
optionVar -fv "timeSliderMaxPlaySpeed" 1;
playbackOptions -playbackSpeed 0;
playbackOptions -maxPlaybackSpeed 1;
As you can see, you're not setting the actual framerate values: The values are indexed, so you need to choose the correct index for the option that you're after. The Prefs UI drop-downs show you the options you can choose from.

Querying dockControl states

$
0
0
dockContols are great for making docked windows that can be torn off. But it's not entirely easy to track then state they're in. Here's one solution I've come up with for tracking if they're torn off (floating) or not, and to track when it's visible\hidden.

When making your dockControl, pass in callback arguments to its visibleChangeCommand and floatChangeCommand parameters:
  • visibleChangeCommand : This is executed whenever a dock is made visible \ invisible (different from delete).
  • floatChangeCommand : This is executed whenever a dock is torn off (made to float) or docked.
Note that when you close a dockControl via the little [x] in the corner, you're really just hiding it: You can RMB on the menubar of any other dock to get a popup menu of all available docs.
from functools import partial as callback
from pymel.core import pm

def visibleChangeCallback(*args):
    dockName = args[0]
    if pm.dockControl(dockName, query=True, isObscured=True):
        # Do stuff when dock is hidden
    else:
        # Do stuff when dock is visible

def dockFloatCallback(*args):
    dockName = args[0]
    if pm.dockControl(dockName, query=True, floating=True):
        # Do things when dock floats
    else:
        # Do things when dock is docked

awesomeLayout = # some layout already made, to be put in the dock...

dockName = "awesomeDock"
pm.dockControl(dockName, allowedArea=['left','right'], area='right',
               floating=False, content=awesomeLayout, label="Awesome Dock", visible=True,
               visibleChangeCommand=callback(visibleChangeCallback, dockName),
               floatChangeCommand=callback(dockFloatCallback, dockName))
The key to the visibility check is to query the isObscured flag, rather than the visible flag: Visibility always seems to return True, even if it's hidden.


Also see:

How can I create a joint hierarchy iterator?

$
0
0
Say you have a hierarchy of joints, and you want to walk over each joint in the hierarchy, and do something to that joint, and its children? This simple Python generator has you covered: For each item in the hierarchy it will return the current item, and any children it has. It will keep doing this until the hierarchy fully walked.
Given this example joint hierarchy:
  • grandparent
    • grandchildA
      • greatGrandchildA1
      • greatGrandchildA2
    • grandchildB
      • greatGrandchildB1
      • greatGrandchildB2
Run the below code:
import pymel.core as pm

def jointWalk(root):
    kids = [pm.PyNode(root)]
    for k in kids:
        children = pm.listRelatives(k, type='joint')
        kids.extend(children)
        yield (k, children)
root = 'grandparent'
for parent, children in jointWalk(root):
    print parent, children

nt.Joint(u'grandparent') [nt.Joint(u'grandchildA'), nt.Joint(u'grandchildB')]
nt.Joint(u'grandchildA') [nt.Joint(u'greatGrandchildA1'), nt.Joint(u'greatGrandchildA2')]
nt.Joint(u'grandchildB') [nt.Joint(u'greatGrandchildB1'), nt.Joint(u'greatGrandchildB2')]
nt.Joint(u'greatGrandchildA1') []
nt.Joint(u'greatGrandchildA2') []
nt.Joint(u'greatGrandchildB1') []
nt.Joint(u'greatGrandchildB2') []

PyMel : Converting between numeric data types

$
0
0
This is sort of a PyMel cheat sheet exposing various methods for the various data types, allowing for conversion between them. Also included is the Transform node type, since it ties closely with these data types.

Docs (2015):
Data Types:
Node Types:

Right side are arguments to methods on the left.
Data TypeMethod/AttributeReturnPointVectorQuaternionEulerRotationMatrixTransformationMatrix
Pointinit()PointXXXX
Vectorinit()VectorXXX
rotateBy()XXXXX
rotateTo()QuaternionX
transformAsNormal()VectorX
QuaternionasEulerRotation()EulerRotation
EulerRotationinit()EulerRotationXXXXXX
asVector()Vector
incrementalRotateBy()EulerRotationX
setValue()EulerRotationX
asMatrix()Matrix
decompose()EulerRotationX
Matrixinit()MatrixXXXXXX
TransformationMatrixinit()TransformationMatrixXXXXXX
getRotatePivot()Point
getScalePivot()Point
setRotatePivot()X
setScalePivot()X
addTranslation()X
getRotatePivotTranslation()Vector
getScalePivotTranslation()Vector
getTranslation()Vector
setRotatePivotTranslation()X
setScalePivotTranslation()X
setToRotationAxis()X
setTranslation()X
addRotationQuaternion()X (as list)
getRotationQuaternion()Quaternion (as list)
setRotationQuaternion()X (as list)
rotateQuaternion
rotateTo()XX
eulerRotation()EulerRotation
asMatrixInverse()Matrix
asRotateMatrix()Matrix
asScaleMatrix()Matrix
Node TypeMethod/AttributeReturnPointVectorQuaternionEulerRotationMatrixTransformationMatrix
TransformgetRotatePivotPoint
getScalePivotPoint
setRotatePivotX
setScalePivotX
getRotatePivotTranslationVector
getScalePivotTranslationVector
getTranslationVector
setRotatePivotTranslationX
setScalePivotTranslationX
setTranslationX
translateByX
getRotationQuaternion, EulerRotation
rotateByX
setRotationXX
getMatrixMatrix
setMatrixX
getRestPositionTransformationMatrix
getTransformationTransformationMatrix
setRestPositionX
setTransformationX

API: How can I find the working units?

$
0
0
The below classes compare with the mel currentUnit command.

The below classes have the ability to query both the internal units, and the ui units (except MTime). The internal units, while they can be changed, usually shouldn't be. Defaults for internal units are:
  • Linear : Centimeters
  • Angular : ? Mine comes up as kInvalid, which is odd. I'd expect it to be Radians though.
  • Time : Has no differentiation between internal\ui: There is only ui units. I think when Maya installs this is 24fps (film).
UI units are the ones you can change via Maya's prefs, or via mel.

Linear:

OpenMaya.MDistance
import maya.OpenMaya as om
uiLinearUnit = om.MDistance.uiUnit()
This returns an int value that corresponds to the Unit enum on the MDistance class. Which corresponds to these constants:
1kInches
2kFeet
3kYards
4kMiles
5kMillimeters
6kCentimeters
7kKilometers
8kMeters

Angular:

OpenMaya.MAngle
import maya.OpenMaya as om
uiAngularUnit = om.MAngle.uiUnit()
This returns an int value that corresponds to the Unit enum on the MAngle class. Which corresponds to these constants:
1kInvalid
2kRadians
3kDegrees
4kAngMinutes
5kAngSeconds
6kLast

Time:

OpenMaya.MTime
import maya.OpenMaya as om
uiTimeUnit = om.MTime.uiUnit()
This returns an int value that corresponds to the Unit enum on the MTime class. There are a lot of constant values, check the docs for the specifics.

Also see:

API: How can I convert from linear internal units to ui units?

$
0
0
When dealing with the API, all linear 'internal units' are held in cm (unless overridden, but I've never seen this happen). But if you're passing that data to mel/Python commands, they expect the values to be in whatever 'ui unit' has been set. By default, the ui unit is cm as well, but many studios will change this value to something else. In my case, it's currently inches...
If these two values line up you won't notice any problems in your code. But if you author code that expects the ui units to be cm and they're not, chaos will ensue.

In the below example, we grab the center-point of the bounding-box of a node, then convert that position into the current ui-units:
import maya.OpenMaya as om

node = 'pSphere1'

# Get the MDagPath for a node:
selList = om.MSelectionList() # MSelectionList
selList.add(node)
mDagPath = om.MDagPath() # MDagPath
selList.getDagPath(0, mDagPath)

# Find the centerpoint based on bounding box, this will be in cm:
dagNodeFunc = om.MFnDagNode(mDagPath) # MFnDagNode
boundingBox = dagNodeFunc.boundingBox() # MBoundingBox
centerPoint = boundingBox.center() # MPoint

#-----------------------
# Now that we have some data, convert it from internal to ui units:

# Convert from cm to current units:
center = []
unitType = om.MDistance.uiUnit() # Get the current UI unit:
for i in range(3):
    distance = om.MDistance(centerPoint[i]) # MDistance, as cm
    converted = distance.asUnits(unitType) # double, converted
    center.append(converted)

print center

Also see:
Viewing all 610 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>