02 August, 2010

MSyntax Flag Definition & Invalid Flag Error

I had some frustrating moments in making flags work for a command using MSyntax. A part of it was my ignorance in reading the function detail, but the other one I am not sure is mentioned in docs. Here is what it says in the doc. for MSyntax::addFlag(const char* shortFlag, const char* longFlag, ......)

shortName  the string representing the short (< 4 character) version of the flag
longName   the string representing the int (> 3 character) version of the flag


=> Short flag name should always be < 4 characters.
=> I defined one flag name like this:  syntax.addFlag("-it", "-infType", MSyntax::kDouble);
and used it with my command like this: myCmd -infType 2 Maya gave me an error, "invalid flag -infType"!

Then I changed the flag long name from -infType to -influenceType it all worked fine. So I wonder there is a problem with defining a long flag name beginning with three lower case characters, followed by an upper case character.

20 July, 2010

Rigenerator: Update 1

The major change in the new version is the rule based DG traversal. Earlier DG traversal was unrestricted and it will fetch all the nodes involved in the scene. In the new version I have added rules that are stored in a simple dictionary structure which can be updated dynamically. This allows me to select a DAG structure and Rigenerator will only get the rig involved in that selected DAG tree.

Rules are used by algorithm to decide if it needs to ignore a node, or ignore a branch or end at a particular node in DG. These conditions are triggered when we encounter specified node type or name. I will add regular expression to both, node name and type, so that we can target more generalized types of nodes. Later on, my plan is to be able to specify node patterns and levels in the rules. This rule based DG traversal is very powerful as I can fine tune the algorithm dynamically to extract any kind of rig.

I have also fixed problems with DG connection sequence in the code. So now there will be no connection dependency problems like this:
connectAttr nurbsCurveShape.worldSpace[0] curveInfo1.inputCurve;
connectAttr makeNurbsCircle1.outputCurve nurbsCurveShape.create;
// This will be an error since nurbsCurveShape is not created 
//  when doing first connection
// Shape is created when we make the second connection

Next addition is the option to include top hierarchy nodes of the selected rig. There are three options for this:
1. Recreate the top hierarchy
2. Use existing top hierarchy
3. Just group the rig under a separate node

For example, you Rignerate the Arm setup and also want all the top hierarchy nodes, e.g. shoulder, upper_body, root etc, then you can include those transform nodes, so it maintains the hierarchy. Using existing hierarchy helps, if you are mirroring the rig. And you can group the rig under a single node, in case you want to move it somewhere else.

Right now the code generation is still in .MA format and even if its simplicity is very useful it adds limitations because certain type of code cannot be executed with MEL! I can understand the reasons behind it, but it has added complexity to the code generation.
For example,
createNode animCurveUA -n L_Index2_rotateZ;
setAttr "L_Index2_rotateZ.wgt" no;
setAttr "L_Index2_rotateZ.tan" 10;
setAttr -s 3 "L_Index2_rotateZ.ktv[0:2]"  -10 26.247651337441738 0 0 10 -77.759996825174923;
Last line will silently fail and also the code following that line. This thread explains this: LINK

Hence the next step is to add special processing for special nodes, in this case animCurveUA. So instead of using MPlug.getSetAttrCmds() I will query attribute using normal mel commands. But, I need to generalize this into a proper system, instead of going and adding so many specific if conditions. This will also allow me to generate special commands for deformers as I have posted in one of my earlier posts. So that's the next goal.

19 July, 2010

FreeMind: Mind mapping tool

It is very important to organize not only your code but the thought process and all the ideas that come up during planning of a project. I started with my rigging framework and then branched to make Rigenerator, which by itself is now a big project. Each feature of Rigenerator requires good amount of planning to consider all possible cases. At the current stage code of Rigenerator is turning very complex for me even with lines and lines of comments. I have started loosing myself in these different possible scenarios and ideas to solve them. The problem is that going back to a particular problem after few days is not easy.

So I have decided to document as much thought process as I can so that resuming to the last problem is easier and I can trace old problems and their solutions. I found that I can do this by a technique called mind mapping. Basically you write everything in nodes which are interconnected and organized as a tree. I am using FreeMind (open source) to document my ideas and its helping me so much. And I always feel good when I am organized :)

You may find this tool very helpful. Check out the video demo at the bottom of the screenshot page.
FreeMind

14 July, 2010

Rigenerator : Converting deformer network into its command

