Pārlūkot izejas kodu

build a tree of NodeDesc's first, then use to generate joints and tables

David Rose 22 gadi atpakaļ
vecāks
revīzija
74d002cb4e

+ 2 - 0
pandatool/src/mayaegg/Sources.pp

@@ -17,6 +17,8 @@
   #define SOURCES \
     config_mayaegg.cxx config_mayaegg.h \
     mayaEggGroupUserData.cxx mayaEggGroupUserData.I mayaEggGroupUserData.h \
+    mayaNodeDesc.cxx mayaNodeDesc.h \
+    mayaNodeTree.cxx mayaNodeTree.h \
     mayaToEggConverter.cxx mayaToEggConverter.h
 
 #end ss_lib_target

+ 2 - 0
pandatool/src/mayaegg/config_mayaegg.cxx

@@ -18,6 +18,7 @@
 
 #include "config_mayaegg.h"
 #include "mayaEggGroupUserData.h"
+#include "mayaNodeDesc.h"
 
 #include "dconfig.h"
 
@@ -45,5 +46,6 @@ init_libmayaegg() {
   initialized = true;
 
   MayaEggGroupUserData::init_type();
+  MayaNodeDesc::init_type();
 }
 

+ 191 - 0
pandatool/src/mayaegg/mayaNodeDesc.cxx

