3ds Max 7 exporter and Maya 6 exporter differences

Hello everyone,

I’m am getting quite far with moving my existing art pipeline over to collada. I was working on porting animation and skinning portions of my existing pipeline over to collada when I realized I totally skipped something fundamental in my basic geometry exporter. See the simple visual scene element below:

  <visual_scene id="unnamed_scene" name="unnamed_scene">
     <node id="Box01" name="Box01">
        <matrix>1 0 0 10 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
        <node id="Box01_PIVOT" name="Box01_PIVOT">
           <matrix>1 0 0 -10 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
           <instance_geometry url="#Box01-obj"></instance_geometry>

So, this is a visual scene element for a simple box exported from Max 7. This test case show what happens when you modify the pivot point for an object. It creates a child <node> element under the “Box01” element called “Box01_PIVOT”. Oops. This was the first time I had moved the pivot point on an object, so of course my collada parser wasn’t ready and didn’t know how to handle the new node. Boom…crash.

My question is, ok, I get what the “Box01_PIVOT” node is. I’m assuming that since my engine stores vertices in pivot space, I’ll need to transform each vert of the box’s geometry by the inverse of the pivot point transform. Assuming that’s right then I’m all set correct? Well, maybe not. I fired up Maya 6 and created the same simple test case of a box that had its pivot moved 10 units along the X axis as well. Upon export the visual scene element looked nothing like max’s visual scene element. I didn’t see a “Box01_PIVOT” node. The only way I know to parse the visual scene element above is to recognize that I’ve found a “*_PIVOT” node and take the appropriate action, BUT, if Max and Maya don’t agree in the XML output or naming conventions, it seems I need to make distinctions between max and maya collada DAEs. Right? Or, maybe I don’t fully understand the “Box01_PIVOT” node.

Anyone have any insightful ideas? Maybe I don’t understand something basic here. So far, I haven’t had to distinquish between Max and Maya DAE’s in my collada parser and things are moving along nicely.

Thanks in advance,

Perhaps I’m wrong, but it sounds like you don’t fully understand the transform node hierarchy and how it works. Not only can a node have a child node inside of it, but it can have multiple child nodes, and those nodes can have their own children, etc etc. This tree data structure is known as the transform node hiearchy. You should be processing this hierarchy in a generic way, and not looking at the names of the nodes for specific keywords (_PIVOT) and things like that. So to answer your question, the fact that the Max and Maya exporters have different node naming conventions is irrelevant.

Thanks for the reply. I definitely understand the node hierarchy and you’re right, it’s generic. But at some point, the generality of the Collada document has to be made specific and converted to work with the application at hand (an engine for example). Generic doesn’t mean “inconsistent”. Every professional game engine I’ve worked with and even my own, will require very specific node hierarchies and formats that will force the node hierarchy into some sort of constraints…force them to be specific nodes in the engine…model object, bones, etc…

For example, an engine may have no need for the “ambient-light-global-scene-node” that get’s exported from Max 7. In fact, this is quite likely so at some point in the art pipeline tool chain, it will have to be determined that this node is irrelevant to the engine and then discarded. The same can be said for vertices, some engines may require that vertices be stored in object space but others may want world space or even pivot space. See what I mean? At some point, the nodes have to be put into some sort of semantic that’s appropriate for the “consumer” of the information.

The particular semantic is, of course, up to the application. I guarantee you that the Unreal 3 Engine vs. RenderWare vs. Doom III engine vs. Joe Bob’s engine will want take Collada’s representation of the node hierarchy and make it “fit” into it’s own “node hierarchy”…culling nodes of no use to the engine…or perhaps converting the representation of the nodes to something the engine understands.

So, to clarify my situation, since my engine needs verts defined in pivot space, I simply have to detect the “_PIVOT” string to know that the object is offset by some transform. This transform defines the “pivot space” and I can pull the verts back into some representation my engine understands. BAM. That’s working.

I just didn’t see how Maya can export something different and still maintain the promise of Collada. Generality is not the problem, consistency is. Does that help?

Naming conventions ARE important if the semantic of a node is put into it’s name. The “_PIVOT” gives the node meaning that is important to the game engine.

Ok, I think I mistook you for someone who’s new to this, and my previous post probably seemed insulting. My apologies.