While developing my Rigenerator tool, I am studying more and more about Maya DG graph and how to reproduce it. Right now I can create all the nodes individually and put them together to recreate the rig. However, I want to be able to replace those individual commands with a specific command e.g. use cluster instead of individual create, addAttr, setAttr, connectAttr commands for each node involved in that cluster deformation.

I believe this feature is going to be very helpful since, it can literally give you a major part of code for your rig already laid out. So you don't have to go back in Maya to check all connections and then come back to editor to code. But, implementing this feature is very tricky so I am studying how deformer network is created, how a deformer gets all the inputs and what each node in that network is used for. I have taken some notes from this study and I want to post them once I have some example images ready.

13 July, 2010

Cluster Deformer Weights

Each component affected by a cluster has its own weight stored in the deformer attribtue .wl (weightList). This attribute is inherited from weightGeometryFilter node. ".wl" is a two dimensional array with first dimension specifying an array related to each geometry object a cluster affects.e.g.
cluster1.wl[0] => array of weight values for nurbsCurve1
cluster1.wl[1] => array of weight values for nurbsCurve2
cluster1.wl[0][3] =>  cluster weight of nurbsCurve1.cv[3]

Now, let's say nurbsCurve1 has 8 CVs, out of which first four (0:3)  are affected by cluster1. Hence the cluster1.wl[0] will have 8 actual weight values (keep in mind that you can do "getAttr cluster1.wl[0][1000]" even if 1000th CV does not exist, more on that later). Now,we change all weights to 0.5 using cmds.percent("cluster1", "nurbsCircle1", v=0.5). That will also change weights of CVs which are not affected by cluster1. Now if I go to "Edit Membership Tool" and add a CV to cluster1, by default its weight would be 0.5 instead of 1.

This may not be very useful, but this is one of those good to know aspects of Maya.

22 June, 2010

maulik13: New Design coming soon...

I am going a bit slow on my rigging framework since I decided to redesign my website (maulik13). I needed more space to put some content and share some of my scripts. And I wanted to add a simple database so that I can update downloads and portfolio easily. It's taking some time, but I have finished the new logo, layout, colors, login and database connectivity. So hopefully I will have my downloads section up and running live soon.
edit:
I just forgot to mention that I am using codeigniter php framework for my website and I am loving it. This is one of those few frameworks that I absolutely appreciate because of its ease and smooth learning curve. Hopefully I can get those characteristics in my rigging framework as well.

21 June, 2010

Rigenerator: Rules Based Graph Traversal

It is not very difficult to traverse the whole graph in Maya and make a data structure to be able to analyze it. In "Rigenerator" I can select one node in the outliner and I am able to get all the nodes related to it in anyway possible. The next step I want to add is to be able to specify rules in the graph traversal algorithm so that I can restrict the network within the specified hierarchy. This is so that my "Rigenerator" tool can extract only a part of the rig.

My goal is to come up with some kind of short hand notation for specifying these rules. I am trying to come up with different possible scenarios to be able to generalize these rules. So for example, I can specify levels and combine it with a node type(s) and their relationships with the source node. This is just theory and hopefully I can find some way to implement this idea.

06 June, 2010

Rigenerator: Rig Code Generator, Milestone 1

Rigenerator is one of the toolsets from my modular rigging framework (which is still in very very early stage). My goal with this tool is to help riggers speed up the coding and modularizing of the rig components. This can also be used for copying, mirroring rigs of any kind. The first step for this tool is to generate the raw code which is the same as .MA file commands. There are some very useful functions in API to generate a lot of code directly (e.g. setAttr commands for given plug, check MPlug class). You can check an example plugin called MA exporter in devkit. Later based on this raw code this tool allows to organize it and add modularity.

In my previous planning, I wanted to work on this tool at a later stage. But, since I have to create many rig parts for the framework, I thought I would actually finish Rigenerator first. So I can create rigs in different ways and whichever I find the best, I can convert it into code using this tool. This will speed up the coding process for my rigs.

I have reached the first Milestone for this module. I am able to generate the raw commands to regenerate the rigs. I have planned many features for this tool which includes, mirroring, code analysis/modification, dg graph inclusion/exclusion options, mel or python options,  etc. I will post more on this as I make some progress.

02 June, 2010

Parenting a Shape the Right Way

I had written a script some time ago to parent a shape to a given transform. I took into account frozen transformation on both source and target. However, script would not place the shape on to pivot point of the target in some cases. Recently I fixed this script as I need to use it quite often. I had to change the logic compeltely to parent a shape to account for different orientations on the source(s) and the target. The main idea is to match the pivots of both the objects before parenting. I will make this script avaiable for download once I create a download section on my website (hopefully very soon). Meanwhile if you need to try this script let me know and I will send it to you.