@@ -0,0 +1,191 @@
+// Filename: mayaNodeDesc.cxx
+// Created by:  drose (06Jun03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "mayaNodeDesc.h"
+
+TypeHandle MayaNodeDesc::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MayaNodeDesc::
+MayaNodeDesc(MayaNodeDesc *parent, const string &name) :
+  Namable(name),
+  _parent(parent)
+{
+  _dag_path = (MDagPath *)NULL;
+  _egg_group = (EggGroup *)NULL;
+  _egg_table = (EggTable *)NULL;
+  _anim = (EggXfmSAnim *)NULL;
+  _joint_type = JT_none;
+
+  // Add ourselves to our parent.
+  if (_parent != (MayaNodeDesc *)NULL) {
+    _parent->_children.push_back(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MayaNodeDesc::
+~MayaNodeDesc() {
+  if (_dag_path != (MDagPath *)NULL) {
+    delete _dag_path;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::from_dag_path
+//       Access: Public
+//  Description: Indicates an associated between the MayaNodeDesc and
+//               some Maya instance.
+////////////////////////////////////////////////////////////////////
+void MayaNodeDesc::
+from_dag_path(const MDagPath &dag_path) {
+  if (_dag_path == (MDagPath *)NULL) {
+    _dag_path = new MDagPath(dag_path);
+
+    if (_dag_path->hasFn(MFn::kJoint)) {
+      // This node is a joint.
+      _joint_type = JT_joint;
+      if (_parent != (MayaNodeDesc *)NULL) {
+        _parent->mark_joint_parent();
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::has_dag_path
+//       Access: Public
+//  Description: Returns true if a Maya dag path has been associated
+//               with this node, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool MayaNodeDesc::
+has_dag_path() const {
+  return (_dag_path != (MDagPath *)NULL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::get_dag_path
+//       Access: Public
+//  Description: Returns the dag path associated with this node.  It
+//               is an error to call this unless has_dag_path()
+//               returned true.
+////////////////////////////////////////////////////////////////////
+const MDagPath &MayaNodeDesc::
+get_dag_path() const {
+  nassertr(_dag_path != (MDagPath *)NULL, *_dag_path);
+  return *_dag_path;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::is_joint
+//       Access: Private
+//  Description: Returns true if the node should be treated as a joint
+//               by the converter.
+////////////////////////////////////////////////////////////////////
+bool MayaNodeDesc::
+is_joint() const {
+  return _joint_type == JT_joint || _joint_type == JT_pseudo_joint;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::is_joint_parent
+//       Access: Private
+//  Description: Returns true if the node is the parent or ancestor of
+//               a joint.
+////////////////////////////////////////////////////////////////////
+bool MayaNodeDesc::
+is_joint_parent() const {
+  return _joint_type == JT_joint_parent;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::clear_egg
+//       Access: Private
+//  Description: Recursively clears the egg pointers from this node
+//               and all children.
+////////////////////////////////////////////////////////////////////
+void MayaNodeDesc::
+clear_egg() {
+  _egg_group = (EggGroup *)NULL;
+  _egg_table = (EggTable *)NULL;
+  _anim = (EggXfmSAnim *)NULL;
+
+  Children::const_iterator ci;
+  for (ci = _children.begin(); ci != _children.end(); ++ci) {
+    MayaNodeDesc *child = (*ci);
+    child->clear_egg();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::mark_joint_parent
+//       Access: Private
+//  Description: Indicates that this node has at least one child that
+//               is a joint or a pseudo-joint.
+////////////////////////////////////////////////////////////////////
+void MayaNodeDesc::
+mark_joint_parent() {
+  if (_joint_type == JT_none) {
+    _joint_type = JT_joint_parent;
+    if (_parent != (MayaNodeDesc *)NULL) {
+      _parent->mark_joint_parent();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeDesc::check_pseudo_joints
+//       Access: Private
+//  Description: Walks the hierarchy, looking for non-joint nodes that
+//               are both children and parents of a joint.  These
+//               nodes are deemed to be pseudo joints, since the
+//               converter must treat them as joints.
+////////////////////////////////////////////////////////////////////
+void MayaNodeDesc::
+check_pseudo_joints(bool joint_above) {
+  if (_joint_type == JT_joint_parent && joint_above) {
+    // This is one such node: it is the parent of a joint
+    // (JT_joint_parent is set), and it is the child of a joint
+    // (joint_above is set).
+    _joint_type = JT_pseudo_joint;
+  }
+
+  if (_joint_type == JT_joint) {
+    // If this node is itself a joint, then joint_above is true for
+    // all child nodes.
+    joint_above = true;
+  }
+
+  // Don't bother traversing further if _joint_type is none, since
+  // that means this node has no joint children.
+  if (_joint_type != JT_none) {
+    Children::const_iterator ci;
+    for (ci = _children.begin(); ci != _children.end(); ++ci) {
+      MayaNodeDesc *child = (*ci);
+      child->check_pseudo_joints(joint_above);
+    }
+  }
+}

+ 98 - 0
pandatool/src/mayaegg/mayaNodeDesc.h

@@ -0,0 +1,98 @@
+// Filename: mayaNodeDesc.h
+// Created by:  drose (06Jun03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MAYANODEDESC_H
+#define MAYANODEDESC_H
+
+#include "pandatoolbase.h"
+
+#include "referenceCount.h"
+#include "pointerTo.h"
+#include "namable.h"
+
+#include "pre_maya_include.h"
+#include <maya/MDagPath.h>
+#include "post_maya_include.h"
+
+class EggGroup;
+class EggTable;
+class EggXfmSAnim;
+
+////////////////////////////////////////////////////////////////////
+//       Class : MayaNodeDesc
+// Description : Describes a single instance of a node in the Maya
+//               scene graph, relating it to the corresponding egg
+//               structures (e.g. node, group, or table entry) that
+//               will be created.
+////////////////////////////////////////////////////////////////////
+class MayaNodeDesc : public ReferenceCount, public Namable {
+public:
+  MayaNodeDesc(MayaNodeDesc *parent = NULL, const string &name = string());
+  ~MayaNodeDesc();
+
+  void from_dag_path(const MDagPath &dag_path);
+  bool has_dag_path() const;
+  const MDagPath &get_dag_path() const;
+
+  bool is_joint() const;
+  bool is_joint_parent() const;
+
+  MayaNodeDesc *_parent;
+  typedef pvector< PT(MayaNodeDesc) > Children;
+  Children _children;
+  
+private:
+  void clear_egg();
+  void mark_joint_parent();
+  void check_pseudo_joints(bool joint_above);
+
+  MDagPath *_dag_path;
+
+  EggGroup *_egg_group;
+  EggTable *_egg_table;
+  EggXfmSAnim *_anim;
+
+  enum JointType {
+    JT_none,         // Not a joint.
+    JT_joint,        // An actual joint in Maya.
+    JT_pseudo_joint, // Not a joint in Maya, but treated just like a
+                     // joint for the purposes of the converter.
+    JT_joint_parent, // A parent or ancestor of a joint or pseudo joint.
+  };
+  JointType _joint_type;
+
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ReferenceCount::init_type();
+    Namable::init_type();
+    register_type(_type_handle, "MayaNodeDesc",
+                  ReferenceCount::get_class_type(),
+                  Namable::get_class_type());
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class MayaNodeTree;
+};
+
+#endif

+ 387 - 0
pandatool/src/mayaegg/mayaNodeTree.cxx

@@ -0,0 +1,387 @@
+// Filename: mayaNodeTree.cxx
+// Created by:  drose (06Jun03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "mayaNodeTree.h"
+#include "mayaEggGroupUserData.h"
+#include "config_mayaegg.h"
+#include "maya_funcs.h"
+#include "eggGroup.h"
+#include "eggTable.h"
+#include "eggXfmSAnim.h"
+#include "eggData.h"
+
+#include "pre_maya_include.h"
+#include <maya/MString.h>
+#include <maya/MItDag.h>
+#include <maya/MSelectionList.h>
+#include <maya/MGlobal.h>
+#include "post_maya_include.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MayaNodeTree::
+MayaNodeTree() {
+  _root = new MayaNodeDesc;
+  _fps = 0.0;
+  _egg_data = (EggData *)NULL;
+  _egg_root = (EggGroupNode *)NULL;
+  _skeleton_node = (EggGroupNode *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::build_node
+//       Access: Public
+//  Description: Returns a pointer to the node corresponding to the
+//               indicated dag_path object, creating it first if
+//               necessary.
+////////////////////////////////////////////////////////////////////
+MayaNodeDesc *MayaNodeTree::
+build_node(const MDagPath &dag_path) {
+  MayaNodeDesc *node_desc = r_build_node(dag_path.fullPathName().asChar());
+  node_desc->from_dag_path(dag_path);
+  return node_desc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::build_complete_hierarchy
+//       Access: Public
+//  Description: Walks through the complete Maya hierarchy and builds
+//               up the corresponding tree.
+////////////////////////////////////////////////////////////////////
+bool MayaNodeTree::
+build_complete_hierarchy() {
+  MStatus status;
+
+  MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
+  if (!status) {
+    status.perror("MItDag constructor");
+    return false;
+  }
+
+  // Get the entire Maya scene.
+    
+  // This while loop walks through the entire Maya hierarchy, one
+  // node at a time.  Maya's MItDag object automatically performs a
+  // depth-first traversal of its scene graph.
+  
+  bool all_ok = true;
+  while (!dag_iterator.isDone()) {
+    MDagPath dag_path;
+    status = dag_iterator.getPath(dag_path);
+    if (!status) {
+      status.perror("MItDag::getPath");
+    } else {
+      build_node(dag_path);
+    }
+    
+    dag_iterator.next();
+  }
+
+  if (all_ok) {
+    _root->check_pseudo_joints(false);
+  }
+  
+  return all_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::build_selected_hierarchy
+//       Access: Public
+//  Description: Walks through the selected subset of the Maya
+//               hierarchy (or the complete hierarchy, if nothing is
+//               selected) and builds up the corresponding tree.
+////////////////////////////////////////////////////////////////////
+bool MayaNodeTree::
+build_selected_hierarchy() {
+  MStatus status;
+
+  MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
+  if (!status) {
+    status.perror("MItDag constructor");
+    return false;
+  }
+
+  // Get only the selected geometry.
+  MSelectionList selection;
+  status = MGlobal::getActiveSelectionList(selection);
+  if (!status) {
+    status.perror("MGlobal::getActiveSelectionList");
+    return false;
+  }
+  
+  // Get the selected geometry only if the selection is nonempty;
+  // otherwise, get the whole scene anyway.
+  if (selection.isEmpty()) {
+    mayaegg_cat.info()
+      << "Selection list is empty.\n";
+    return build_complete_hierarchy();
+  }
+
+  bool all_ok = true;
+  unsigned int length = selection.length();
+  for (unsigned int i = 0; i < length; i++) {
+    MDagPath root_path;
+    status = selection.getDagPath(i, root_path);
+    if (!status) {
+      status.perror("MSelectionList::getDagPath");
+    } else {
+      // Now traverse through the selected dag path and all nested
+      // dag paths.
+      dag_iterator.reset(root_path);
+      while (!dag_iterator.isDone()) {
+        MDagPath dag_path;
+        status = dag_iterator.getPath(dag_path);
+        if (!status) {
+          status.perror("MItDag::getPath");
+        } else {
+          build_node(dag_path);
+        }
+        
+        dag_iterator.next();
+      }
+    }
+  }
+
+  if (all_ok) {
+    _root->check_pseudo_joints(false);
+  }
+
+  return all_ok;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::get_num_nodes
+//       Access: Public
+//  Description: Returns the total number of nodes in the hierarchy,
+//               not counting the root node.
+////////////////////////////////////////////////////////////////////
+int MayaNodeTree::
+get_num_nodes() const {
+  return _nodes.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::get_node
+//       Access: Public
+//  Description: Returns the nth node in the hierarchy, in an
+//               arbitrary ordering.
+////////////////////////////////////////////////////////////////////
+MayaNodeDesc *MayaNodeTree::
+get_node(int n) const {
+  nassertr(n >= 0 && n < (int)_nodes.size(), NULL);
+  return _nodes[n];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::clear_egg
+//       Access: Public
+//  Description: Removes all of the references to generated egg
+//               structures from the tree, and prepares the tree for
+//               generating new egg structures.
+////////////////////////////////////////////////////////////////////
+void MayaNodeTree::
+clear_egg(EggData *egg_data, EggGroupNode *egg_root, 
+          EggGroupNode *skeleton_node) {
+  _root->clear_egg();
+  _egg_data = egg_data;
+  _egg_root = egg_root;
+  _skeleton_node = skeleton_node;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::get_egg_group
+//       Access: Public
+//  Description: Returns the EggGroupNode corresponding to the group
+//               or joint for the indicated node.  Creates the group
+//               node if it has not already been created.
+////////////////////////////////////////////////////////////////////
+EggGroup *MayaNodeTree::
+get_egg_group(MayaNodeDesc *node_desc) {
+  nassertr(_egg_root != (EggGroupNode *)NULL, NULL);
+
+  if (node_desc->_egg_group == (EggGroup *)NULL) {
+    // We need to make a new group node.
+    EggGroup *egg_group;
+
+    nassertr(node_desc->_parent != (MayaNodeDesc *)NULL, NULL);
+    egg_group = new EggGroup(node_desc->get_name());
+    if (node_desc->is_joint()) {
+      egg_group->set_group_type(EggGroup::GT_joint);
+    }
+
+    if (node_desc->_parent == _root) {
+      // The parent is the root.
+      _egg_root->add_child(egg_group);
+
+    } else {
+      // The parent is another node.
+      EggGroup *parent_egg_group = get_egg_group(node_desc->_parent);
+      parent_egg_group->add_child(egg_group);
+    }
+
+    if (node_desc->has_dag_path()) {
+      // Check for an object type setting, from Oliver's plug-in.
+      MObject dag_object = node_desc->get_dag_path().node();
+      string object_type;
+      if (get_enum_attribute(dag_object, "eggObjectTypes1", object_type)) {
+        egg_group->add_object_type(object_type);
+      }
+      if (get_enum_attribute(dag_object, "eggObjectTypes2", object_type)) {
+        egg_group->add_object_type(object_type);
+      }
+      if (get_enum_attribute(dag_object, "eggObjectTypes3", object_type)) {
+        egg_group->add_object_type(object_type);
+      }
+
+      // We treat the object type "billboard" as a special case: we
+      // apply this one right away and also flag the group as an
+      // instance.
+      if (egg_group->has_object_type("billboard")) {    
+        egg_group->remove_object_type("billboard");
+        egg_group->set_group_type(EggGroup::GT_instance);
+        egg_group->set_billboard_type(EggGroup::BT_axis);
+        
+      } else if (egg_group->has_object_type("billboard-point")) {    
+        egg_group->remove_object_type("billboard-point");
+        egg_group->set_group_type(EggGroup::GT_instance);
+        egg_group->set_billboard_type(EggGroup::BT_point_camera_relative);
+      }
+      
+      // We also treat the object type "dcs" and "model" as a special
+      // case, so we can test for these flags later.
+      if (egg_group->has_object_type("dcs")) {
+        egg_group->remove_object_type("dcs");
+        egg_group->set_dcs_type(EggGroup::DC_default);
+      }
+      if (egg_group->has_object_type("model")) {
+        egg_group->remove_object_type("model");
+        egg_group->set_model_flag(true);
+      }
+      
+      // And "vertex-color" has meaning only to this converter.
+      if (egg_group->has_object_type("vertex-color")) {
+        egg_group->remove_object_type("vertex-color");
+        MayaEggGroupUserData *user_data = new MayaEggGroupUserData;
+        user_data->_vertex_color = true;
+        egg_group->set_user_data(user_data);
+      }
+    }
+
+    node_desc->_egg_group = egg_group;
+  }
+
+  return node_desc->_egg_group;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::get_egg_table
+//       Access: Public
+//  Description: Returns the EggTable corresponding to the joint
+//               for the indicated node.  Creates the table node if it
+//               has not already been created.
+////////////////////////////////////////////////////////////////////
+EggTable *MayaNodeTree::
+get_egg_table(MayaNodeDesc *node_desc) {
+  nassertr(_skeleton_node != (EggGroupNode *)NULL, NULL);
+  nassertr(node_desc->is_joint(), NULL);
+
+  if (node_desc->_egg_table == (EggTable *)NULL) {
+    // We need to make a new table node.
+    nassertr(node_desc->_parent != (MayaNodeDesc *)NULL, NULL);
+
+    EggTable *egg_table = new EggTable(node_desc->get_name());
+    node_desc->_anim = new EggXfmSAnim("xform", _egg_data->get_coordinate_system());
+    node_desc->_anim->set_fps(_fps);
+    egg_table->add_child(node_desc->_anim);
+
+    if (!node_desc->_parent->is_joint()) {
+      // The parent is not a joint; put it at the top.
+      _skeleton_node->add_child(egg_table);
+
+    } else {
+      // The parent is another joint.
+      EggTable *parent_egg_table = get_egg_table(node_desc->_parent);
+      parent_egg_table->add_child(egg_table);
+    }
+
+    node_desc->_egg_table = egg_table;
+  }
+
+  return node_desc->_egg_table;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::get_egg_anim
+//       Access: Public
+//  Description: Returns the anim table corresponding to the joint
+//               for the indicated node.  Creates the table node if it
+//               has not already been created.
+////////////////////////////////////////////////////////////////////
+EggXfmSAnim *MayaNodeTree::
+get_egg_anim(MayaNodeDesc *node_desc) {
+  get_egg_table(node_desc);
+  return node_desc->_anim;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaNodeTree::r_build_node
+//       Access: Private
+//  Description: The recursive implementation of build_node().
+////////////////////////////////////////////////////////////////////
+MayaNodeDesc *MayaNodeTree::
+r_build_node(const string &path) {
+  // If we have already encountered this pathname, return the
+  // corresponding MayaNodeDesc immediately.
+  NodesByPath::const_iterator ni = _nodes_by_path.find(path);
+  if (ni != _nodes_by_path.end()) {
+    return (*ni).second;
+  }
+
+  // Otherwise, we have to create it.  Do this recursively, so we
+  // create each node along the path.
+  MayaNodeDesc *node_desc;
+
+  if (path.empty()) {
+    // This is the top.
+    node_desc = _root;
+
+  } else {
+    // Maya uses vertical bars to separate path components.  Remove
+    // everything from the rightmost bar on; this will give us the
+    // parent's path name.
+    size_t bar = path.rfind("|");
+    string parent_path, local_name;
+    if (bar != string::npos) {
+      parent_path = path.substr(0, bar);
+      local_name = path.substr(bar + 1);
+    } else {
+      local_name = path;
+    }
+
+    MayaNodeDesc *parent_node_desc = r_build_node(parent_path);
+    node_desc = new MayaNodeDesc(parent_node_desc, local_name);
+    _nodes.push_back(node_desc);
+  }
+
+  _nodes_by_path.insert(NodesByPath::value_type(path, node_desc));
+  return node_desc;
+}

+ 67 - 0
pandatool/src/mayaegg/mayaNodeTree.h

@@ -0,0 +1,67 @@
+// Filename: mayaNodeTree.h
+// Created by:  drose (06Jun03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MAYANODETREE_H
+#define MAYANODETREE_H
+
+#include "pandatoolbase.h"
+
+#include "mayaNodeDesc.h"
+
+class EggData;
+class EggGroupNode;
+
+////////////////////////////////////////////////////////////////////
+//       Class : MayaNodeTree
+// Description : Describes a complete tree of maya nodes for
+//               conversion.
+////////////////////////////////////////////////////////////////////
+class MayaNodeTree {
+public:
+  MayaNodeTree();
+  MayaNodeDesc *build_node(const MDagPath &dag_path);
+  bool build_complete_hierarchy();
+  bool build_selected_hierarchy();
+
+  int get_num_nodes() const;
+  MayaNodeDesc *get_node(int n) const;
+
+  void clear_egg(EggData *egg_data, EggGroupNode *egg_root, 
+                 EggGroupNode *skeleton_node);
+  EggGroup *get_egg_group(MayaNodeDesc *node_desc);
+  EggTable *get_egg_table(MayaNodeDesc *node_desc);
+  EggXfmSAnim *get_egg_anim(MayaNodeDesc *node_desc);
+
+  PT(MayaNodeDesc) _root;
+  float _fps;
+
+private:
+  EggData *_egg_data;
+  EggGroupNode *_egg_root;
+  EggGroupNode *_skeleton_node;
+
+  MayaNodeDesc *r_build_node(const string &path);
+
+  typedef pmap<string, MayaNodeDesc *> NodesByPath;
+  NodesByPath _nodes_by_path;
+
+  typedef pvector<MayaNodeDesc *> Nodes;
+  Nodes _nodes;
+};
+
+#endif

+ 169 - 505
pandatool/src/mayaegg/mayaToEggConverter.cxx

@@ -188,8 +188,6 @@ convert_maya(bool from_selection) {
   _from_selection = from_selection;
   _textures.clear();
   _shaders.clear();
-  _groups.clear();
-  _tables.clear();
 
   if (!open_api()) {
     mayaegg_cat.error()
@@ -235,53 +233,61 @@ convert_maya(bool from_selection) {
 
   bool all_ok = true;
 
-  switch (get_animation_convert()) {
-  case AC_pose:
-    // pose: set to a specific frame, then get out the static geometry.
-    mayaegg_cat.info(false)
-      << "frame " << start_frame << "\n";
-    MGlobal::viewFrame(MTime(start_frame, MTime::uiUnit()));
-    // fall through
-
-  case AC_none:
-    // none: just get out a static model, no animation.
-    all_ok = convert_hierarchy(&get_egg_data());
-    break;
-
-  case AC_flip:
-    // flip: get out a series of static models, one per frame, under a
-    // sequence node.
-    all_ok = convert_flip(start_frame, end_frame, frame_inc,
-                          output_frame_rate);
-    break;
-
-  case AC_model:
-    // model: get out an animatable model with joints and vertex
-    // membership.
-    all_ok = convert_char_model();
-    break;
-
-  case AC_chan:
-    // chan: get out a series of animation tables.
-    all_ok = convert_char_chan(start_frame, end_frame, frame_inc,
-                               output_frame_rate);
-    break;
+  if (_from_selection) {
+    all_ok = _tree.build_selected_hierarchy();
+  } else {
+    all_ok = _tree.build_complete_hierarchy();
+  }
 
-  case AC_both:
-    // both: Put a model and its animation into the same egg file.
-    _animation_convert = AC_model;
-    if (!convert_char_model()) {
-      all_ok = false;
-    }
-    _animation_convert = AC_chan;
-    if (!convert_char_chan(start_frame, end_frame, frame_inc,
-                           output_frame_rate)) {
-      all_ok = false;
-    }
-    break;
-  };
+  if (all_ok) {
+    switch (get_animation_convert()) {
+    case AC_pose:
+      // pose: set to a specific frame, then get out the static geometry.
+      mayaegg_cat.info(false)
+        << "frame " << start_frame << "\n";
+      MGlobal::viewFrame(MTime(start_frame, MTime::uiUnit()));
+      // fall through
+      
+    case AC_none:
+      // none: just get out a static model, no animation.
+      all_ok = convert_hierarchy(&get_egg_data());
+      break;
+      
+    case AC_flip:
+      // flip: get out a series of static models, one per frame, under a
+      // sequence node.
+      all_ok = convert_flip(start_frame, end_frame, frame_inc,
+                            output_frame_rate);
+      break;
+
+    case AC_model:
+      // model: get out an animatable model with joints and vertex
+      // membership.
+      all_ok = convert_char_model();
+      break;
+
+    case AC_chan:
+      // chan: get out a series of animation tables.
+      all_ok = convert_char_chan(start_frame, end_frame, frame_inc,
+                                 output_frame_rate);
+      break;
+      
+    case AC_both:
+      // both: Put a model and its animation into the same egg file.
+      _animation_convert = AC_model;
+      if (!convert_char_model()) {
+        all_ok = false;
+      }
+      _animation_convert = AC_chan;
+      if (!convert_char_chan(start_frame, end_frame, frame_inc,
+                             output_frame_rate)) {
+        all_ok = false;
+      }
+      break;
+    };
 
-  reparent_decals(&get_egg_data());
+    reparent_decals(&get_egg_data());
+  }
 
   if (all_ok) {
     mayaegg_cat.info()
@@ -355,10 +361,9 @@ convert_flip(double start_frame, double end_frame, double frame_inc,
     sequence_node->add_child(frame_root);
 
     MGlobal::viewFrame(frame);
-    if (!convert_hierarchy(frame_root)) {
+    if (!convert_hierarchy(&get_egg_data())) {
       all_ok = false;
     }
-    _groups.clear();
 
     frame += frame_inc;
   }
@@ -408,38 +413,10 @@ convert_char_chan(double start_frame, double end_frame, double frame_inc,
   EggTable *skeleton_node = new EggTable("<skeleton>");
   bundle_node->add_child(skeleton_node);
 
-  // First, walk through the scene graph and build up the table of
-  // joints.
-  MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
-  if (!status) {
-    status.perror("MItDag constructor");
-    return false;
-  }
-  bool all_ok = true;
-  while (!dag_iterator.isDone()) {
-    MDagPath dag_path;
-    status = dag_iterator.getPath(dag_path);
-    if (!status) {
-      status.perror("MItDag::getPath");
-    } else {
-      if (!process_chan_node(dag_path, skeleton_node)) {
-        all_ok = false;
-      }
-    }
-
-    dag_iterator.next();
-  }
-
-  // Now walk through the joints in that table and make sure they're
-  // all set to use the correct frame frame.
-  double fps = output_frame_rate / frame_inc;
-  Tables::iterator ti;
-  for (ti = _tables.begin(); ti != _tables.end(); ++ti) {
-    JointAnim *joint_anim = (*ti).second;
-    nassertr(joint_anim != (JointAnim *)NULL && 
-             joint_anim->_anim != (EggXfmSAnim *)NULL, false);
-    joint_anim->_anim->set_fps(fps);
-  }
+  // Set the frame rate before we start asking for anim tables to be
+  // created.
+  _tree._fps = output_frame_rate / frame_inc;
+  _tree.clear_egg(&get_egg_data(), NULL, skeleton_node);
 
   // Now we can get the animation data by walking through all of the
   // frames, one at a time, and getting the joint angles at each
@@ -449,6 +426,9 @@ convert_char_chan(double start_frame, double end_frame, double frame_inc,
   // each joint each frame.
   PT(EggGroup) tgroup = new EggGroup;
 
+  int num_nodes = _tree.get_num_nodes();
+  int i;
+
   MTime frame(start_frame, MTime::uiUnit());
   MTime frame_stop(end_frame, MTime::uiUnit());
   while (frame <= frame_stop) {
@@ -461,114 +441,50 @@ convert_char_chan(double start_frame, double end_frame, double frame_inc,
     }
     MGlobal::viewFrame(frame);
 
-    for (ti = _tables.begin(); ti != _tables.end(); ++ti) {
-      JointAnim *joint_anim = (*ti).second;
-      get_joint_transform(joint_anim->_dag_path, tgroup);
-      joint_anim->_anim->add_data(tgroup->get_transform());
+    for (i = 0; i < num_nodes; i++) {
+      MayaNodeDesc *node_desc = _tree.get_node(i);
+      if (node_desc->is_joint()) {
+        get_joint_transform(node_desc->get_dag_path(), tgroup);
+        _tree.get_egg_anim(node_desc)->add_data(tgroup->get_transform());
+      }
     }
 
     frame += frame_inc;
   }
-  mayaegg_cat.info(false)
-    << "\n";
 
-  // Finally, clean up by deleting all of the JointAnim structures we
-  // created.
-  for (ti = _tables.begin(); ti != _tables.end(); ++ti) {
-    JointAnim *joint_anim = (*ti).second;
-    delete joint_anim;
+  // Now optimize all of the tables we just filled up, for no real
+  // good reason.
+  for (i = 0; i < num_nodes; i++) {
+    MayaNodeDesc *node_desc = _tree.get_node(i);
+    if (node_desc->is_joint()) {
+      _tree.get_egg_anim(node_desc)->optimize();
+    }
   }
-  _tables.clear();
 
-  return all_ok;
+  mayaegg_cat.info(false)
+    << "\n";
+
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: MayaToEggConverter::convert_hierarchy
 //       Access: Private
-//  Description: Walks the entire Maya hierarchy, converting it to a
-//               corresponding egg hierarchy under the indicated root
-//               node.
+//  Description: Generates egg structures for each node in the Maya
+//               hierarchy.
 ////////////////////////////////////////////////////////////////////
 bool MayaToEggConverter::
 convert_hierarchy(EggGroupNode *egg_root) {
-  MStatus status;
-
-  MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
-  if (!status) {
-    status.perror("MItDag constructor");
-    return false;
-  }
+  int num_nodes = _tree.get_num_nodes();
 
-  if (_from_selection) {
-    // Get only the selected geometry.
-    MSelectionList selection;
-    status = MGlobal::getActiveSelectionList(selection);
-    if (!status) {
-      status.perror("MGlobal::getActiveSelectionList");
+  _tree.clear_egg(&get_egg_data(), egg_root, NULL);
+  for (int i = 0; i < num_nodes; i++) {
+    if (!process_model_node(_tree.get_node(i))) {
       return false;
     }
-
-    // Get the selected geometry only if the selection is nonempty;
-    // otherwise, get the whole scene anyway.
-    if (!selection.isEmpty()) {
-      bool all_ok = true;
-      unsigned int length = selection.length();
-      for (unsigned int i = 0; i < length; i++) {
-        MDagPath root_path;
-        status = selection.getDagPath(i, root_path);
-        if (!status) {
-          status.perror("MSelectionList::getDagPath");
-        } else {
-          // Now traverse through the selected dag path and all nested
-          // dag paths.
-          dag_iterator.reset(root_path);
-          while (!dag_iterator.isDone()) {
-            MDagPath dag_path;
-            status = dag_iterator.getPath(dag_path);
-            if (!status) {
-              status.perror("MItDag::getPath");
-            } else {
-              if (!process_model_node(dag_path, egg_root)) {
-                all_ok = false;
-              }
-            }
-            
-            dag_iterator.next();
-          }
-        }
-      }
-      return all_ok;
-
-    } else {
-      mayaegg_cat.info()
-        << "Selection list is empty.\n";
-      // Fall through.
-    }
   }
 
-  // Get the entire Maya scene.
-    
-  // This while loop walks through the entire Maya hierarchy, one
-  // node at a time.  Maya's MItDag object automatically performs a
-  // depth-first traversal of its scene graph.
-  
-  bool all_ok = true;
-  while (!dag_iterator.isDone()) {
-    MDagPath dag_path;
-    status = dag_iterator.getPath(dag_path);
-    if (!status) {
-      status.perror("MItDag::getPath");
-    } else {
-      if (!process_model_node(dag_path, egg_root)) {
-        all_ok = false;
-      }
-    }
-    
-    dag_iterator.next();
-  }
-  
-  return all_ok;
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -580,7 +496,14 @@ convert_hierarchy(EggGroupNode *egg_root) {
 //               successful, false if an error was encountered.
 ////////////////////////////////////////////////////////////////////
 bool MayaToEggConverter::
-process_model_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
+process_model_node(MayaNodeDesc *node_desc) {
+  if (!node_desc->has_dag_path()) {
+    // If the node has no Maya equivalent, never mind.
+    return true;
+  }
+
+  MDagPath dag_path = node_desc->get_dag_path();
+
   MStatus status;
   MFnDagNode dag_node(dag_path, &status);
   if (!status) {
@@ -588,9 +511,11 @@ process_model_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
     return false;
   }
 
+  string path = dag_path.fullPathName().asChar();
+
   if (mayaegg_cat.is_debug()) {
     mayaegg_cat.debug()
-      << dag_path.fullPathName().asChar() << ": " << dag_node.typeName();
+      << path << ": " << dag_node.typeName();
 
     if (MAnimUtil::isAnimated(dag_path)) {
       mayaegg_cat.debug(false)
@@ -600,70 +525,54 @@ process_model_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
     mayaegg_cat.debug(false) << "\n";
   }
 
-  if (dag_node.inUnderWorld()) {
+  if (node_desc->is_joint()) {
+    // Don't bother with joints unless we're getting an animatable
+    // model.
+    if (_animation_convert == AC_model) { 
+      EggGroup *egg_group = _tree.get_egg_group(node_desc);
+      get_joint_transform(dag_path, egg_group);
+    }
+
+  } else if (dag_node.inUnderWorld()) {
     if (mayaegg_cat.is_debug()) {
       mayaegg_cat.debug()
-        << "Ignoring underworld node " << dag_path.fullPathName().asChar()
+        << "Ignoring underworld node " << path
         << "\n";
     }
 
   } else if (dag_node.isIntermediateObject()) {
     if (mayaegg_cat.is_debug()) {
       mayaegg_cat.debug()
-        << "Ignoring intermediate object " << dag_path.fullPathName().asChar()
+        << "Ignoring intermediate object " << path
         << "\n";
     }
 
   } else if (dag_path.hasFn(MFn::kCamera)) {
     if (mayaegg_cat.is_debug()) {
       mayaegg_cat.debug()
-        << "Ignoring camera node " << dag_path.fullPathName().asChar()
+        << "Ignoring camera node " << path
         << "\n";
     }
 
   } else if (dag_path.hasFn(MFn::kLight)) {
     if (mayaegg_cat.is_debug()) {
       mayaegg_cat.debug()
-        << "Ignoring light node " << dag_path.fullPathName().asChar()
+        << "Ignoring light node " << path
         << "\n";
     }
 
-  } else if (dag_path.hasFn(MFn::kJoint)) {
-    // A joint.
-
-    // Don't bother with joints unless we're getting an animatable
-    // model.
-    if (_animation_convert == AC_model) {
-      EggGroup *egg_group = get_egg_group(dag_path, egg_root);
-
-      if (egg_group != (EggGroup *)NULL) {
-        egg_group->set_group_type(EggGroup::GT_joint);
-        get_joint_transform(dag_path, egg_group);
-      }
-    }
-
   } else if (dag_path.hasFn(MFn::kNurbsSurface)) {
-    EggGroup *egg_group = get_egg_group(dag_path, egg_root);
-
-    if (egg_group == (EggGroup *)NULL) {
-      mayaegg_cat.error()
-        << "Cannot determine group node.\n";
-      return false;
-
+    EggGroup *egg_group = _tree.get_egg_group(node_desc);
+    get_transform(dag_path, egg_group);
+    
+    MFnNurbsSurface surface(dag_path, &status);
+    if (!status) {
+      mayaegg_cat.info()
+        << "Error in node " << path
+        << ":\n"
+        << "  it appears to have a NURBS surface, but does not.\n";
     } else {
-      if (_animation_convert != AC_model) {
-        get_transform(dag_path, egg_group);
-      }
-
-      MFnNurbsSurface surface(dag_path, &status);
-      if (!status) {
-        mayaegg_cat.info()
-          << "Error in node " << dag_path.fullPathName().asChar()
-          << ":\n"
-          << "  it appears to have a NURBS surface, but does not.\n";
-      } else {
-        make_nurbs_surface(dag_path, surface, egg_group, egg_root);
-      }
+      make_nurbs_surface(dag_path, surface, egg_group);
     }
 
   } else if (dag_path.hasFn(MFn::kNurbsCurve)) {
@@ -671,124 +580,57 @@ process_model_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
     // Animated models, as a general rule, don't want these sorts of
     // things in them.
     if (_animation_convert != AC_model) {
-      EggGroup *egg_group = get_egg_group(dag_path, egg_root);
+      EggGroup *egg_group = _tree.get_egg_group(node_desc);
+      get_transform(dag_path, egg_group);
       
-      if (egg_group == (EggGroup *)NULL) {
-        mayaegg_cat.error()
-          << "Cannot determine group node.\n";
-        
+      MFnNurbsCurve curve(dag_path, &status);
+      if (!status) {
+        mayaegg_cat.info()
+          << "Error in node " << path << ":\n"
+          << "  it appears to have a NURBS curve, but does not.\n";
       } else {
-        get_transform(dag_path, egg_group);
-        
-        MFnNurbsCurve curve(dag_path, &status);
-        if (!status) {
-          mayaegg_cat.info()
-            << "Error in node " << dag_path.fullPathName().asChar() << ":\n"
-            << "  it appears to have a NURBS curve, but does not.\n";
-        } else {
-          make_nurbs_curve(dag_path, curve, egg_group, egg_root);
-        }
+        make_nurbs_curve(dag_path, curve, egg_group);
       }
     }
       
   } else if (dag_path.hasFn(MFn::kMesh)) {
-    EggGroup *egg_group = get_egg_group(dag_path, egg_root);
-
-    if (egg_group == (EggGroup *)NULL) {
-      mayaegg_cat.error()
-        << "Cannot determine group node.\n";
-      return false;
+    EggGroup *egg_group = _tree.get_egg_group(node_desc);
+    get_transform(dag_path, egg_group);
 
+    MFnMesh mesh(dag_path, &status);
+    if (!status) {
+      mayaegg_cat.info()
+        << "Error in node " << path << ":\n"
+        << "  it appears to have a polygon mesh, but does not.\n";
     } else {
-      if (_animation_convert != AC_model) {
-        get_transform(dag_path, egg_group);
-      }
-
-      MFnMesh mesh(dag_path, &status);
-      if (!status) {
-        mayaegg_cat.info()
-          << "Error in node " << dag_path.fullPathName().asChar() << ":\n"
-          << "  it appears to have a polygon mesh, but does not.\n";
-      } else {
-        make_polyset(dag_path, mesh, egg_group, egg_root);
-      }
+      make_polyset(dag_path, mesh, egg_group);
     }
 
   } else if (dag_path.hasFn(MFn::kLocator)) {
-    EggGroup *egg_group = get_egg_group(dag_path, egg_root);
-
-    if (egg_group == (EggGroup *)NULL) {
-      mayaegg_cat.error()
-        << "Cannot determine group node.\n";
-      return false;
-
-    } else {
-      if (mayaegg_cat.is_debug()) {
-        mayaegg_cat.debug()
-          << "Locator at " << dag_path.fullPathName().asChar() << "\n";
-      }
-
-      // Presumably, the locator's position has some meaning to the
-      // end-user, so we will implicitly tag it with the DCS flag so it
-      // won't get flattened out.
-      if (_animation_convert != AC_model) {
-        // For now, don't set the DCS flag on locators within
-        // character models, since egg-optchar doesn't understand
-        // this.  Perhaps there's no reason to ever change this, since
-        // locators within character models may not be meaningful.
-        egg_group->set_dcs_type(EggGroup::DC_net);
-        get_transform(dag_path, egg_group);
-      }
-      make_locator(dag_path, dag_node, egg_group, egg_root);
-    }
-
-  } else {
-    // Get the translation/rotation/scale data
-    EggGroup *egg_group = get_egg_group(dag_path, egg_root);
-
-    if (egg_group != (EggGroup *)NULL) {
-      if (_animation_convert != AC_model) {
-        get_transform(dag_path, egg_group);
-      }
-    }
-  }
-
-  return true;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaToEggConverter::process_chan_node
-//       Access: Private
-//  Description: Similar to process_model_node(), but this code path
-//               is followed only when we are building a table of
-//               animation data (AC_chan).  It just builds up the
-//               EggTable hierarchy according to the joint table.
-////////////////////////////////////////////////////////////////////
-bool MayaToEggConverter::
-process_chan_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
-  MStatus status;
-  MFnDagNode dag_node(dag_path, &status);
-  if (!status) {
-    status.perror("MFnDagNode constructor");
-    return false;
-  }
-
-  if (dag_path.hasFn(MFn::kJoint)) {
-    // A joint.
+    EggGroup *egg_group = _tree.get_egg_group(node_desc);
 
     if (mayaegg_cat.is_debug()) {
       mayaegg_cat.debug()
-        << dag_path.fullPathName().asChar() << ": " << dag_node.typeName();
-      
-      if (MAnimUtil::isAnimated(dag_path)) {
-        mayaegg_cat.debug(false)
-          << " (animated)";
-      }
-      
-      mayaegg_cat.debug(false) << "\n";
+        << "Locator at " << path << "\n";
+    }
+    
+    // Presumably, the locator's position has some meaning to the
+    // end-user, so we will implicitly tag it with the DCS flag so it
+    // won't get flattened out.
+    if (_animation_convert != AC_model) {
+      // For now, don't set the DCS flag on locators within
+      // character models, since egg-optchar doesn't understand
+      // this.  Perhaps there's no reason to ever change this, since
+      // locators within character models may not be meaningful.
+      egg_group->set_dcs_type(EggGroup::DC_net);
     }
+    get_transform(dag_path, egg_group);
+    make_locator(dag_path, dag_node, egg_group);
 
-    get_egg_table(dag_path, egg_root);
+  } else {
+    // Just a generic node.
+    EggGroup *egg_group = _tree.get_egg_group(node_desc);
+    get_transform(dag_path, egg_group);
   }
 
   return true;
@@ -802,6 +644,12 @@ process_chan_node(const MDagPath &dag_path, EggGroupNode *egg_root) {
 ////////////////////////////////////////////////////////////////////
 void MayaToEggConverter::
 get_transform(const MDagPath &dag_path, EggGroup *egg_group) {
+  if (_animation_convert == AC_model) {
+    // When we're getting an animated model, we only get transforms
+    // for joints.
+    return;
+  }
+
   MStatus status;
   MObject transformNode = dag_path.transform(&status);
   if (!status && status.statusCode() == MStatus::kInvalidParameter) {
@@ -959,7 +807,7 @@ get_joint_transform(const MDagPath &dag_path, EggGroup *egg_group) {
 ////////////////////////////////////////////////////////////////////
 void MayaToEggConverter::
 make_nurbs_surface(const MDagPath &dag_path, MFnNurbsSurface &surface,
-                   EggGroup *egg_group, EggGroupNode *egg_root) {
+                   EggGroup *egg_group) {
   MStatus status;
   string name = surface.name().asChar();
 
@@ -1015,7 +863,7 @@ make_nurbs_surface(const MDagPath &dag_path, MFnNurbsSurface &surface,
       status.perror("MFnMesh constructor");
       return;
     }
-    make_polyset(polyset_path, polyset_fn, egg_group, egg_root, shader);
+    make_polyset(polyset_path, polyset_fn, egg_group, shader);
 
     // Now remove the polyset we created.
     MFnDagNode parent_node(polyset_parent, &status);
@@ -1271,7 +1119,7 @@ make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name,
 ////////////////////////////////////////////////////////////////////
 void MayaToEggConverter::
 make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
-                 EggGroup *egg_group, EggGroupNode *) {
+                 EggGroup *egg_group) {
   MStatus status;
   string name = curve.name().asChar();
 
@@ -1360,8 +1208,7 @@ make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
 ////////////////////////////////////////////////////////////////////
 void MayaToEggConverter::
 make_polyset(const MDagPath &dag_path, const MFnMesh &mesh,
-             EggGroup *egg_group, EggGroupNode *egg_root,
-             MayaShader *default_shader) {
+             EggGroup *egg_group, MayaShader *default_shader) {
   MStatus status;
   string name = mesh.name().asChar();
 
@@ -1556,10 +1403,10 @@ make_polyset(const MDagPath &dag_path, const MFnMesh &mesh,
   MFloatArray weights;
   if (_animation_convert == AC_model) {
     got_weights = 
-      get_vertex_weights(dag_path, mesh, egg_root, joints, weights);
+      get_vertex_weights(dag_path, mesh, joints, weights);
   }
 
-  if (got_weights) {
+  if (got_weights && !joints.empty()) {
     int num_joints = joints.size();
     int num_weights = (int)weights.length();
     int num_verts = num_weights / num_joints;
@@ -1598,7 +1445,7 @@ make_polyset(const MDagPath &dag_path, const MFnMesh &mesh,
 ////////////////////////////////////////////////////////////////////
 void MayaToEggConverter::
 make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
-             EggGroup *egg_group, EggGroupNode *egg_root) {
+             EggGroup *egg_group) {
   MStatus status;
 
   unsigned int num_children = dag_node.childCount();
@@ -1650,7 +1497,6 @@ make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
 ////////////////////////////////////////////////////////////////////
 bool MayaToEggConverter::
 get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
-                   EggGroupNode *egg_root,
                    pvector<EggGroup *> &joints, MFloatArray &weights) {
   MStatus status;
   
@@ -1696,7 +1542,8 @@ get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
         joints.clear();
         for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
           MDagPath joint_dag_path = influence_objects[oi];
-          EggGroup *joint = get_egg_group(joint_dag_path, egg_root);
+          MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
+          EggGroup *joint = _tree.get_egg_group(joint_node_desc);
           joints.push_back(joint);
         }
 
@@ -1736,189 +1583,6 @@ get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
   return false;
 }
 
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaToEggConverter::get_egg_group
-//       Access: Private
-//  Description: Returns the EggGroup corresponding to the indicated
-//               fully-qualified Maya path name.  If there is not
-//               already an EggGroup corresponding to this Maya path,
-//               creates one and returns it.
-//
-//               In this way we generate a unique EggGroup for each
-//               Maya node we care about about, and also preserve the
-//               Maya hierarchy sensibly.
-////////////////////////////////////////////////////////////////////
-EggGroup *MayaToEggConverter::
-get_egg_group(const MDagPath &dag_path, EggGroupNode *egg_root) {
-  return r_get_egg_group(dag_path.fullPathName().asChar(), dag_path, egg_root);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaToEggConverter::r_get_egg_group
-//       Access: Private
-//  Description: The recursive implementation of get_egg_group().
-////////////////////////////////////////////////////////////////////
-EggGroup *MayaToEggConverter::
-r_get_egg_group(const string &name, const MDagPath &dag_path,
-                EggGroupNode *egg_root) {
-  // If we have already encountered this pathname, return the
-  // corresponding EggGroup immediately.
-  Groups::const_iterator gi = _groups.find(name);
-  if (gi != _groups.end()) {
-    return (*gi).second;
-  }
-
-  // Otherwise, we have to create it.  Do this recursively, so we
-  // create each node along the path.
-  EggGroup *egg_group;
-
-  if (name.empty()) {
-    // This is the top.
-    egg_group = (EggGroup *)NULL;
-
-  } else {
-    // Maya uses vertical bars to separate path components.  Remove
-    // everything from the rightmost bar on; this will give us the
-    // parent's path name.
-    size_t bar = name.rfind("|");
-    string parent_name, local_name;
-    if (bar != string::npos) {
-      parent_name = name.substr(0, bar);
-      local_name = name.substr(bar + 1);
-    } else {
-      local_name = name;
-    }
-
-    EggGroup *parent_egg_group = 
-      r_get_egg_group(parent_name, dag_path, egg_root);
-    egg_group = new EggGroup(local_name);
-
-    if (parent_egg_group != (EggGroup *)NULL) {
-      parent_egg_group->add_child(egg_group);
-    } else {
-      egg_root->add_child(egg_group);
-    }
-
-    // Check for an object type setting, from Oliver's plug-in.
-    MObject dag_object = dag_path.node();
-    string object_type;
-    if (get_enum_attribute(dag_object, "eggObjectTypes1", object_type)) {
-      egg_group->add_object_type(object_type);
-    }
-    if (get_enum_attribute(dag_object, "eggObjectTypes2", object_type)) {
-      egg_group->add_object_type(object_type);
-    }
-    if (get_enum_attribute(dag_object, "eggObjectTypes3", object_type)) {
-      egg_group->add_object_type(object_type);
-    }
-
-    // We treat the object type "billboard" as a special case: we
-    // apply this one right away and also flag the group as an
-    // instance.
-    if (egg_group->has_object_type("billboard")) {    
-      egg_group->remove_object_type("billboard");
-      egg_group->set_group_type(EggGroup::GT_instance);
-      egg_group->set_billboard_type(EggGroup::BT_axis);
-
-    } else if (egg_group->has_object_type("billboard-point")) {    
-      egg_group->remove_object_type("billboard-point");
-      egg_group->set_group_type(EggGroup::GT_instance);
-      egg_group->set_billboard_type(EggGroup::BT_point_camera_relative);
-    }
-
-    // We also treat the object type "dcs" and "model" as a special
-    // case, so we can test for these flags later.
-    if (egg_group->has_object_type("dcs")) {
-      egg_group->remove_object_type("dcs");
-      egg_group->set_dcs_type(EggGroup::DC_default);
-    }
-    if (egg_group->has_object_type("model")) {
-      egg_group->remove_object_type("model");
-      egg_group->set_model_flag(true);
-    }
-
-    // And "vertex-color" has meaning only to this converter.
-    if (egg_group->has_object_type("vertex-color")) {
-      egg_group->remove_object_type("vertex-color");
-      MayaEggGroupUserData *user_data = new MayaEggGroupUserData;
-      user_data->_vertex_color = true;
-      egg_group->set_user_data(user_data);
-    }
-  }
-
-  _groups.insert(Groups::value_type(name, egg_group));
-  return egg_group;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaToEggConverter::get_egg_table
-//       Access: Private
-//  Description: Returns the EggTable corresponding to the indicated
-//               fully-qualified Maya path name.  This is similar to
-//               get_egg_group(), but this variant is used only when
-//               we are building a channel file, which is just a
-//               hierarchical collection of animation tables.
-////////////////////////////////////////////////////////////////////
-MayaToEggConverter::JointAnim *MayaToEggConverter::
-get_egg_table(const MDagPath &dag_path, EggGroupNode *egg_root) {
-  string name = dag_path.fullPathName().asChar();
-
-  // If we have already encountered this pathname, return the
-  // corresponding EggTable immediately.
-  Tables::const_iterator ti = _tables.find(name);
-  if (ti != _tables.end()) {
-    return (*ti).second;
-  }
-
-  // Otherwise, we have to create it.
-  JointAnim *joint_anim;
-
-  if (name.empty()) {
-    // This is the top.
-    joint_anim = (JointAnim *)NULL;
-
-  } else {
-    // Maya uses vertical bars to separate path components.  Remove
-    // everything from the rightmost bar on; this will give us the
-    // parent's path name.
-    size_t bar = name.rfind("|");
-    string parent_name, local_name;
-    if (bar != string::npos) {
-      parent_name = name.substr(0, bar);
-      local_name = name.substr(bar + 1);
-    } else {
-      local_name = name;
-    }
-
-    // Look only one level up for the parent.  If it hasn't been
-    // defined yet, don't define it; instead, just start from the
-    // root.
-    JointAnim *parent_joint_anim = NULL;
-    if (!parent_name.empty()) {
-      ti = _tables.find(parent_name);
-      if (ti != _tables.end()) {
-        parent_joint_anim = (*ti).second;
-      }
-    }
-
-    joint_anim = new JointAnim;
-    joint_anim->_dag_path = dag_path;
-    joint_anim->_table = new EggTable(local_name);
-    joint_anim->_anim = new EggXfmSAnim("xform", _egg_data->get_coordinate_system());
-    joint_anim->_table->add_child(joint_anim->_anim);
-
-    if (parent_joint_anim != (JointAnim *)NULL) {
-      parent_joint_anim->_table->add_child(joint_anim->_table);
-    } else {
-      egg_root->add_child(joint_anim->_table);
-    }
-  }
-
-  _tables.insert(Tables::value_type(name, joint_anim));
-  return joint_anim;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: MayaShader::set_shader_attributes
 //       Access: Private

+ 10 - 25
pandatool/src/mayaegg/mayaToEggConverter.h

@@ -21,6 +21,7 @@
 
 #include "pandatoolbase.h"
 #include "somethingToEggConverter.h"
+#include "mayaNodeTree.h"
 
 #include "mayaApi.h"
 #include "mayaShaders.h"
@@ -79,12 +80,13 @@ public:
 private:
   bool convert_flip(double start_frame, double end_frame, 
                     double frame_inc, double output_frame_rate);
+
   bool convert_char_model();
   bool convert_char_chan(double start_frame, double end_frame, 
                          double frame_inc, double output_frame_rate);
   bool convert_hierarchy(EggGroupNode *egg_root);
-  bool process_model_node(const MDagPath &dag_path, EggGroupNode *egg_root);
-  bool process_chan_node(const MDagPath &dag_path, EggGroupNode *egg_root);
+  bool process_model_node(MayaNodeDesc *node_desc);
+
   void get_transform(const MDagPath &dag_path, EggGroup *egg_group);
   void get_joint_transform(const MDagPath &dag_path, EggGroup *egg_group);
 
@@ -93,35 +95,22 @@ private:
   // reference.
   void make_nurbs_surface(const MDagPath &dag_path, 
                           MFnNurbsSurface &surface,
-                          EggGroup *group, EggGroupNode *egg_root);
+                          EggGroup *group);
   EggNurbsCurve *make_trim_curve(const MFnNurbsCurve &curve,
                                  const string &nurbs_name,
                                  EggGroupNode *egg_group,
                                  int trim_curve_index);
   void make_nurbs_curve(const MDagPath &dag_path, 
                         const MFnNurbsCurve &curve,
-                        EggGroup *group, EggGroupNode *egg_root);
+                        EggGroup *group);
   void make_polyset(const MDagPath &dag_path,
                     const MFnMesh &mesh,
-                    EggGroup *egg_group, EggGroupNode *egg_root,
+                    EggGroup *egg_group,
                     MayaShader *default_shader = NULL);
   void make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
-                    EggGroup *egg_group, EggGroupNode *egg_root);
+                    EggGroup *egg_group);
   bool get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
-                          EggGroupNode *egg_root,
                           pvector<EggGroup *> &joints, MFloatArray &weights);
-  class JointAnim {
-  public:
-    MDagPath _dag_path;
-    EggTable *_table;
-    EggXfmSAnim *_anim;
-  };
-
-  EggGroup *get_egg_group(const MDagPath &dag_path, EggGroupNode *egg_root);
-  EggGroup *r_get_egg_group(const string &name, const MDagPath &dag_path,
-                            EggGroupNode *egg_root);
-  JointAnim *get_egg_table(const MDagPath &dag_path, EggGroupNode *egg_root);
-  JointAnim *get_egg_table(const string &name, EggGroupNode *egg_root);
   void set_shader_attributes(EggPrimitive &primitive,
                              const MayaShader &shader);
   void apply_texture_properties(EggTexture &tex, 
@@ -131,15 +120,11 @@ private:
 
   bool reparent_decals(EggGroupNode *egg_parent);
 
-  typedef pmap<string, EggGroup *> Groups;
-  Groups _groups;
-
-  typedef pmap<string, JointAnim *> Tables;
-  Tables _tables;
-
   string _program_name;
   bool _from_selection;
 
+  MayaNodeTree _tree;
+
 public:
   MayaShaders _shaders;
   EggTextureCollection _textures;