Why? The pivot transform is just another transform, no more or less important than the others. The only thing that should matter is how to get all the vertices in the same space, and that’s what the transform node hierarchy is for.

If I may add my thoughts on the discussion…
I agree with Lynn here. The problem is that vertices are defined in their pivot space from the beginning. What space are they else defined in? Center of mass?

If the pivot is another transform then what happens if there are several instances of the same geometry? Will the transform move the vertices object data? What happens if we already have moved this data to the GPU and declared it as static? What happens with skinning if vertices have their pivot point defined in the node transform?

I’m not that familiar with Max but what happens if you freeze the transformation on the object that you made with the pivot. I hope the exporter will then export the vertices from a relative position according to the pivot and then you can use the pivot position as a regular transform.

Ok, this conversation has touched a touched a number of subjects. Hopefully I can clarify some of them.

First, the pivots should indeed be treated as generic transforms. They are used to choose a different origin from which the scale and/or rotation will be applied. They should have no impact on the local (object) space in which the vertices are expressed. So it’s possible to have multiple instances, each with their own pivots.

If your engine supports only a limited number of hardcoded transform elements (e.g. one rotate vector, one translate vector, one scale), you’ll have to write code to map the arbitrary Collada transform stack onto that. It’s not fun but it’s certainly possible to do so with minimal loss… witness the fact that we can map animations to/from Maya, Max and/or XSI despite the differences between each DCC.

If you’re writing your own engine, then you should support the concept of animatable transform stacks hierarchy. This is the most general way to represent the transform concept, and it maps nicely to COLLADA, OpenGL and other graphics APIs.

I hope that helps,

I’m sorry that what I am about to say is a little off the topic of what the dcc tools should do but something struck me as odd.

The pivot should not be a child node of the object it is associated with. It should be the parent.
I’m not expert at DCC tool’s inards but it just makes sense to me that since it is a transform heirarchy your transform depends on your parent.
If the pivot is the parent a simple rotate/translate/scale of that node will effect the object its associated with (its child).
But as the example shows, the pivot is a child, rotating that node does nothing to the associated object. Why would a node in a transform heirarchy be effected by its child nodes? thats just backwards.

Excuse me if there is something I am missing.

[edit - I’m sorry upon further inspection it seems that the node names are just backwards. The instance_geometry is in the innermost child as it should be. I’m gonna leave the post up just to illustrate the confusion this can bring about.]

Thanks for all of the replies. I think there is a mix of backgrounds here so I thought maybe I’d rephrase with a small example. I think we have slightly different definitions of the same words for better or worse.

In this example an artist has created a simple model for game engine X. This model consists of 3 objects linked in a hierarchical fashion such that there is only one root object, object A, and objects B and C are children of that object A. Here is the visual scene element from the DAE exported from Max 7 for such a model where:

object A = Box01
object B = Sphere01
object C = Cone01

Also, the pivot points have NOT been modified in Max yet by the artists so there are no “*_PIVOT” nodes in the DAE output from Max. Here it is:

<visual_scene id=“unnamed_scene” name=“unnamed_scene”>
<node id=“Box01” name=“Box01”>
<matrix>1 0 0 1.68131 0 1 0 1.1417 0 0 1 44.5881 0 0 0 1</matrix>
<instance_geometry url="#Box01-obj"></instance_geometry>
<node id=“Sphere01” name=“Sphere01”>
<matrix>1 0 0 -50.2823 0 1 0 19.5414 0 0 1 -44.5881 0 0 0 1</matrix>
<instance_geometry url="#Sphere01-obj"></instance_geometry>
<node id=“Cone01” name=“Cone01”>
<matrix>1 0 0 50.6662 0 1 0 4.14335 0 0 1 -44.5881 0 0 0 1</matrix>
<instance_geometry url="#Cone01-obj"></instance_geometry>

So, a little more info, engine X internally will want to represent this model as 3 nodes connected in a hierarchical fashion very similar to Collada’s. In this case the import is very simple. Each Collada node contains a geometry instance and a corresponding matrix so the conversion process is almost 1 to 1. The engine then does a depth first traversal of the 3 node tree and calculates the worlds transforms of each node by using a standard matrix stack approach. All is good.