06 May, 2010

Conditions: No Need for Extra "else"

Sometimes when I am writing code quickly and jumping between functions, I miss a bit obvious logic and end up having multiple "else" statements. Below are two examples doing the same thing. Here the logic itself is not important, this is just to make a note.
...
   if trueKey != "":
       modObj = pkgDict[trueKey]
       if modObj.__dict__.haskey(className):
           return modObj.__dict__[className]
       else:
           return None
   else:
       return None 

In above example, two else conditions are unnecessary and messy. Concise version,
...
    if trueKey != "":
        modObj = pkgDict[trueKey]
        if modObj.__dict__.haskey(className):
            return modObj.__dict__[className]

    return None 

30 April, 2010

Python: Import a Module and Get a given Class

Python offers different ways to import a module. The most flexible one is to use __import__ function. This allows script to load a module dynamically. So we can load a particular module based on user input.

Since everything in python is an object even the python module has some useful properties. One of them is __dict__ property. This is how you can use it.
className = 'MyClass'
modObj = __import__('moduleName')
# This gives us the class reference
MyClass = modObj.__dict__[className]
# So we can create class instance like this
inst = MyClass(params)

Edit:
This logic works if you are using just modules. For packaging this may need to be modified.

Modular Rigging System : Goals

As I have started writing my modular rigging system, I am constantly thinking about the main goals. I feel that at each stage as I put a concept on paper or write code that should satisfy the main goals. After a lot of thinking I have decided on the following goals,
  1. One should be to be able to change/extend pretty much everything in the system
  2. All the parts of the system should be as much independent as possible
  3. Whenever possible not be specific to Maya 
  4. Editable template/skeleton/rig at any stage

So any module that I create can be changed or replaced. This will create very minimal core modules. We are not really defining the logic of rigging in the core modules. I think the core modules define the concept of modular rigging. And the modules that has some common logic for rigging are defined in core utility modules. Hence the core system modules represent only the idea of the modular rigging system.

Rigging modules should not be constrained to a personal workflow.
Example:
In general, there is a template system that lets us define blueprint of the rig and then we create joints and rig on top of it. So roughly there are three parts to this system, locators(guides), joints(skeleton), rig(controls). But,
what if I already have my joint chains created manually? What if I have my own guide system? or I want to change the way animation controls are aligned or created? etc...

There is really no right or wrong way to do things here. But I am doing my planning based on what I think a modular system should be.

29 April, 2010

Being Pythonic

I mentioned in my earlier post that we can assign a tuple/list to multiple variables. However both number of elements should match on both sides. One of the options is to assign only fixed number of elements to variables like this
mylist = [1,2,3,4,5]
x,y,z = mylist[:3]
mylist[:3] will return 3 elements from the beginning.

28 April, 2010

Being Pythonic

Tuple is a very important data structure in python. You can directly assign tuple elements to variables like this.

Tuple assignment
tup = (3,'xyz',[1,2,3])
ind, myKey, myList = tup 
This is called unpacking. It is wise to do this only on tuples since they are immutable and it is sure that the number of elements being unpacked is same as the number of variables on the left side. This can be very useful when using *args and **kwargs. More on that later.

Use negative index
myList[-1] returns the last element

And Now I am a Python Fan

I was a little hesitant towards learning one more language since I get mixed up easily after working with many different languages: c, c++, ActionScript, mel, php, .Net, qlikview and now python (no boasting but I get surprised myself thinking what human brain can do). For example,

String concatenation,

php : $str1.$str2
c++: str1 + str2
vb: str1 & str2

Defining functions,
mel : global int[] proc getMyAnts(string $place)
c++: int* MyClass::getMyAnts(MString place)
python: def getMyAnts(self, place)

However, after learning python I am thinking about making it my main focus amongst all programming languages. The ease of use, elegant syntax and flexibility to create magic is what sets this language apart from other languages. However, I don't claim it's the best language out there, but it surely is powerful and different in a way that I have started loving it.

Since I am still young in using python I thought it would be a good idea to post tricks and helpful language features that I learn on my own or from others.

24 April, 2010

Coding Tricks Using Arithmetic

When I was in 4th semester (if I remember correctly) of computer engineering studies I had a light bulb moment about using simple arithmetic in programming to avoid many if conditions. This was when I looked at my classmate's (who is now my life partner :) ) C program and found out how modulas operator can save me if conditions while implementing a program to convert decimal to binary.

