Panda3D
Loading...
Searching...
No Matches
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
29using std::string;
30
31TypeHandle MayaNodeDesc::_type_handle;
32
33// This is a list of the names of Maya connections that count as a transform.
34static const char *transform_connections[] = {
35 "translate",
36 "translateX",
37 "translateY",
38 "translateZ",
39 "rotate",
40 "rotateX",
41 "rotateY",
42 "rotateZ",
43};
44static const int num_transform_connections = sizeof(transform_connections) / sizeof(const char *);
45
46/**
47 *
48 */
49MayaNodeDesc::
50MayaNodeDesc(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 */
73MayaNodeDesc::
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 */
84from_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 */
150has_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 */
158const MDagPath &MayaNodeDesc::
159get_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 */
169get_num_blend_descs() const {
170 return _blend_descs.size();
171}
172
173/**
174 * Returns the nth MayaBlendDesc object that affects the geometry in this
175 * node.
176 */
178get_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 */
187is_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 */
196is_joint_parent() const {
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 */
206is_joint_tagged() const {
207 return _joint_tagged;
208}
209
210/**
211 * Tags this node for conversion, but does not tag child nodes.
212 */
213void MayaNodeDesc::
214tag_joint() {
215 _joint_tagged = true;
216}
217
218/**
219 * Tags this node and all descendant nodes for conversion.
220 */
221void MayaNodeDesc::
222tag_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 */
236is_tagged() const {
237 return _tagged;
238}
239
240/**
241 * Tags this node for conversion, but does not tag child nodes.
242 */
243void MayaNodeDesc::
244tag() {
245 _tagged = true;
246}
247
248/**
249 * Un-tags this node for conversion, but does not tag child nodes.
250 */
251void MayaNodeDesc::
252untag() {
253 _tagged = false;
254}
255
256/**
257 * Tags this node and all descendant nodes for conversion.
258 */
259void MayaNodeDesc::
260tag_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 */
273void MayaNodeDesc::
274untag_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 */
289has_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 */
304void MayaNodeDesc::
305clear_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 */
321void MayaNodeDesc::
322mark_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 */
336void MayaNodeDesc::
337check_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 */
427void MayaNodeDesc::
428check_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 */
500void MayaNodeDesc::
501check_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}
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
A handle to a Maya blend shape description.
Describes a single instance of a node in the Maya scene graph, relating it to the corresponding egg s...
bool is_joint_tagged() const
Returns true if the node has been joint_tagged to be converted, false otherwise.
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.
const MDagPath & get_dag_path() const
Returns the dag path associated with this node.
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() const
Returns true if the node should be treated as a joint by the converter.
int get_num_blend_descs() const
Returns the number of unique MayaBlendDesc objects (and hence the number of morph sliders) that affec...
bool has_dag_path() const
Returns true if a Maya dag path has been associated with this node, false otherwise.
bool is_joint_parent() const
Returns true if the node is the parent or ancestor of a joint.
bool is_tagged() const
Returns true if the node has been tagged to be converted, false otherwise.
Describes a complete tree of maya nodes for conversion.
bool ignore_slider(const std::string &name) const
Returns true if the indicated name is on the list of sliders to ignore, false otherwise.
void report_ignored_slider(const std::string &name)
Outputs a message to the user reporting that a slider was ignored.
MayaBlendDesc * add_blend_desc(MayaBlendDesc *blend_desc)
Adds the indicated MayaBlendDesc object to the list of blends collected so far.
This class supervises the construction of an EggData structure from a single Maya file,...
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,...
A base class for all things which can have a name.
Definition namable.h:26
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_connected(MObject &node, const string &attribute_name)
Returns true if the named connection exists on the node and is connected to anything,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.