Now, say that the artists moves the pivot point of object B (the sphere) let’s say…so now there are 4 nodes output in the Collada DAE. Here it is. The sphere has a child node that is called “Sphere01_PIVOT” that contains the geometry now.

<visual_scene id=“unnamed_scene” name=“unnamed_scene”>
<node id=“Box01” name=“Box01”>
<matrix>1 0 0 1.68131 0 1 0 1.1417 0 0 1 44.5881 0 0 0 1</matrix>
<instance_geometry url="#Box01-obj"></instance_geometry>
<node id=“Sphere01” name=“Sphere01”>
<matrix>1 0 0 7.71774 0 1 0 19.5414 0 0 1 -44.5881 0 0 0 1</matrix>
<node id=“Sphere01_PIVOT” name=“Sphere01_PIVOT”>
<matrix>1 0 0 -58 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
<instance_geometry url="#Sphere01-obj"></instance_geometry>
<node id=“Cone01” name=“Cone01”>
<matrix>1 0 0 50.6662 0 1 0 4.14335 0 0 1 -44.5881 0 0 0 1</matrix>
<instance_geometry url="#Cone01-obj"></instance_geometry>

So, remember that the engine still will want to represent this model with only 3 geometry nodes…so when the collada parser gets to the “Sphere01” node it sees that it has no geometry so it creates a dummy node for the engine. A dummy node is a node in the engine representing only transform, but no geometrical data. No prob…it then gets to the “Sphere01_PIVOT” node and says “Aha…a geometrical child node exists here so let’s create a brand new node with geometry.” So now, the engine internal representation has 4 nodes, when really only 3 nodes are necessary in a optimal, real time representation of this 3 part model.

But now, the engine internal representation of the model has 4 nodes when really we could optimize this and say, well yeah, the model has 4 nodes, but really we could just define the verts in the space of the pivot and collapse the model to 3 parts.

So this can easily be extended to a 50 part model where the artists adjusts all the pivot points and the collada DAE now has a 100 element hierarchy, but really in real time, we could get away with 50 collapsed nodes.

Maybe the whole confusion for me is that…the engine is way more optimized that the DAE represenation of a model. Is there a way to collapse pivot nodes in Max/Maya before export that would take our 4 part model back to a 3 part model?

This is what I meant in my earlier posts, all engines will want to collapse here and there and ingore certain nodes. But for a DAE parser to make these decisions it has to know that the “_PIVOT" node can really just be collapsed by using the matrix in the "_PIVOT” node to transform the verts back into that space, thereby eliminating the need for a superfluous “pivot” node.

I hope I’ve explained this well. I think writing an engine that can use the Collada structure would not be hard, but would be wasteful in the case of adjusting a pivot point. I’m not sure what “freeze transforms” does in Maya or if Max has a similar function…maybe it’s just a matter of the artists pressing a button to “re-bake” the pivots…I dont’ know.

Thanks for all of your responses. This Collada is fun stuff. What I said may have been what you guys are saying but I had to put it into an example.

So now the question is, what else can the parser do but look at the names of objects to know that it has “_PIVOT” attached and therefore can optimize. It seems like there is no other meta-data the parser could use to make this decision.


Ok, I think I understand your situation better, and I’m absolutely convinced that you don’t want to perform any specialized processing on “_PIVOT” nodes. What you want is to collapse the transform hierarchy as much as possible. I’ve been in a similar situation actually. I worked with a converter that output a model with an unnecessarily bloated transform hierarchy, so I wrote a post-conversion pass over the transform hierarchy to collapse as many of the transform nodes as possible. This was mostly to make the model structurally simpler for easier viewing/manipulation in Max’s schematic view. There wasn’t much of a performance improvement because only a small part of model rendering was spent doing matrix multiplies or traversing the node hierarchy.

For your situation, I highly doubt that the Max or Maya exporters are producing extremely bloated transform hierarchies, so I’d recommend just accepting the hierarchies that those tools output and working with them directly.

However if you’re absolutely certain that you want to collapse the hierarchy, you have to understand that whether or not a node has “_PIVOT” in the name doesn’t necessarily mean that it’s collapsable. For example, what if the pivot transform is animated? Then collapsing it wouldn’t be acceptable. What if the pivot node parent contains other child nodes? Then collapsing probably wouldn’t be acceptable. What if a user gives a node a name that has “_PIVOT” in it? Wouldn’t your parser get confused? For all these reasons equating node collapsability with “_PIVOT” isn’t a good idea. You should use a more generic method to determine if a node is collapsable.

