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 }
MayaNodeDesc::from_dag_path
void from_dag_path(const MDagPath &dag_path, MayaToEggConverter *converter)
Indicates an association between the MayaNodeDesc and some Maya instance.
Definition: mayaNodeDesc.cxx:84
config_mayaegg.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeTree::report_ignored_slider
void report_ignored_slider(const std::string &name)
Outputs a message to the user reporting that a slider was ignored.
Definition: mayaNodeTree.cxx:520
MayaNodeDesc::get_blend_desc
MayaBlendDesc * get_blend_desc(int n) const
Returns the nth MayaBlendDesc object that affects the geometry in this node.
Definition: mayaNodeDesc.cxx:178
MayaNodeDesc::has_object_type
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.
Definition: mayaNodeDesc.cxx:289
post_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeDesc::is_joint_tagged
bool is_joint_tagged() const
Returns true if the node has been joint_tagged to be converted, false otherwise.
Definition: mayaNodeDesc.cxx:206
MayaNodeDesc::is_joint
bool is_joint() const
Returns true if the node should be treated as a joint by the converter.
Definition: mayaNodeDesc.cxx:187
MayaNodeTree::add_blend_desc
MayaBlendDesc * add_blend_desc(MayaBlendDesc *blend_desc)
Adds the indicated MayaBlendDesc object to the list of blends collected so far.
Definition: mayaNodeTree.cxx:538
MayaNodeDesc::has_dag_path
bool has_dag_path() const
Returns true if a Maya dag path has been associated with this node, false otherwise.
Definition: mayaNodeDesc.cxx:150
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
mayaNodeTree.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeTree
Describes a complete tree of maya nodes for conversion.
Definition: mayaNodeTree.h:37
MayaToEggConverter
This class supervises the construction of an EggData structure from a single Maya file,...
Definition: mayaToEggConverter.h:52
MayaNodeDesc::get_num_blend_descs
int get_num_blend_descs() const
Returns the number of unique MayaBlendDesc objects (and hence the number of morph sliders) that affec...
Definition: mayaNodeDesc.cxx:169
pre_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeDesc::is_tagged
bool is_tagged() const
Returns true if the node has been tagged to be converted, false otherwise.
Definition: mayaNodeDesc.cxx:236
maya_funcs.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaBlendDesc
A handle to a Maya blend shape description.
Definition: mayaBlendDesc.h:40
is_connected
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
EggGroup::has_object_type
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
MayaNodeTree::ignore_slider
bool ignore_slider(const std::string &name) const
Returns true if the indicated name is on the list of sliders to ignore, false otherwise.
Definition: mayaNodeTree.cxx:511
mayaBlendDesc.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaToEggConverter::force_joint
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,...
Definition: mayaToEggConverter.cxx:331
Namable
A base class for all things which can have a name.
Definition: namable.h:26
mayaToEggConverter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeDesc::get_dag_path
const MDagPath & get_dag_path() const
Returns the dag path associated with this node.
Definition: mayaNodeDesc.cxx:159
MayaNodeDesc
Describes a single instance of a node in the Maya scene graph, relating it to the corresponding egg s...
Definition: mayaNodeDesc.h:40
MayaNodeDesc::is_joint_parent
bool is_joint_parent() const
Returns true if the node is the parent or ancestor of a joint.
Definition: mayaNodeDesc.cxx:196
mayaNodeDesc.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggGroup.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.