From that day I always try to use arithmetic to avoid conditions. However, many times I forget about this technique and I keep rediscovering this method. So this time I am writing it down here hoping that I remember it and hoping that someone may find it useful.

Problem: I want to pass one axis in a single 1D parameter and later for calculation I need corresponding vector in an array

Solution 1:
One option is to pass "x", "y" or "z" and then use if conditions to find an array form of vector. I have to consider negative axis too so to minimize number of if conditions, I can pass another variable as a multiplier.
proc int[] getVectorOfAxis(string $axis, int $dir)
{
    int $axisVec[3]={0,0,0};
   $axis = tolower($axis);
    if($axis=="x")
        $axisVec = {$dir, 0, 0};
    else if($axis =="y")
        $axisVec = {0, $dir, 0};
    else if($axis == "z")
        $axisVec = {0, 0, $dir};
    return $axisVec;
}

getVectorOfAxis("y", -1);
Solution 2:
However, if we pass integer for axis then we can use arithmetic to find the axis vector without a single if condition.
proc int[] getVectorOfAxis(int $axis)
{
    int $axisVec[3]={0,0,0};
    int $tempInd;
    $tempInd = abs($axis);
    $axisVec[$tempInd-1] =$tempInd/$axis;
    return $axisVec;
}

getVectorOfAxis(-2);
You can argue that the first is easy to understand and it's really not a big deal considering speed is not an issue for us here. However, training mind to use arithmetic can give more flexible options and you can avoid "if" conditions in many cases. I use this trick a lot in making node networks in maya by using multiplyDivide nodes to replace condition nodes.

Solution 3:
If you find passing integers 1,2,3 for axis unintuitive you can still use "x", "y", "z" without conditions. Here is one more solution :)
def getVectorOfAxis(axis, dir):
    axisVec=[0,0,0]
    axisNum = ord(axis.lower())- ord("x")
    axisVec[axisNum] =dir
    return axisVec

getVectorOfAxis("X", -1)

23 April, 2010

Procedural Rigging System vs Auto Rigger

This is somewhat subjective, but I have started believing that there is a fundamental difference between procedural rigging system and auto rigging scripts.

Auto rigging scripts basically automates tasks for you and generates rig or rigging parts. Many times they are limited by many assumptions or fixed concepts. On the other end procedural rigging system defines how the rig will be generated automatically. It defines a system which helps rigging scripts do their jobs.

For example, in one system all nodes contain basic metadata information like, object type(animControl, bindJoint), dir(R,L,C) etc. These nodes are connected to some other metadata nodes that defines a set of objects, e.g. a metanode that lists all the animation controls for a given rig part. Extending this idea will give a system that allows flexibility to give different inputs and create many possible outputs.

Auto Rigger will use this information from meta system to build the rig or rig parts. So for me Auto Rigging Tool is divided into two main components:
  1. A system that defines how meta information is created and stored
  2. Auto rigging scripts that create rig based on meta information

21 April, 2010

Reason for Cycle Errors While Constraining

When creating a constraint system many times we encounter cycle errors even if we know that some transfroms should not change. For example if a joint is affecting translation of a locator in some way and locator calculates the rotation of this joint, they should be unaffected by each other because they change different parts of the transformation. However, we end up having cycle because we are using world space transformation values.

When we are using worldspace transformation values, we use worldMatrix of the transform node. If the object is moved by any mean worldMatrix needs to be calculated causing it to be marked dirty. So even if we change only rotation the whole matrix attribute is marked dirty. Hence we will see a cycle error in the above example. 

One way to avoid cycle errors is to use the parent of the object that you want to constrain to. Or you can use local space transformation if it works as part of the whole setup.

Using curve 'normal' from PointOnCurveInfo

We can use PointOnCurveInfo node to get information about any point along the given curve. I have been using it for constraining a locator on a curve. It is the same logic used in constraining a transform on a NURBS surface. In this method normal and tangent vectors are used to aim constraint a locator to get the right orientation on the geometry. However, since curve is one dimensional in size, i.e. it has only length and no width, the curve normal tends to swing around when we go pass certain angle. This makes it difficult to use 'normal' vector for aim constraint. We can skip connecting normal vector with up object and just use floating transform or use object rotation axis based on requirement.

17 March, 2010

ScriptNode and Sequence of Loading