It sounds like you’re trying to detect if a node has no geometry but has a child node that does have geometry. If that’s the case, then code against that directly instead of trying to use “_PIVOT” to detect that situation. Think something like this:

if (pCurrentNode->numGeometryInstances == 0  &&
    pCurrentNode->numChildNodes == 1  &&
    pCurrentNode->childNodes[0]->numGeometryInstances == 1)
    // Ding, we have a winner, without ever referring to "_PIVOT" =)

Remember that that isn’t enough to determine if a node is collapsable… you should still make sure none of the nodes are animated, etc. All in all I think you’d be better to just work with the transform hierarchy as output by the Collada plugin. Even if you decide against that, keep in mind that what you’re really after isn’t detecting if a node represents a pivot transform, but instead if a node can be collapsed. They’re separate concepts.

I think the pivot transform is the same as the object-offset transform (isn’t that the term for it in Max?). It should be applied to the geometry at that particular node, but not to the child nodes. As such I think the node names are right. If the names were reversed that would mean the pivot transform is being applied to all child nodes of the Box01 object (using the original example), which is contrary to what the pivot / object-offset transform is trying to do.

stomas…you have helped tremendously. You’re absolutely right!! I took a step back from the problem and read this thread over and over again and came to the exact conclusion that you did.

It’s not that I care about the “_PIVOT”, I just care about the geometry and what node it’s defined in. In fact, the ONLY reason I care about collapsing nodes in the first place (other than the perfomance optimization from less matrix multiplies) is because the particular engine at hand requires vertices to be defined in their pivot space.

:slight_smile: So, you nailed it…I only want to collapse in cases where I need to redefine the verts because of a special pivot node where the space has changed. Thank you so much. My engine allows artists to use dummy nodes as well…and I can detect dummy nodes vs. nodes that need to be collaspsed exacly how you mentioned…by verifying if the current node has no geometry but the child does. The only requirement I have to put on artists is that they can’t have geometry nodes hanging off of dummy nodes (which isn’t vary useful anyway in games from my experience). It is useful however, to leave some dummy nodes in a model for say… places to attach other models like for a character who carries guns in his hands etc… Artists can use dummy nodes and they will be exported correctly as long as they have only 0 children or children made up of nothing but dummy nodes. Sweet.

You’ve really opened up my eyes on this one. Right now my parser uses the name _PIVOT for decision making and it’s working…I will change all of it soon.

If I weren’t concerned with real time performance issues, the Collada node hierarchy would be a perfect one to one translation. To give you an idea on why collapsing nodes is such a win in my book, listen to this. On an Nvidia 6800 Ultra, 3.2 GHz AMD, I could skin about 250, 36-bone skin meshes while each one was playing a 45 second animation and I could maintain 30 FPS. That was BEFORE verts were stored in pivot space. Once verts were stored in pivot space that number increased over 2x to 600-ish skin meshes at 30 FPS.

Thanks again for describing a much better approach. Cheers! Thanks to the others that helped as well.

In conclusion to this thread, I would like to point out that:

  • Collapsing the hierarchy, and may other optimizations, can be (and IMHO should be) done in a separate tool. This is what we call a ‘conditioner’ and have a few simple example of in the COLLADA DOM distribution.
    Load a COLLADA document, do some transformations, save a COLLADA document. The conditioner is part of a build process (makefile) for your data.

  • The beauty of COLLADA is that it is re-entrant. In other words, an optimized COLLADA document, can be loaded back in the DCC tool with no change at all. So you can even use the COLLADA conditioning process as part of the tools that artists can use.

  • Explore the <asset> element, this let you create multiple representation of the same data. Basically, you may want to keep the original data, the data optimized for target A, and the data optimized for target B in the same document. Or any other combination. Use the time stamp in the <asset> tag to determine if the data is out of date compared to the original data, and automatically run the appropriate conditioner if needed.

This is very true. The tool I’m working on is a tool that processes Collada DAE’s for specific targets selectable in Max/Maya. The output is a highly optimized binary stream ready for the target’s consumption.

Great forums.

Hey Lynn, glad to have helped. Thanks for considering what I had to say. Good luck with the project.