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