This is from Maya docs,
"If a scene contains several script nodes, there is no guaranteed order of execution and the scripts should not depend on a specific execution sequence."

So this means that there is no way to make sure that a particular script node gets executed first. This creates a problem because If I want to modularize my script nodes and have all the procedures in one scriptNode (so that I can call it from multiple other scriptNodes and I can easily update them) I cannot make sure those procedures will be available before I use them.

One of the workarounds is not to run all the scriptNodes on open/close event. Instead you can only have one scriptNode that is loaded on file open. This scriptNode has all the needed procedures defined at the beginning. At the end we execute another scriptNode that in turn calls all other user defined scriptNodes. Here is a pseudo MEL code explaining the concept. This can be handled with referencing (more coming on that later). However, some care should be taken because a node can be executed more than once in reference mode, but I think that problem is easily solvable.

//ProcNode
proc int doThis(){
    // code to snap something
}
proc string doThat(){
    // code to unsnap something 
}
proc callScriptNodes(string $p_name) 
{
    // In case file is referenced
    string $sNodes[] = `ls  ("*"+$p_name)`;

    for ($i=0; $i < size($sNodes); $i++)
    {
       scriptNode  -eb $sNodes[$i]; 
    }
}
// Call the main scriptNode(s) to load other nodes
callScriptNodes("LoadScriptNodes");
//LoadScriptNodes
// Load other script nodes
callScriptNodes("makeUI");
callScriptNodes("makeThingsHappen");
//makeUI
...
$win = `window ...`;
columnLayout;
$sliderL = `attrFieldSliderGrp -min -0 -max 10.0 -at ($leftFeet+".tz") -l "Left Feet"`;
...
showWindow $win;

MEL: Cleaning Memory

One of the problems with MEL is that we don't have much control over deleting variables from memory. The only command available for this purpose is clear and it only works with array. But, we can use this function to clear up very long string data.

We can store string variables in an array for cases where string holds a large amount of data (string holding a script). In this case we can clear the array and it will also free the memory occupied by the string data. But we should only use this wisely as it can lead to the code that is difficult to read. So here is an example.
string $data[5];
$data[1] = "scriptNodeName";
$data[0] = `scriptNode -q -bs $data[1]`;
// Do processing here
clear $data;

08 March, 2010

Dirty Bit Propagation

I have been looking for more information on how and when Maya marks nodes dirty. First thing I have come to understand is that for an attribute two dirty bits can be set, connection and datablock. See isDirty command for these two options. I am still not clear how using these two flags Maya decides to call compute method. And what has confused me more is that if I don't mark a datablock clean then the compute method does not get called after first compute call.
Here is an interesting discussion about dirty propagation,
http://forums.cgsociety.org/showthread.php?t=119449

07 March, 2010

compute Method and Dirty Bit

Working at the lower level functioning of Maya, is a very different experience as there are lots of obvious elements to consider. Basic rule of Maya is that a node is computed only if there is a need of new output from that node. So if there is no output attribute or if it's not connected  then the node is not computed. There has to be another node dependent on output of our node for Maya to call compute method of our node. And we have to explicitly mark a plug clean to tell maya that the output is latest.

edit:
Technically not entirely correct post. I will be posting a new elaborated post explaining this matter.

23 February, 2010

Animatable Pivot System


This is a working demo of my solution for animatable pivot in Maya. I believe it is quite flexible and fast. It allows animators to shift pivots very easily and only one object needs to be keyed. No adjacent two key mechanism necessary, but you can do it if you want.

Most of the logic is implemented using Node network. There is one sctiptJob for each pivot which calls a function. This function is very small and common for all pivots hence no multiple expressions. And the only thing this mel function does is to add the value to the offset to create a new offset which then can be keyed. So there is very minimal mel footprint. All the calculations including matrix multiplication, finding how much object will move, calculate temporary offset etc are done via nodes. I should probably make a single node out of this later.

I will post a video showing the usage of this method.

22 February, 2010

Attribute Change Event

If you have a boolean attribute set to 0. And you set the value again to 0 then the attribute change event is still fired even if it is the same value as before. That means the network will be evaluated again even if the actual value does not change. Hence a dirty bit is set whenever you set a value, it does not matter if it's the same as before. I concluded this from testing with expression node since it works based on input output concept.

edit:(23-2-10)
This feature is actually nice if you want to use an attribute as a button. i.e. you can use only one enum value and user can just click the same value to execute some kind of script or node network.

19 February, 2010

Interesting Notes @ Expression Node

