No matter how the title sounds, this article is about 3D CGI and 3ds Max primarily.
From the TD (Technical Direction) point of view, the parent world is what you’ll be dealing with most of the times when it comes to objects’ (nodes, entities, fragments tec…) manipulation and mainly exchange between different software packages. Why? Let me explain. When you draw an object into your scene, let’s say a single box called “MyBox”, the object automatically becomes a part of a hierarchy. What is hierarchy you ask? Think of it as a relationship between objects (or whatever for that matter). There are strict rules that apply every time all the times for all objects. So, the box, MyBox, becomes automatically a child of the “World” – an invisible MAXObject.
Why the hell is it so important that I write about it? Well, for a modeler or a “regular” user, this is something you’re never bothered with. But, for a TD, this is a mandatory knowledge! Why is that? Because Max behaves a bit differently than Maya or XSI, which is the reason you have to take certain extra measures in order to extract transformation data from your objects. You see, XSI, for example, automatically creates a certain, visible, accessible and usable hierarchy for you. As soon as you “get” an object within XSI, it gets assigned to a certain “group” (please, do not mistake for Max’s groups, which are a very special kind of a hierarchy, about which I might write later) that carries the objects with it. You can change objects’ parents of course, but this little difference in default behavior is actually a pretty big thing when it comes to animation data exchange. While in Max, as stated above, the default parent for all objects is the rootNode, but, this is different to XSI’s hierarchy design. The rootNode isn’t accessible, it isn’t visible and any regular user without an extensive knowledge of 3ds Max or MAXScript doesn’t even know what it is and that such a thing exists. All the nodes’ transformation gets calculated from the rootNode’s origin. The rootNode’s origin, on the other hand, is very well know to any user at any skill level, it is the very center of the grid and you can access the values in the Move, Rotate or Scale dialogs. So, why would you need to consider any other situation? Well, if you’re a rigging TD, general TD or anyone else in charge of data transfer/exchange between apps. you have to deal with different parent < - > child hierarchies and you have to know HOW to deal with them.
This article is not a tutorial, it’s more of a note from someone who’ve such experience and wants to share it, so to get to the root of the problem here, try this: animate the MyBox any way you like, preferably with some rotation involved. Now, create a null (point helper), offset it from the [0,0,0] coordinates and rotate it however you like, or run this line in the listener:
pt = point transform:(matrix3 [0.474869,0.877075,0.0723854] [-0.669229,0.306471,0.676911] [0.571517,-0.369886,0.732497] [125,0,0]) wireColor:green
it’ll create such a point for you. It is best if you display the MyBox’s trajectory
$MyBox.showTrajectory = true to see the result of the test more clearly. Now you have a box that is animated in the rootNode’s space (MyBox’s parent space) that moves as you expect and as you want, but notice what happens if you link the box to the point helper
$MyBox.parent = pt; redrawViews() the trajectory changes to reflect its new parent space, which is now the pt’s transformation matrix.
Same rule applies to everything in the 3d space, including the subObjects. This is especially important for plotting vertex transformation data in the, so called, point caches. Another example of such behavior can be explained on the same box, MyBox, but in a different context. This time, unlink the box from the point
$MyBox.parent = none (note two things, the new parent assigned is “none” and that the trajectory snaps back to where it was) and apply a point cache modifier to save the vertex animation data to the disk
addModifier $MyBox (pointCache()) and plot the animation. After you’ve done that, you can now delete the animation keys from the timeline
deleteKeys $MyBox.controller #allKeys and you “should” read the cached animation data from the disk. Well, you’ll find out that this isn’t the case, but why? Because the vertices move in their respective parent space, which is the object itself, so, if you animate the object itself, the vertices actually don’t move at all. However, if you animate the point helper the same way the box was animated and skin the verts onto this point you effectively “link” the vertices to the point and therefore make the point helper their new parent. This code will save you the manual work if you’re short on time:
deleteModifier $MyBox 1; pt.transform = $MyBox.transform; pt.controller = copy $MyBox.controller; deleteKeys $MyBox.controller #allKeys;
addModifier $MyBox (skin()); skinOps.addbone $MyBox.skin pt 1; addModifier $MyBox (pointCache())
Now plot the cache to the disk and disable the skin modifier in the stack. You’ll see that the box “moves” along with the point. In fact, the box object itself doesn’t move at all (check the MyBox’s transformation data) only the vertices now, thanks to the skin modifier, have a different parent than the MyBox object and therefore inherit the transformation change.
This is all nice, but still, why should Max TDs be bothered by Max’s default hierarchical behavior? Simply because the rootNode (the “World”) isn’t actually considered as a legitimate parent! Again, exemplify for yourselves: copy the MyBox and link it (change its parent) to the point helper while keeping the original MyBox “parent free”. Now run this code in the listener:
messageBox ("The parent of" + ($MyBox.name as string) + " is: " + ($MyBox.parent as string) + "\nThe parent of" + ($MyBox01.name as string) + " is: " + ($MyBox01.parent.name as string)) caption:"Results:" beep:false a message box appears with the results saying that the MyBox’s parent is “undefined” while the MyBox01′s parent is “Point01″. Bear this in mind and, especially when dealing with transformation extractions or offsets, make sure you explicitly specify your objects’ parent space to derive from.
So these are the basics, of course there are much deeper scenarios and applications than the few I’ve covered here, but the main purpose was to make aware anyone aspiring to become a good TD to take notice of such behavior and be able to quickly respond and potentially solve problems with transformation errors or similar problems.