Panda3D
 All Classes Functions Variables Enumerations
mayaNodeDesc.cxx
1 // Filename: mayaNodeDesc.cxx
2 // Created by: drose (06Jun03)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "mayaNodeDesc.h"
16 #include "mayaNodeTree.h"
17 #include "mayaBlendDesc.h"
18 #include "mayaToEggConverter.h"
19 #include "maya_funcs.h"
20 #include "eggGroup.h"
21 #include "config_mayaegg.h"
22 
23 #include "pre_maya_include.h"
24 #include <maya/MFnBlendShapeDeformer.h>
25 #include <maya/MItDependencyGraph.h>
26 #include <maya/MFnNurbsSurface.h>
27 #include <maya/MFnMesh.h>
28 #include "post_maya_include.h"
29 
30 TypeHandle MayaNodeDesc::_type_handle;
31 
32 // This is a list of the names of Maya connections that count as a
33 // transform.
34 static const char *transform_connections[] = {
35  "translate",
36  "translateX",
37  "translateY",
38  "translateZ",
39  "rotate",
40  "rotateX",
41  "rotateY",
42  "rotateZ",
43 };
44 static const int num_transform_connections = sizeof(transform_connections) / sizeof(const char *);
45 
46 ////////////////////////////////////////////////////////////////////
47 // Function: MayaNodeDesc::Constructor
48 // Access: Public
49 // Description:
50 ////////////////////////////////////////////////////////////////////
51 MayaNodeDesc::
52 MayaNodeDesc(MayaNodeTree *tree, MayaNodeDesc *parent, const string &name) :
53  Namable(name),
54  _tree(tree),
55  _parent(parent)
56 {
57  _dag_path = (MDagPath *)NULL;
58  _egg_group = (EggGroup *)NULL;
59  _egg_table = (EggTable *)NULL;
60  _anim = (EggXfmSAnim *)NULL;
61  _joint_type = JT_none;
62  _is_lod = false;
63  _tagged = false;
64  _joint_tagged = false;
65 
66  // Add ourselves to our parent.
67  if (_parent != (MayaNodeDesc *)NULL) {
68  _parent->_children.push_back(this);
69  }
70 }
71 
72 ////////////////////////////////////////////////////////////////////
73 // Function: MayaNodeDesc::Destructor
74 // Access: Public
75 // Description:
76 ////////////////////////////////////////////////////////////////////
77 MayaNodeDesc::
78 ~MayaNodeDesc() {
79  if (_dag_path != (MDagPath *)NULL) {
80  delete _dag_path;
81  }
82 }
83 
84 ////////////////////////////////////////////////////////////////////
85 // Function: MayaNodeDesc::from_dag_path
86 // Access: Public
87 // Description: Indicates an association between the MayaNodeDesc and
88 // some Maya instance.
89 ////////////////////////////////////////////////////////////////////
90 void MayaNodeDesc::
91 from_dag_path(const MDagPath &dag_path, MayaToEggConverter *converter) {
92  MStatus status;
93 
94  if (_dag_path == (MDagPath *)NULL) {
95  _dag_path = new MDagPath(dag_path);
96 
97  string name;
98  MFnDagNode dag_node(dag_path, &status);
99  if (!status) {
100  status.perror("MFnDagNode constructor");
101  } else {
102  name = dag_node.name().asChar();
103  }
104 
105  if (_dag_path->hasFn(MFn::kJoint) || converter->force_joint(name)) {
106  // This node is a joint, or the user specifically asked to treat
107  // it like a joint.
108  _joint_type = JT_joint;
109  if (_parent != (MayaNodeDesc *)NULL) {
110  _parent->mark_joint_parent();
111  }
112 
113  } else {
114  // The node is not a joint, but maybe its transform is
115  // controlled by connected inputs. If so, we should treat it
116  // like a joint.
117  bool transform_connected = false;
118 
119  MStatus status;
120  MObject node = dag_path.node(&status);
121  if (status) {
122  for (int i = 0;
123  i < num_transform_connections && !transform_connected;
124  i++) {
125  if (is_connected(node, transform_connections[i])) {
126  transform_connected = true;
127  }
128  }
129  }
130 
131  if (transform_connected) {
132  _joint_type = JT_joint;
133  if (_parent != (MayaNodeDesc *)NULL) {
134  _parent->mark_joint_parent();
135  }
136  }
137  }
138 
139  if (dag_path.hasFn(MFn::kNurbsSurface)) {
140  MFnNurbsSurface surface(dag_path, &status);
141  if (status) {
142  check_blend_shapes(surface, "create");
143  }
144  } else if (dag_path.hasFn(MFn::kMesh)) {
145  MFnMesh mesh(dag_path, &status);
146  if (status) {
147  check_blend_shapes(mesh, "inMesh");
148  }
149  }
150  }
151 }
152 
153 ////////////////////////////////////////////////////////////////////
154 // Function: MayaNodeDesc::has_dag_path
155 // Access: Public
156 // Description: Returns true if a Maya dag path has been associated
157 // with this node, false otherwise.
158 ////////////////////////////////////////////////////////////////////
159 bool MayaNodeDesc::
160 has_dag_path() const {
161  return (_dag_path != (MDagPath *)NULL);
162 }
163 
164 ////////////////////////////////////////////////////////////////////
165 // Function: MayaNodeDesc::get_dag_path
166 // Access: Public
167 // Description: Returns the dag path associated with this node. It
168 // is an error to call this unless has_dag_path()
169 // returned true.
170 ////////////////////////////////////////////////////////////////////
171 const MDagPath &MayaNodeDesc::
172 get_dag_path() const {
173  nassertr(_dag_path != (MDagPath *)NULL, *_dag_path);
174  return *_dag_path;
175 }
176 
177 ////////////////////////////////////////////////////////////////////
178 // Function: MayaNodeDesc::get_num_blend_descs
179 // Access: Public
180 // Description: Returns the number of unique MayaBlendDesc objects
181 // (and hence the number of morph sliders) that affect
182 // the geometry in this node.
183 ////////////////////////////////////////////////////////////////////
184 int MayaNodeDesc::
186  return _blend_descs.size();
187 }
188 
189 ////////////////////////////////////////////////////////////////////
190 // Function: MayaNodeDesc::get_blend_desc
191 // Access: Public
192 // Description: Returns the nth MayaBlendDesc object that affects the
193 // geometry in this node.
194 ////////////////////////////////////////////////////////////////////
196 get_blend_desc(int n) const {
197  nassertr(n >= 0 && n < (int)_blend_descs.size(), NULL);
198  return _blend_descs[n];
199 }
200 
201 ////////////////////////////////////////////////////////////////////
202 // Function: MayaNodeDesc::is_joint
203 // Access: Public
204 // Description: Returns true if the node should be treated as a joint
205 // by the converter.
206 ////////////////////////////////////////////////////////////////////
207 bool MayaNodeDesc::
208 is_joint() const {
209  //return _joint_type == JT_joint || _joint_type == JT_pseudo_joint;
210  return _joint_tagged && (_joint_type == JT_joint || _joint_type == JT_pseudo_joint);
211 }
212 
213 ////////////////////////////////////////////////////////////////////
214 // Function: MayaNodeDesc::is_joint_parent
215 // Access: Public
216 // Description: Returns true if the node is the parent or ancestor of
217 // a joint.
218 ////////////////////////////////////////////////////////////////////
219 bool MayaNodeDesc::
221  return _joint_type == JT_joint_parent;
222  //return _joint_tagged && (_joint_type == JT_joint_parent);
223 }
224 
225 ////////////////////////////////////////////////////////////////////
226 // Function: MayaNodeDesc::is_joint_tagged
227 // Access: Public
228 // Description: Returns true if the node has been joint_tagged to be
229 // converted, false otherwise.
230 ////////////////////////////////////////////////////////////////////
231 bool MayaNodeDesc::
233  return _joint_tagged;
234 }
235 
236 ////////////////////////////////////////////////////////////////////
237 // Function: MayaNodeDesc::tag_joint
238 // Access: Private
239 // Description: Tags this node for conversion, but does not tag child
240 // nodes.
241 ////////////////////////////////////////////////////////////////////
242 void MayaNodeDesc::
243 tag_joint() {
244  _joint_tagged = true;
245 }
246 
247 ////////////////////////////////////////////////////////////////////
248 // Function: MayaNodeDesc::tag_joint_recursively
249 // Access: Private
250 // Description: Tags this node and all descendant nodes for
251 // conversion.
252 ////////////////////////////////////////////////////////////////////
253 void MayaNodeDesc::
254 tag_joint_recursively() {
255  _joint_tagged = true;
256  //mayaegg_cat.info() << "tjr: " << get_name() << endl;
257  Children::const_iterator ci;
258  for (ci = _children.begin(); ci != _children.end(); ++ci) {
259  MayaNodeDesc *child = (*ci);
260  child->tag_joint_recursively();
261  }
262 }
263 
264 ////////////////////////////////////////////////////////////////////
265 // Function: MayaNodeDesc::is_tagged
266 // Access: Public
267 // Description: Returns true if the node has been tagged to be
268 // converted, false otherwise.
269 ////////////////////////////////////////////////////////////////////
270 bool MayaNodeDesc::
271 is_tagged() const {
272  return _tagged;
273 }
274 
275 ////////////////////////////////////////////////////////////////////
276 // Function: MayaNodeDesc::tag
277 // Access: Private
278 // Description: Tags this node for conversion, but does not tag child
279 // nodes.
280 ////////////////////////////////////////////////////////////////////
281 void MayaNodeDesc::
282 tag() {
283  _tagged = true;
284 }
285 
286 ////////////////////////////////////////////////////////////////////
287 // Function: MayaNodeDesc::untag
288 // Access: Private
289 // Description: Un-tags this node for conversion, but does not tag child
290 // nodes.
291 ////////////////////////////////////////////////////////////////////
292 void MayaNodeDesc::
293 untag() {
294  _tagged = false;
295 }
296 
297 ////////////////////////////////////////////////////////////////////
298 // Function: MayaNodeDesc::tag_recursively
299 // Access: Private
300 // Description: Tags this node and all descendant nodes for
301 // conversion.
302 ////////////////////////////////////////////////////////////////////
303 void MayaNodeDesc::
304 tag_recursively() {
305  _tagged = true;
306 
307  Children::const_iterator ci;
308  for (ci = _children.begin(); ci != _children.end(); ++ci) {
309  MayaNodeDesc *child = (*ci);
310  child->tag_recursively();
311  }
312 }
313 
314 ////////////////////////////////////////////////////////////////////
315 // Function: MayaNodeDesc::untag_recursively
316 // Access: Private
317 // Description: Un-tags this node and all descendant nodes for
318 // conversion.
319 ////////////////////////////////////////////////////////////////////
320 void MayaNodeDesc::
321 untag_recursively() {
322  _tagged = false;
323 
324  Children::const_iterator ci;
325  for (ci = _children.begin(); ci != _children.end(); ++ci) {
326  MayaNodeDesc *child = (*ci);
327  child->untag_recursively();
328  }
329 }
330 
331 ////////////////////////////////////////////////////////////////////
332 // Function: MayaNodeDesc::has_object_type
333 // Access: Public
334 // Description: Returns true if this node or any of its parent
335 // has_object_type of object_type.
336 ////////////////////////////////////////////////////////////////////
337 bool MayaNodeDesc::
338 has_object_type(string object_type) const {
339  bool ret = false;
340  if ((_egg_group != (EggGroup*) NULL)
341  && _egg_group->has_object_type(object_type)) {
342  return true;
343  }
344  if (_parent != (MayaNodeDesc *)NULL) {
345  ret |= _parent->has_object_type(object_type);
346  }
347  return ret;
348 }
349 
350 ////////////////////////////////////////////////////////////////////
351 // Function: MayaNodeDesc::clear_egg
352 // Access: Private
353 // Description: Recursively clears the egg pointers from this node
354 // and all children.
355 ////////////////////////////////////////////////////////////////////
356 void MayaNodeDesc::
357 clear_egg() {
358  _egg_group = (EggGroup *)NULL;
359  _egg_table = (EggTable *)NULL;
360  _anim = (EggXfmSAnim *)NULL;
361 
362  Children::const_iterator ci;
363  for (ci = _children.begin(); ci != _children.end(); ++ci) {
364  MayaNodeDesc *child = (*ci);
365  child->clear_egg();
366  }
367 }
368 
369 ////////////////////////////////////////////////////////////////////
370 // Function: MayaNodeDesc::mark_joint_parent
371 // Access: Private
372 // Description: Indicates that this node has at least one child that
373 // is a joint or a pseudo-joint.
374 ////////////////////////////////////////////////////////////////////
375 void MayaNodeDesc::
376 mark_joint_parent() {
377  if (_joint_type == JT_none) {
378  _joint_type = JT_joint_parent;
379  if (_parent != (MayaNodeDesc *)NULL) {
380  _parent->mark_joint_parent();
381  }
382  }
383 }
384 
385 ////////////////////////////////////////////////////////////////////
386 // Function: MayaNodeDesc::check_pseudo_joints
387 // Access: Private
388 // Description: Walks the hierarchy, looking for non-joint nodes that
389 // are both children and parents of a joint. These
390 // nodes are deemed to be pseudo joints, since the
391 // converter must treat them as joints.
392 ////////////////////////////////////////////////////////////////////
393 void MayaNodeDesc::
394 check_pseudo_joints(bool joint_above) {
395  static PN_uint32 space_count = 0;
396  string space;
397  for (PN_uint32 idx=0; idx<space_count; ++idx) {
398  space.append(" ");
399  }
400  if (mayaegg_cat.is_spam()) {
401  mayaegg_cat.spam() << "cpj:" << space << get_name() << " joint_type: " << _joint_type << endl;
402  }
403  if (_joint_type == JT_joint_parent && joint_above) {
404  // This is one such node: it is the parent of a joint
405  // (JT_joint_parent is set), and it is the child of a joint
406  // (joint_above is set).
407  _joint_type = JT_pseudo_joint;
408  }
409 
410  if (_joint_type == JT_joint) {
411  // If this node is itself a joint, then joint_above is true for
412  // all child nodes.
413  joint_above = true;
414  }
415 
416  // Don't bother traversing further if _joint_type is none, since
417  // that means this node has no joint children.
418  if (_joint_type != JT_none) {
419 
420  bool any_joints = false;
421  Children::const_iterator ci;
422  for (ci = _children.begin(); ci != _children.end(); ++ci) {
423  MayaNodeDesc *child = (*ci);
424  if (mayaegg_cat.is_spam()) {
425  ++space_count;
426  }
427  child->check_pseudo_joints(joint_above);
428  //if (child->is_joint()) {
429  if (child->_joint_type == JT_joint || child->_joint_type == JT_pseudo_joint) {
430  any_joints = true;
431  }
432  }
433 
434  // If any children qualify as joints, then any sibling nodes that
435  // are parents of joints are also elevated to joints.
436  if (any_joints) {
437  bool all_joints = true;
438  for (ci = _children.begin(); ci != _children.end(); ++ci) {
439  MayaNodeDesc *child = (*ci);
440  MStatus status;
441  MFnDagNode dag_node(child->get_dag_path(), &status);
442  if (!status) {
443  status.perror("MFnDagNode constructor");
444  }
445  string type_name = dag_node.typeName().asChar();
446  if (child->_joint_type == JT_joint_parent) {
447  child->_joint_type = JT_pseudo_joint;
448  } else if (child->_joint_type == JT_none) {
449  if (mayaegg_cat.is_spam()) {
450  mayaegg_cat.spam() << "cpj: " << space << "jt_none for " << child->get_name() << endl;
451  }
452  if (type_name.find("transform") == string::npos) {
453  if (mayaegg_cat.is_spam()) {
454  mayaegg_cat.spam() << "cpj: " << space << "all_joints false for " << get_name() << endl;
455  }
456  all_joints = false;
457  }
458  }
459  }
460 
461  if (all_joints) {
462  // Finally, if all children are joints, then we are too.
463  if (_joint_type == JT_joint_parent) {
464  if (!get_name().empty()) { // make sure parent of root is not a joint
465  _joint_type = JT_pseudo_joint;
466  }
467  }
468  }
469  }
470  }
471  if (mayaegg_cat.is_spam()) {
472  if (space_count > 0)
473  --space_count;
474  }
475 }
476 
477 ////////////////////////////////////////////////////////////////////
478 // Function: MayaNodeDesc::check_blend_shapes
479 // Access: Private
480 // Description: Looks for blend shapes on a NURBS surface or polygon
481 // mesh and records any blend shapes found. This is
482 // similar to MayaToEggConverter::get_vertex_weights(),
483 // which checks for membership of vertices to joints;
484 // Maya stores the blend shape table in the same place.
485 // See the comments in get_vertex_weights() for a more
486 // in-depth description of the iteration process here.
487 ////////////////////////////////////////////////////////////////////
488 void MayaNodeDesc::
489 check_blend_shapes(const MFnDagNode &node, const string &attrib_name) {
490  MStatus status;
491 
492  MObject attr = node.attribute(attrib_name.c_str());
493 
494  MPlug history(node.object(), attr);
495  MItDependencyGraph it(history, MFn::kDependencyNode,
496  MItDependencyGraph::kUpstream,
497  MItDependencyGraph::kDepthFirst,
498  MItDependencyGraph::kNodeLevel);
499 
500  while (!it.isDone()) {
501  MObject c_node = it.thisNode();
502 
503  if (c_node.hasFn(MFn::kBlendShape)) {
504  MFnBlendShapeDeformer blends(c_node, &status);
505  if (!status) {
506  status.perror("MFnBlendShapeDeformer constructor");
507 
508  } else {
509  // Check if the slider is a "parallel blender", which is a
510  // construct created by Maya for Maya's internal purposes
511  // only. We don't want to fiddle with the parallel blenders.
512  MPlug plug = blends.findPlug("pb");
513  bool is_parallel_blender;
514  status = plug.getValue(is_parallel_blender);
515  if (!status) {
516  status.perror("Could not get value of pb plug.");
517  is_parallel_blender = false;
518  }
519 
520  if (is_parallel_blender ||
521  _tree->ignore_slider(blends.name().asChar())) {
522  _tree->report_ignored_slider(blends.name().asChar());
523 
524  } else {
525  MObjectArray base_objects;
526  status = blends.getBaseObjects(base_objects);
527  if (!status) {
528  status.perror("MFnBlendShapeDeformer::getBaseObjects");
529  } else {
530  for (unsigned int oi = 0; oi < base_objects.length(); oi++) {
531  MObject base_object = base_objects[oi];
532 
533  MIntArray index_list;
534  status = blends.weightIndexList(index_list);
535  if (!status) {
536  status.perror("MFnBlendShapeDeformer::weightIndexList");
537  } else {
538  for (unsigned int i = 0; i < index_list.length(); i++) {
539  int wi = index_list[i];
540  PT(MayaBlendDesc) blend_desc = new MayaBlendDesc(blends, wi);
541  blend_desc = _tree->add_blend_desc(blend_desc);
542  _blend_descs.push_back(blend_desc);
543  }
544  }
545  }
546  }
547  }
548  }
549  }
550 
551  it.next();
552  }
553 }
554 
555 ////////////////////////////////////////////////////////////////////
556 // Function: MayaNodeDesc::check_lods
557 // Access: Private
558 // Description: Walks through the hierarchy again and checks for LOD
559 // specifications. Any such specifications found are
560 // recorded on the child nodes of the lodGroups
561 // themselves: the nodes that actually switch in and
562 // out. (This is the way they are recorded in an egg
563 // file.)
564 ////////////////////////////////////////////////////////////////////
565 void MayaNodeDesc::
566 check_lods() {
567  // Walk through the children first. This makes it easier in the
568  // below (we only have to return in the event of an error).
569  Children::iterator ci;
570  for (ci = _children.begin(); ci != _children.end(); ++ci) {
571  MayaNodeDesc *child = (*ci);
572  child->check_lods();
573  }
574 
575  // Now consider whether this node is an lodGroup.
576  if (_dag_path != (MDagPath *)NULL &&
577  _dag_path->hasFn(MFn::kLodGroup)) {
578  // This node is a parent lodGroup; its children, therefore, are
579  // LOD's.
580  MStatus status;
581  MFnDagNode dag_node(*_dag_path, &status);
582  if (!status) {
583  status.perror("Couldn't get node from dag path for lodGroup");
584  return;
585  }
586 
587  MPlug plug = dag_node.findPlug("threshold", &status);
588  if (!status) {
589  status.perror("Couldn't get threshold attributes on lodGroup");
590  return;
591  }
592 
593  // There ought to be the one fewer elements in the array than
594  // there are children of the node.
595  unsigned int num_elements = plug.numElements();
596  unsigned int num_children = _children.size();
597  if (num_elements + 1 != num_children) {
598  mayaegg_cat.warning()
599  << "Node " << get_name() << " has " << num_elements
600  << " LOD entries, but " << num_children << " children.\n";
601  }
602 
603  // Should we also consider cameraMatrix, to transform the LOD's
604  // origin? It's not clear precisely what this transform matrix
605  // means in Maya, so we'll wait until we have a sample file that
606  // demonstrates its use.
607 
608  double switch_out = 0.0;
609  unsigned int i = 0;
610  while (i < num_elements && i < num_children) {
611  MPlug element = plug.elementByLogicalIndex(i);
612  MayaNodeDesc *child = _children[i];
613 
614  double switch_in;
615  status = element.getValue(switch_in);
616  if (!status) {
617  status.perror("Couldn't get double value from threshold.");
618  return;
619  }
620 
621  child->_is_lod = true;
622  child->_switch_in = switch_in;
623  child->_switch_out = switch_out;
624 
625  switch_out = switch_in;
626  ++i;
627  }
628 
629  while (i < num_children) {
630  // Also set the last child(ren). Maya wants this to switch in
631  // at infinity, but Panda doesn't have such a concept; we'll
632  // settle for four times the switch_out distance.
633  MayaNodeDesc *child = _children[i];
634  child->_is_lod = true;
635  child->_switch_in = switch_out * 4.0;
636  child->_switch_out = switch_out;
637 
638  ++i;
639  }
640  }
641 }
A handle to a Maya blend shape description.
Definition: mayaBlendDesc.h:44
bool has_dag_path() const
Returns true if a Maya dag path has been associated with this node, false otherwise.
void from_dag_path(const MDagPath &dag_path, MayaToEggConverter *converter)
Indicates an association between the MayaNodeDesc and some Maya instance.
MayaBlendDesc * get_blend_desc(int n) const
Returns the nth MayaBlendDesc object that affects the geometry in this node.
bool is_joint_tagged() const
Returns true if the node has been joint_tagged to be converted, false otherwise.
bool has_object_type(string object_type) const
Returns true if this node or any of its parent has_object_type of object_type.
Describes a complete tree of maya nodes for conversion.
Definition: mayaNodeTree.h:40
bool is_tagged() const
Returns true if the node has been tagged to be converted, false otherwise.
bool force_joint(const string &name) const
Returns true if the indicated name is on the list of DAG nodes to treat as a joint, false otherwise.
The main glue of the egg hierarchy, this corresponds to the &lt;Group&gt;, &lt;Instance&gt;, and &lt;Joint&gt; type nod...
Definition: eggGroup.h:36
A base class for all things which can have a name.
Definition: namable.h:29
Describes a single instance of a node in the Maya scene graph, relating it to the corresponding egg s...
Definition: mayaNodeDesc.h:43
This corresponds to an &lt;Xfm$Anim_S$&gt; entry, which is a collection of up to nine &lt;S$Anim&gt; entries that...
Definition: eggXfmSAnim.h:33
bool ignore_slider(const string &name) const
Returns true if the indicated name is on the list of sliders to ignore, false otherwise.
bool has_object_type(const string &object_type) const
Returns true if the indicated object type has been added to the group, or false otherwise.
Definition: eggGroup.cxx:157
This class supervises the construction of an EggData structure from a single Maya file...
bool is_joint_parent() const
Returns true if the node is the parent or ancestor of a joint.
This corresponds to a.
Definition: eggTable.h:31
const MDagPath & get_dag_path() const
Returns the dag path associated with this node.
bool is_joint() const
Returns true if the node should be treated as a joint by the converter.
void report_ignored_slider(const string &name)
Outputs a message to the user reporting that a slider was ignored.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
int get_num_blend_descs() const
Returns the number of unique MayaBlendDesc objects (and hence the number of morph sliders) that affec...