Expression in maya is basically a node that takes inputs and processes them with MEL and gives output.

If you want to create an Expression node without connection to the actual objects, you can do the following.
.O[0] = .I[0];
.O[1] = .I[1];
.O[2] = .I[2];
After this you can connect the inputs and outputs to this Expression node manually through connection editor. If you want your expression to be evaluated only based on value change of inputs then you can change expression to be evaluated "On Demand" instead of always. With always "time" node will be connected to expression resulting in evaluation when time changes (i.e. also while animation is playing).

Also, you should note that if you rename the nodes, expression automatically updates the object names in the expression (if you have used direct value assignments e.g. obj1.tx = obj2.tx).

It is also possible to have input connections to expression node from other nodes even if we are not using them later in expression. To do this you can just assign the attribute value to a variable instead of another attribute. So you can create an expression just with $myVar = $myCtrl.attr. We can use this technique to evaluate expression when an attribute changes (with evaluate = on demand). This is a good alternative to a scriptJob for attribute change!! I discovered this technique while trying to solve bidirectional dependency using nodes in my independent study in spring'09. I could not manage to make bidirectional stuff in a perfectly working solution using just maya nodes. But, I have not given up.

edit:
Few Notes:
  • Only those nodes will be renamed in expression that has direct assignment values.
  • It is required to compute some kind of output to be able to call expression, without any output connection expression will not be computed.
  • You can use any kind of input and any kind of output connection even if they are not related.

17 February, 2010

Understanding Pivot Point

Generally we rotate an object about the origin. But we can also rotate an object around any arbitrary point in space. This arbitrary point is the pivot which is a reference point for rotating or scaling the object. So how does pivot point affect the object's transformation?

In essence, the pivot point just translates the object. It does not affect the orientation of the object in any way. This means that the object's orientation remains the same weather it is rotated around the origin or any arbitrary point in space.  Following image explains this characteristic. You can see that even if the pivot changes, the object's orientation remains the same in all cases.

12 February, 2010

API: Node start and end functions

Following are what I call startup and ending functions that are called during some kind of (un)initializing process. I have also described at what event they are called in Maya.

-initializePlugin()
-uninitializePlugin()
-Constructor
-Destructor
-MPxNode::initialize()

Sequence in which these functions are called:

MPxNode::initialize()
When a node plugin is loaded this is the first function to be called.

initializePlugin()
After above function is called during loading of the plugin.

Constructor
When a node is created, e.g. using createNode.

Destructor
When node is deleted and it is not referenced by undo queue (imp).

uninitializePlugin()
When a node plugin is unloaded from Maya.

animCurveTA update after set key

While looking into how set keyframe event works, I found that animCurveTA nodes (or any other animation curve nodes) don't get recalculated until time changes. And this makes sense considering input for animCurveTA node is time1.outTime. So here is what happens,
- You set a key on the object.
- animCurveTA node creates a key-value pair, but it does not recalculate its "output" attribute. (I think some DG node adds value to animCurveTA)
- If you change time then output will get recomputed as "input" (time value) changes and plug is marked dirty.

So animCurveTA.output is updated only when time changes, not when a key is set.

11 February, 2010

ScriptJob for Keyable Attribute Change Event

Reading from forums it seems that there is a resistance in using ScriptJobs amongst character TDs. And there are reasons for that. But, I think there are some good uses of scriptJob without hitting the performance.

I was trying to make an attribute that is boolean and it jumps from one value to the other like a step key. And I wanted to detect change in this attribute. So I used scriptJob to do the same. However, as soon as I make this attribute keyable, scriptJob does not work as intended. And the reason is that if you are scrubbing the timeline and jump from one key to the other and then release the mouse, it fires up change attribute event even if you are not on the keyframe! And there isn't any elegant way to solve this in MEL. So scriptJob even if powerful is limited when animation comes in the picture.

edit:
There is a work around logic to ensure within the scriptjob call that the attribute change was caused only by a manual change in channel box.

edit:
There is a similar problem with non-keyable attribute and scriotJob. However, that happens only if you are continuously playing animation in viewport and then stop the animation, not while scrubbing timeline.

08 February, 2010

Using Constraints in Scripts

Sometime we require to align one object to another object or do some other kind of matching in our scripts. We can use dynamically created constraints in a script for such purpose and delete the constraint once we get the data. However, while using constraints in this way one should make sure that the object on which constraint is applied does not contain any animation curve. Because if animation curve already exists and constraint is applied then a pairBlend node is created which by default is turned off for the constraint.