Panda3D
maxNodeTree.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 maxNodeTree.cxx
10  * @author crevilla
11  * from mayaNodeTree.cxx created by: drose (06Jun03)
12  */
13 
14 #include "maxEgg.h"
15 
16 /**
17  *
18  */
19 MaxNodeTree::
20 MaxNodeTree() {
21  _root = new MaxNodeDesc;
22  _fps = 0.0;
23  _export_mesh = false;
24  _egg_data = nullptr;
25  _egg_root = nullptr;
26  _skeleton_node = nullptr;
27 }
28 
29 /**
30  * Returns a pointer to the node corresponding to the indicated INode object,
31  * creating it first if necessary.
32  */
34 build_node(INode *max_node) {
35  MaxNodeDesc *node_desc = r_build_node(max_node);
36  node_desc->from_INode(max_node);
37 
38  if (node_desc->is_node_joint()) {
39  node_desc->_joint_entry = build_joint(max_node, node_desc);
40  }
41  return node_desc;
42 }
43 
44 /**
45  * Returns a pointer to the node corresponding to the indicated INode object,
46  * creating it first if necessary.
47  */
49 build_joint(INode *max_node, MaxNodeDesc *node_joint) {
50  MaxNodeDesc *node_desc = r_build_joint(node_joint, max_node);
51  node_desc->from_INode(max_node);
52  node_desc->set_joint(true);
53  return node_desc;
54 }
55 
56 bool MaxNodeTree::node_in_list(ULONG handle, ULONG *list, int len) {
57  if (!list) return true;
58  for (int i = 0; i < len; i++)
59  if (list[i] == handle) return true;
60  return false;
61 }
62 
63 bool MaxNodeTree::is_joint(INode *node) {
64  Control *c = node->GetTMController();
65  return (node->GetBoneNodeOnOff() || //joints
66  (c && //bipeds
67  ((c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) ||
68  (c->ClassID() == BIPBODY_CONTROL_CLASS_ID) ||
69  (c->ClassID() == FOOTPRINT_CLASS_ID))));
70 }
71 
72 bool MaxNodeTree::
73 r_build_hierarchy(INode *root, ULONG *selection_list, int len) {
74  if (node_in_list(root->GetHandle(), selection_list, len))
75  build_node(root);
76  // Export children
77  for ( int i = 0; i < root->NumberOfChildren(); i++ ) {
78  // *** Should probably be checking the return value of the following line
79  r_build_hierarchy(root->GetChildNode(i), selection_list, len);
80  }
81  return true;
82 }
83 /**
84  * Walks through the complete Max hierarchy and builds up the corresponding
85  * tree.
86  */
87 bool MaxNodeTree::
88 build_complete_hierarchy(INode *root, ULONG *selection_list, int len) {
89 
90  // Get the entire Max scene.
91  if (root == nullptr) {
92  // *** Log an error
93  return false;
94  }
95 
96  bool all_ok = true;
97  r_build_hierarchy(root, selection_list, len);
98 
99  if (all_ok) {
100  _root->check_pseudo_joints(false);
101  }
102 
103  return all_ok;
104 }
105 
106 /**
107  * Returns the total number of nodes in the hierarchy, not counting the root
108  * node.
109  */
110 int MaxNodeTree::
111 get_num_nodes() const {
112  return _nodes.size();
113 }
114 
115 /**
116  * Returns the nth node in the hierarchy, in an arbitrary ordering.
117  */
119 get_node(int n) const {
120  nassertr(n >= 0 && n < (int)_nodes.size(), nullptr);
121  return _nodes[n];
122 }
123 
124 /**
125  * Removes all of the references to generated egg structures from the tree,
126  * and prepares the tree for generating new egg structures.
127  */
128 void MaxNodeTree::
129 clear_egg(EggData *egg_data, EggGroupNode *egg_root,
130  EggGroupNode *skeleton_node) {
131  _root->clear_egg();
132  _egg_data = egg_data;
133  _egg_root = egg_root;
134  _skeleton_node = skeleton_node;
135 }
136 
137 /**
138  * Returns the EggGroupNode corresponding to the group or joint for the
139  * indicated node. Creates the group node if it has not already been created.
140  */
143  nassertr(_egg_root != nullptr, nullptr);
144 
145  if (node_desc->_egg_group == nullptr) {
146  // We need to make a new group node.
147  EggGroup *egg_group;
148 
149  nassertr(node_desc->_parent != nullptr, nullptr);
150  egg_group = new EggGroup(node_desc->get_name());
151  if (node_desc->is_joint()) {
152  egg_group->set_group_type(EggGroup::GT_joint);
153  }
154  if (node_desc->_parent == _root) {
155  // The parent is the root. Set collision properties for the root if it
156  // has them:
157  if(!_export_mesh)
158  {
159  set_collision_tags(node_desc, egg_group);
160  }
161  _egg_root->add_child(egg_group);
162 
163  } else {
164  // The parent is another node. if export mesh, the tag should be added
165  // at the second level
166  if(_export_mesh)
167  {
168  if(node_desc->_parent->_parent == _root)
169  {
170  set_collision_tags(node_desc, egg_group);
171  }
172  }
173  EggGroup *parent_egg_group = get_egg_group(node_desc->_parent);
174  parent_egg_group->add_child(egg_group);
175  }
176 
177  node_desc->_egg_group = egg_group;
178  }
179 
180  return node_desc->_egg_group;
181 }
182 
183 /**
184  * Returns the EggTable corresponding to the joint for the indicated node.
185  * Creates the table node if it has not already been created.
186  */
189  nassertr(_skeleton_node != nullptr, nullptr);
190  nassertr(node_desc->is_joint(), nullptr);
191 
192  if (node_desc->_egg_table == nullptr) {
193  // We need to make a new table node.
194  nassertr(node_desc->_parent != nullptr, nullptr);
195 
196  EggTable *egg_table = new EggTable(node_desc->get_name());
197  node_desc->_anim = new EggXfmSAnim("xform",
198  _egg_data->get_coordinate_system());
199  node_desc->_anim->set_fps(_fps);
200  egg_table->add_child(node_desc->_anim);
201 
202  if (!node_desc->_parent->is_joint()) {
203  // The parent is not a joint; put it at the top.
204  _skeleton_node->add_child(egg_table);
205 
206  } else {
207  // The parent is another joint.
208  EggTable *parent_egg_table = get_egg_table(node_desc->_parent);
209  parent_egg_table->add_child(egg_table);
210  }
211 
212  node_desc->_egg_table = egg_table;
213  }
214 
215  return node_desc->_egg_table;
216 }
217 
218 /**
219  * Returns the anim table corresponding to the joint for the indicated node.
220  * Creates the table node if it has not already been created.
221  */
224  get_egg_table(node_desc);
225  return node_desc->_anim;
226 }
227 
228 /**
229  * The recursive implementation of build_node().
230  */
231 MaxNodeDesc *MaxNodeTree::
232 r_build_node(INode* max_node) {
233  // If we have already encountered this pathname, return the corresponding
234  // MaxNodeDesc immediately.
235 
236  ULONG node_handle = 0;
237 
238  if (max_node) {
239  node_handle = max_node->GetHandle();
240  }
241 
242  NodesByPath::const_iterator ni = _nodes_by_path.find(node_handle);
243  if (ni != _nodes_by_path.end()) {
244  return (*ni).second;
245  }
246 
247  // Otherwise, we have to create it. Do this recursively, so we create each
248  // node along the path.
249  MaxNodeDesc *node_desc;
250 
251  if (!max_node) {
252  // This is the top.
253  node_desc = _root;
254 
255  } else {
256  INode *parent_node;
257 
258  if (max_node->IsRootNode()) {
259  parent_node = nullptr;
260  } else {
261  parent_node = max_node->GetParentNode();
262  }
263 
264  MaxNodeDesc *parent_node_desc = r_build_node(parent_node);
265  node_desc = new MaxNodeDesc(parent_node_desc, max_node);
266  _nodes.push_back(node_desc);
267  }
268 
269  _nodes_by_path.insert(NodesByPath::value_type(node_handle, node_desc));
270  return node_desc;
271 }
272 
273 /**
274  * The recursive implementation of build_joint().
275  */
276 MaxNodeDesc *MaxNodeTree::
277 r_build_joint(MaxNodeDesc *node_desc, INode *max_node)
278 {
279  MaxNodeDesc *node_joint;
280  if (node_desc == _root) {
281  node_joint = new MaxNodeDesc(_root, max_node);
282  _nodes.push_back(node_joint);
283  return node_joint;
284  } else if (node_desc->is_node_joint() && node_desc->_joint_entry) {
285  node_joint = new MaxNodeDesc(node_desc->_joint_entry, max_node);
286  _nodes.push_back(node_joint);
287  return node_joint;
288  } else {
289  return r_build_joint(node_desc->_parent, max_node);
290  }
291 }
292 
293 /**
294  * The recursive implementation of build_node().
295  */
297 find_node(INode* max_node) {
298  // If we have already encountered this pathname, return the corresponding
299  // MaxNodeDesc immediately.
300 
301  ULONG node_handle = 0;
302 
303  if (max_node) {
304  node_handle = max_node->GetHandle();
305  }
306 
307  NodesByPath::const_iterator ni = _nodes_by_path.find(node_handle);
308  if (ni != _nodes_by_path.end()) {
309  return (*ni).second;
310  }
311 
312  return nullptr;
313 }
314 
315 /**
316  * The recursive implementation of build_node().
317  */
319 find_joint(INode* max_node)
320 {
321  MaxNodeDesc *node = find_node(max_node);
322  if (!node || (is_joint(max_node) && !node->is_node_joint()))
323  node = build_node(max_node);
324  return node->_joint_entry;
325 }
326 
327 /**
328  * Sets the corresponding collision tag to the egg_group based on the User
329  * Defined Tab in the object properties panel
330  */
331 void MaxNodeTree::set_collision_tags(MaxNodeDesc *node_desc, EggGroup *egg_group) {
332  // Max has huge problems passing strings and bools to Get and SetUserProp
333  // So instead we have to use Integers. Now we have to check for every
334  // collide type, then get its collide flags and do some number crunching
335  // to get the actual flag into the group
336 
337  int check = 1; //is the value true. This could be anything really
338 
339  // We have to check each collision type in turn to see if it's true Ugly
340  // but it works per object, not globaly
341  if (node_desc->get_max_node()->GetUserPropInt(_T("polyset"), check)) {
342  // we have a polyset.
343  if (check == 1) {
344  egg_group->set_collision_name(node_desc->get_name());
345  egg_group->set_cs_type(EggGroup::CST_polyset);
346  }
347  }
348  if (node_desc->get_max_node()->GetUserPropInt(_T("plane"), check)) {
349  // plane
350  if (check == 1) {
351  egg_group->set_collision_name(node_desc->get_name());
352  egg_group->set_cs_type(EggGroup::CST_plane);
353  }
354  }
355  if (node_desc->get_max_node()->GetUserPropInt(_T("polygon"), check)) {
356  // polygon
357  if (check == 1) {
358  egg_group->set_collision_name(node_desc->get_name());
359  egg_group->set_cs_type(EggGroup::CST_polygon);
360  }
361  }
362  if (node_desc->get_max_node()->GetUserPropInt(_T("sphere"), check)) {
363  // sphere
364  if (check == 1) {
365  egg_group->set_collision_name(node_desc->get_name());
366  egg_group->set_cs_type(EggGroup::CST_sphere);
367  }
368  }
369  if (node_desc->get_max_node()->GetUserPropInt(_T("inv-sphere"), check)) {
370  // invsphere
371  if (check == 1) {
372  egg_group->set_collision_name(node_desc->get_name());
373  egg_group->set_cs_type(EggGroup::CST_inv_sphere);
374  }
375  }
376  if (node_desc->get_max_node()->GetUserPropInt(_T("invsphere"), check)) {
377  // invsphere (different spelling)
378  if (check == 1) {
379  egg_group->set_collision_name(node_desc->get_name());
380  egg_group->set_cs_type(EggGroup::CST_inv_sphere);
381  }
382  }
383  if (node_desc->get_max_node()->GetUserPropInt(_T("tube"), check)) {
384  // tube
385  if (check == 1) {
386  egg_group->set_collision_name(node_desc->get_name());
387  egg_group->set_cs_type(EggGroup::CST_tube);
388  }
389  }
390  if (node_desc->get_max_node()->GetUserPropInt(_T("floor-mesh"), check)) {
391  // floor-mesh
392  if (check == 1) {
393  egg_group->set_collision_name(node_desc->get_name());
394  egg_group->set_cs_type(EggGroup::CST_floor_mesh);
395  }
396  }
397 
398  if (node_desc->get_max_node()->GetUserPropInt(_T("descend"), check)) {
399  if (check == 1) {
400  // we have the descend flag specified
401  egg_group->set_collide_flags(EggGroup::CF_descend);
402  }
403  }
404  if (node_desc->get_max_node()->GetUserPropInt(_T("event"), check)) {
405  if (check == 1) {
406  // we have the event flag specified
407  egg_group->set_collide_flags(EggGroup::CF_event);
408  }
409  }
410  if (node_desc->get_max_node()->GetUserPropInt(_T("keep"), check)) {
411  if (check == 1) {
412  // we have the keep flag specified
413  egg_group->set_collide_flags(EggGroup::CF_keep);
414  }
415  }
416  if (node_desc->get_max_node()->GetUserPropInt(_T("solid"), check)) {
417  if (check == 1) {
418  // we have the solid flag specified
419  egg_group->set_collide_flags(EggGroup::CF_solid);
420  }
421  }
422  if (node_desc->get_max_node()->GetUserPropInt(_T("center"), check)) {
423  if (check == 1) {
424  // we have the center flag specified
425  egg_group->set_collide_flags(EggGroup::CF_center);
426  }
427  }
428  if (node_desc->get_max_node()->GetUserPropInt(_T("turnstile"), check)) {
429  if (check == 1) {
430  // we have the turnstile flag specified
431  egg_group->set_collide_flags(EggGroup::CF_turnstile);
432  }
433  }
434  if (node_desc->get_max_node()->GetUserPropInt(_T("level"), check)) {
435  if (check == 1) {
436  // we have the level flag specified
437  egg_group->set_collide_flags(EggGroup::CF_level);
438  }
439  }
440  if (node_desc->get_max_node()->GetUserPropInt(_T("intangible"), check)) {
441  if (check == 1) {
442  // we have the intangible flag specified
443  egg_group->set_collide_flags(EggGroup::CF_intangible);
444  }
445  }
446  return;
447 }
EggGroup * get_egg_group(MaxNodeDesc *node_desc)
Returns the EggGroupNode corresponding to the group or joint for the indicated node.
bool is_joint() const
Returns true if the node should be treated as a joint by the converter.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
int get_num_nodes() const
Returns the total number of nodes in the hierarchy, not counting the root node.
EggXfmSAnim * get_egg_anim(MaxNodeDesc *node_desc)
Returns the anim table corresponding to the joint for the indicated node.
MaxNodeDesc * build_node(INode *max_node)
Returns a pointer to the node corresponding to the indicated INode object, creating it first if neces...
Definition: maxNodeTree.cxx:34
bool is_node_joint() const
Returns true if the node is the parent or ancestor of a joint.
bool build_complete_hierarchy(INode *root, ULONG *selection_list, int len)
Walks through the complete Max hierarchy and builds up the corresponding tree.
Definition: maxNodeTree.cxx:88
MaxNodeDesc * find_joint(INode *max_node)
The recursive implementation of build_node().
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
void from_INode(INode *max_node)
Indicates an associated between the MaxNodeDesc and some Max Node instance.
Definition: maxNodeDesc.cxx:61
MaxNodeDesc * build_joint(INode *max_node, MaxNodeDesc *node_joint)
Returns a pointer to the node corresponding to the indicated INode object, creating it first if neces...
Definition: maxNodeTree.cxx:49
MaxNodeDesc * get_node(int n) const
Returns the nth node in the hierarchy, in an arbitrary ordering.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
void clear_egg(EggData *egg_data, EggGroupNode *egg_root, EggGroupNode *skeleton_node)
Removes all of the references to generated egg structures from the tree, and prepares the tree for ge...
This corresponds to an <Xfm$Anim_S$> entry, which is a collection of up to nine <S$Anim> entries that...
Definition: eggXfmSAnim.h:28
Describes a single instance of a node in the Max scene graph, relating it to the corresponding egg st...
Definition: maxNodeDesc.h:22
MaxNodeDesc * find_node(INode *max_node)
The recursive implementation of build_node().
This corresponds to a.
Definition: eggTable.h:27
INode * get_max_node() const
Returns the INode associated with this node.
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
get_coordinate_system
Returns the coordinate system in which the egg file is defined.
Definition: eggData.h:73
EggTable * get_egg_table(MaxNodeDesc *node_desc)
Returns the EggTable corresponding to the joint for the indicated node.