Panda3D
|
00001 // Filename: characterMaker.cxx 00002 // Created by: drose (06Mar02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "characterMaker.h" 00016 #include "eggLoader.h" 00017 #include "config_egg2pg.h" 00018 #include "eggBinner.h" 00019 #include "eggGroup.h" 00020 #include "eggPrimitive.h" 00021 #include "eggBin.h" 00022 #include "partGroup.h" 00023 #include "characterJoint.h" 00024 #include "characterJointBundle.h" 00025 #include "characterSlider.h" 00026 #include "character.h" 00027 #include "geomNode.h" 00028 #include "transformState.h" 00029 #include "eggSurface.h" 00030 #include "eggCurve.h" 00031 #include "modelNode.h" 00032 #include "characterVertexSlider.h" 00033 #include "jointVertexTransform.h" 00034 #include "userVertexTransform.h" 00035 #include "eggAnimPreload.h" 00036 #include "animPreloadTable.h" 00037 00038 00039 00040 00041 00042 //////////////////////////////////////////////////////////////////// 00043 // Function: CharacterMaker::Construtor 00044 // Access: Public 00045 // Description: 00046 //////////////////////////////////////////////////////////////////// 00047 CharacterMaker:: 00048 CharacterMaker(EggGroup *root, EggLoader &loader, bool structured) 00049 : _loader(loader), _egg_root(root) { 00050 00051 _character_node = new Character(_egg_root->get_name()); 00052 _bundle = _character_node->get_bundle(0); 00053 00054 _morph_root = (PartGroup *)NULL; 00055 _skeleton_root = new PartGroup(_bundle, "<skeleton>"); 00056 _structured = structured; 00057 } 00058 00059 //////////////////////////////////////////////////////////////////// 00060 // Function: CharacterMaker::make_node 00061 // Access: Public 00062 // Description: 00063 //////////////////////////////////////////////////////////////////// 00064 Character *CharacterMaker:: 00065 make_node() { 00066 make_bundle(); 00067 return _character_node; 00068 } 00069 00070 //////////////////////////////////////////////////////////////////// 00071 // Function: CharacterMaker::get_name 00072 // Access: Public 00073 // Description: Returns the name of the character. 00074 //////////////////////////////////////////////////////////////////// 00075 string CharacterMaker:: 00076 get_name() const { 00077 return _egg_root->get_name(); 00078 } 00079 00080 //////////////////////////////////////////////////////////////////// 00081 // Function: CharacterMaker::egg_to_part 00082 // Access: Public 00083 // Description: Returns the PartGroup node associated with the given 00084 // egg node. If the egg node is not a node in the 00085 // character's hierarchy, returns the top of the 00086 // character's hierarchy. 00087 //////////////////////////////////////////////////////////////////// 00088 PartGroup *CharacterMaker:: 00089 egg_to_part(EggNode *egg_node) const { 00090 int index = egg_to_index(egg_node); 00091 if (index < 0) { 00092 // If there's a reference to the geometry outside of the 00093 // character, just return the root of the character. 00094 return _bundle; 00095 } 00096 nassertr(index < (int)_parts.size(), NULL); 00097 return _parts[index]; 00098 } 00099 00100 //////////////////////////////////////////////////////////////////// 00101 // Function: CharacterMaker::egg_to_transform 00102 // Access: Public 00103 // Description: Returns a JointVertexTransform suitable for 00104 // applying the animation associated with the given 00105 // egg node (which should be a joint). Returns an 00106 // identity transform if the egg node is not a joint in 00107 // the character's hierarchy. 00108 //////////////////////////////////////////////////////////////////// 00109 VertexTransform *CharacterMaker:: 00110 egg_to_transform(EggNode *egg_node) { 00111 int index = egg_to_index(egg_node); 00112 if (index < 0) { 00113 // Not a joint in the hierarchy. 00114 return get_identity_transform(); 00115 } 00116 00117 VertexTransforms::iterator vi = _vertex_transforms.find(index); 00118 if (vi != _vertex_transforms.end()) { 00119 return (*vi).second; 00120 } 00121 00122 PartGroup *part = _parts[index]; 00123 CharacterJoint *joint; 00124 DCAST_INTO_R(joint, part, get_identity_transform()); 00125 00126 PT(VertexTransform) vt = new JointVertexTransform(joint); 00127 _vertex_transforms[index] = vt; 00128 00129 return vt; 00130 } 00131 00132 //////////////////////////////////////////////////////////////////// 00133 // Function: CharacterMaker::egg_to_index 00134 // Access: Public 00135 // Description: Returns the index number associated with the 00136 // PartGroup node for the given egg node, or -1. 00137 //////////////////////////////////////////////////////////////////// 00138 int CharacterMaker:: 00139 egg_to_index(EggNode *egg_node) const { 00140 NodeMap::const_iterator nmi = _node_map.find(egg_node); 00141 if (nmi == _node_map.end()) { 00142 return -1; 00143 } 00144 return (*nmi).second; 00145 } 00146 00147 //////////////////////////////////////////////////////////////////// 00148 // Function: CharacterMaker::part_to_node 00149 // Access: Public 00150 // Description: Returns the scene graph node associated with the 00151 // given PartGroup node, if there is one. If the 00152 // PartGroup does not have an associated node, returns 00153 // the character's top node. 00154 //////////////////////////////////////////////////////////////////// 00155 PandaNode *CharacterMaker:: 00156 part_to_node(PartGroup *part, const string &name) const { 00157 PandaNode *node = _character_node; 00158 00159 if (part->is_of_type(CharacterJoint::get_class_type())) { 00160 CharacterJoint *joint = DCAST(CharacterJoint, part); 00161 if (joint->_geom_node != (PandaNode *)NULL) { 00162 node = joint->_geom_node; 00163 } 00164 } 00165 00166 // We should always return a GeomNode, so that all polysets 00167 // created at the same level will get added into the same 00168 // GeomNode. Look for a child of this node. If it doesn't have a 00169 // child yet, add a GeomNode and return it. Otherwise, if it 00170 // already has a child, return that. 00171 if (node->is_geom_node() && node->get_name() == name) { 00172 return node; 00173 } 00174 for (int i = 0; i < node->get_num_children(); i++) { 00175 PandaNode *child = node->get_child(i); 00176 if (child->is_geom_node() && child->get_name() == name) { 00177 return child; 00178 } 00179 } 00180 PT(GeomNode) geom_node = new GeomNode(name); 00181 node->add_child(geom_node); 00182 return geom_node; 00183 } 00184 00185 00186 //////////////////////////////////////////////////////////////////// 00187 // Function: CharacterMaker::create_slider 00188 // Access: Public 00189 // Description: Creates a new morph slider of the given name, and 00190 // returns its index. 00191 //////////////////////////////////////////////////////////////////// 00192 int CharacterMaker:: 00193 create_slider(const string &name) { 00194 if (_morph_root == (PartGroup *)NULL) { 00195 _morph_root = new PartGroup(_bundle, "morph"); 00196 } 00197 CharacterSlider *slider = new CharacterSlider(_morph_root, name); 00198 int index = _parts.size(); 00199 _parts.push_back(slider); 00200 return index; 00201 } 00202 00203 //////////////////////////////////////////////////////////////////// 00204 // Function: CharacterMaker::egg_to_slider 00205 // Access: Public 00206 // Description: Returns the VertexSlider corresponding to the 00207 // indicated egg slider name. 00208 //////////////////////////////////////////////////////////////////// 00209 VertexSlider *CharacterMaker:: 00210 egg_to_slider(const string &name) { 00211 VertexSliders::iterator vi = _vertex_sliders.find(name); 00212 if (vi != _vertex_sliders.end()) { 00213 return (*vi).second; 00214 } 00215 00216 int index = create_slider(name); 00217 PT(VertexSlider) slider = 00218 new CharacterVertexSlider(DCAST(CharacterSlider, _parts[index])); 00219 _vertex_sliders[name] = slider; 00220 return slider; 00221 } 00222 00223 00224 //////////////////////////////////////////////////////////////////// 00225 // Function: CharacterMaker::make_bundle 00226 // Access: Private 00227 // Description: 00228 //////////////////////////////////////////////////////////////////// 00229 CharacterJointBundle *CharacterMaker:: 00230 make_bundle() { 00231 build_joint_hierarchy(_egg_root, _skeleton_root, -1); 00232 00233 //if we are structured, the egg loader is going to take care of making the geometry 00234 if(!_structured) { 00235 make_geometry(_egg_root); 00236 } 00237 _bundle->sort_descendants(); 00238 parent_joint_nodes(_skeleton_root); 00239 00240 // Now call update() one more time, to ensure that all of the joints 00241 // have their correct transform (since we might have modified the 00242 // default transform after construction). 00243 _bundle->force_update(); 00244 00245 return _bundle; 00246 } 00247 00248 //////////////////////////////////////////////////////////////////// 00249 // Function: CharacterMaker::build_joint_hierarchy 00250 // Access: Private 00251 // Description: 00252 //////////////////////////////////////////////////////////////////// 00253 void CharacterMaker:: 00254 build_joint_hierarchy(EggNode *egg_node, PartGroup *part, int index) { 00255 if (egg_node->is_of_type(EggAnimPreload::get_class_type())) { 00256 EggAnimPreload *egg_anim_preload = DCAST(EggAnimPreload, egg_node); 00257 00258 double fps = 24.0; 00259 if (egg_anim_preload->has_fps()) { 00260 fps = egg_anim_preload->get_fps(); 00261 } 00262 00263 int num_frames = 1; 00264 if (egg_anim_preload->has_num_frames()) { 00265 num_frames = egg_anim_preload->get_num_frames(); 00266 } 00267 00268 PT(AnimPreloadTable) anim_preload = _bundle->modify_anim_preload(); 00269 if (anim_preload == (AnimPreloadTable *)NULL) { 00270 anim_preload = new AnimPreloadTable; 00271 _bundle->set_anim_preload(anim_preload); 00272 } 00273 anim_preload->add_anim(egg_node->get_name(), fps, num_frames); 00274 return; 00275 } 00276 00277 if (egg_node->is_of_type(EggGroup::get_class_type())) { 00278 EggGroup *egg_group = DCAST(EggGroup, egg_node); 00279 00280 // Each joint we come across is significant, and gets added to the 00281 // hierarchy. Non-joints we encounter are ignored. 00282 if (egg_group->get_group_type() == EggGroup::GT_joint) { 00283 // We need to get the transform of the joint, and then convert 00284 // it to single-precision. 00285 LMatrix4d matd; 00286 00287 // First, we get the original, initial transform from the 00288 // <Transform> entry. 00289 if (egg_group->has_transform()) { 00290 matd = egg_group->get_transform3d(); 00291 } else { 00292 matd = LMatrix4d::ident_mat(); 00293 } 00294 00295 LMatrix4 matf = LCAST(PN_stdfloat, matd); 00296 00297 CharacterJoint *joint = 00298 new CharacterJoint(_character_node, _character_node->get_bundle(0), 00299 part, egg_group->get_name(), matf); 00300 index = _parts.size(); 00301 _parts.push_back(joint); 00302 00303 // Now that we have computed _net_transform (which we need to 00304 // convert the vertices), update the default transform from the 00305 // <DefaultPose> entry. 00306 if (egg_group->get_default_pose().has_transform()) { 00307 matd = egg_group->get_default_pose().get_transform3d(); 00308 matf = LCAST(PN_stdfloat, matd); 00309 joint->_default_value = matf; 00310 joint->_value = matf; 00311 } 00312 00313 if (egg_group->has_dcs_type()) { 00314 // If the joint requested an explicit DCS, create a node for 00315 // it. 00316 PT(ModelNode) geom_node = new ModelNode(egg_group->get_name()); 00317 00318 // To prevent flattening from messing with geometry on 00319 // exposed joints 00320 geom_node->set_preserve_transform(ModelNode::PT_net); 00321 00322 joint->_geom_node = geom_node.p(); 00323 } 00324 00325 part = joint; 00326 } 00327 00328 EggGroup::const_iterator ci; 00329 for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { 00330 build_joint_hierarchy((*ci), part, index); 00331 } 00332 } 00333 00334 _node_map[egg_node] = index; 00335 } 00336 00337 //////////////////////////////////////////////////////////////////// 00338 // Function: CharacterMaker::parent_joint_nodes 00339 // Access: Private 00340 // Description: Walks the joint hierarchy, and parents any explicit 00341 // nodes created for the joints under the character 00342 // node. 00343 //////////////////////////////////////////////////////////////////// 00344 void CharacterMaker:: 00345 parent_joint_nodes(PartGroup *part) { 00346 if (part->is_of_type(CharacterJoint::get_class_type())) { 00347 CharacterJoint *joint = DCAST(CharacterJoint, part); 00348 PandaNode *joint_node = joint->_geom_node; 00349 if (joint_node != NULL) { 00350 _character_node->add_child(joint_node); 00351 joint->add_net_transform(joint_node); 00352 joint_node->set_transform(TransformState::make_mat(joint->_net_transform)); 00353 } 00354 } 00355 00356 for (int i = 0; i < part->get_num_children(); i++) { 00357 parent_joint_nodes(part->get_child(i)); 00358 } 00359 } 00360 00361 //////////////////////////////////////////////////////////////////// 00362 // Function: CharacterMaker::make_geometry 00363 // Access: Private 00364 // Description: Walks the hierarchy, looking for bins that represent 00365 // polysets, which are to be animated with the 00366 // character. Invokes the egg loader to create the 00367 // animated geometry. 00368 //////////////////////////////////////////////////////////////////// 00369 void CharacterMaker:: 00370 make_geometry(EggNode *egg_node) { 00371 if (egg_node->is_of_type(EggBin::get_class_type())) { 00372 EggBin *egg_bin = DCAST(EggBin, egg_node); 00373 00374 if (!egg_bin->empty() && 00375 egg_bin->get_bin_number() == EggBinner::BN_polyset) { 00376 EggGroupNode *bin_home = determine_bin_home(egg_bin); 00377 00378 bool is_dynamic; 00379 if (bin_home == (EggGroupNode *)NULL) { 00380 // This is a dynamic polyset that lives under the character's 00381 // root node. 00382 bin_home = _egg_root; 00383 is_dynamic = true; 00384 } else { 00385 // This is a totally static polyset that is parented under 00386 // some animated joint node. 00387 is_dynamic = false; 00388 } 00389 00390 PandaNode *parent = part_to_node(egg_to_part(bin_home), egg_bin->get_name()); 00391 LMatrix4d transform = 00392 egg_bin->get_vertex_frame() * 00393 bin_home->get_node_frame_inv(); 00394 00395 _loader.make_polyset(egg_bin, parent, &transform, is_dynamic, 00396 this); 00397 } 00398 } 00399 00400 if (egg_node->is_of_type(EggGroupNode::get_class_type())) { 00401 EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); 00402 00403 EggGroupNode::const_iterator ci; 00404 for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) { 00405 make_geometry(*ci); 00406 } 00407 } 00408 } 00409 00410 //////////////////////////////////////////////////////////////////// 00411 // Function: CharacterMaker::determine_primitive_home 00412 // Access: Private 00413 // Description: 00414 //////////////////////////////////////////////////////////////////// 00415 EggGroupNode *CharacterMaker:: 00416 determine_primitive_home(EggPrimitive *egg_primitive) { 00417 // A primitive's vertices may be referenced by any joint in the 00418 // character. Or, the primitive itself may be explicitly placed 00419 // under a joint. 00420 00421 // If any of the vertices are referenced by multiple joints, or if 00422 // any two vertices are referenced by different joints, then the 00423 // entire primitive must be considered dynamic. (We'll indicate a 00424 // dynamic primitive by returning NULL.) 00425 00426 // We need to keep track of the one joint we've encountered so far, 00427 // to see if all the vertices are referenced by the same joint. 00428 EggGroupNode *home = NULL; 00429 00430 EggPrimitive::const_iterator vi; 00431 for (vi = egg_primitive->begin(); 00432 vi != egg_primitive->end(); 00433 ++vi) { 00434 EggVertex *vertex = (*vi); 00435 if (vertex->gref_size() > 1) { 00436 // This vertex is referenced by multiple joints; the primitive 00437 // is dynamic. 00438 return NULL; 00439 } 00440 00441 if (!vertex->_dxyzs.empty() || 00442 !vertex->_dnormals.empty() || 00443 !vertex->_drgbas.empty()) { 00444 // This vertex has some morph slider definitions; therefore, the 00445 // primitive is dynamic. 00446 return NULL; 00447 } 00448 EggVertex::const_uv_iterator uvi; 00449 for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) { 00450 if (!(*uvi)->_duvs.empty()) { 00451 // Ditto: the vertex has some UV morphs; therefore the 00452 // primitive is dynamic. 00453 return NULL; 00454 } 00455 } 00456 00457 EggGroupNode *vertex_home; 00458 00459 if (vertex->gref_size() == 0) { 00460 // This vertex is not referenced at all, which means it belongs 00461 // right where it is. 00462 vertex_home = egg_primitive->get_parent(); 00463 } else { 00464 nassertr(vertex->gref_size() == 1, NULL); 00465 // This vertex is referenced exactly once. 00466 vertex_home = *vertex->gref_begin(); 00467 } 00468 00469 if (home != NULL && home != vertex_home) { 00470 // Oops, two vertices are referenced by different joints! The 00471 // primitive is dynamic. 00472 return NULL; 00473 } 00474 00475 home = vertex_home; 00476 } 00477 00478 // This shouldn't be possible, unless there are no vertices--but we 00479 // check for that before calling this function. 00480 nassertr(home != NULL, NULL); 00481 00482 // So, all the vertices are assigned to the same group. This means 00483 // the polygon belongs entirely to one joint. 00484 00485 // If the group is not, in fact, a joint then we return the first 00486 // joint above the group. 00487 EggGroup *egg_group = (EggGroup *)NULL; 00488 if (home->is_of_type(EggGroup::get_class_type())) { 00489 egg_group = DCAST(EggGroup, home); 00490 } 00491 while (egg_group != (EggGroup *)NULL && 00492 egg_group->get_group_type() != EggGroup::GT_joint && 00493 egg_group->get_dart_type() == EggGroup::DT_none) { 00494 nassertr(egg_group->get_parent() != (EggGroupNode *)NULL, NULL); 00495 home = egg_group->get_parent(); 00496 egg_group = (EggGroup *)NULL; 00497 if (home->is_of_type(EggGroup::get_class_type())) { 00498 egg_group = DCAST(EggGroup, home); 00499 } 00500 } 00501 00502 if (egg_group != (EggGroup *)NULL && 00503 egg_group->get_group_type() == EggGroup::GT_joint && 00504 !egg_group->has_dcs_type()) { 00505 // If the home is a joint without a <DCS> flag--this is the normal 00506 // case--we'll move the polygon under the character node and 00507 // animate it from there explicitly. 00508 return NULL; 00509 } 00510 00511 // Otherwise, if the joint *does* have a <DCS> flag, we'll create 00512 // static geometry that we parent directly to the joint node. 00513 // We'll also create static geometry for polygons that have no 00514 // explicit joint assignment. 00515 return home; 00516 } 00517 00518 //////////////////////////////////////////////////////////////////// 00519 // Function: CharacterMaker::determine_bin_home 00520 // Access: Private 00521 // Description: Examines the joint assignment of the vertices of all 00522 // of the primitives within this bin to determine which 00523 // parent node the bin's polyset should be created 00524 // under. 00525 //////////////////////////////////////////////////////////////////// 00526 EggGroupNode *CharacterMaker:: 00527 determine_bin_home(EggBin *egg_bin) { 00528 // A primitive's vertices may be referenced by any joint in the 00529 // character. Or, the primitive itself may be explicitly placed 00530 // under a joint. 00531 00532 // If any of the vertices, in any primitive, are referenced by 00533 // multiple joints, or if any two vertices are referenced by 00534 // different joints, then the entire bin must be considered dynamic. 00535 // (We'll indicate a dynamic bin by returning NULL.) 00536 00537 if (!egg_rigid_geometry) { 00538 // If we don't have egg-rigid-geometry enabled, then all geometry 00539 // is considered dynamic. 00540 return NULL; 00541 } 00542 00543 // We need to keep track of the one joint we've encountered so far, 00544 // to see if all the vertices are referenced by the same joint. 00545 EggGroupNode *home = NULL; 00546 00547 EggGroupNode::const_iterator ci; 00548 for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) { 00549 CPT(EggPrimitive) egg_primitive = DCAST(EggPrimitive, (*ci)); 00550 00551 EggPrimitive::const_iterator vi; 00552 for (vi = egg_primitive->begin(); 00553 vi != egg_primitive->end(); 00554 ++vi) { 00555 EggVertex *vertex = (*vi); 00556 if (vertex->gref_size() > 1) { 00557 // This vertex is referenced by multiple joints; the primitive 00558 // is dynamic. 00559 return NULL; 00560 } 00561 00562 if (!vertex->_dxyzs.empty() || 00563 !vertex->_dnormals.empty() || 00564 !vertex->_drgbas.empty()) { 00565 // This vertex has some morph slider definitions; therefore, the 00566 // primitive is dynamic. 00567 return NULL; 00568 } 00569 EggVertex::const_uv_iterator uvi; 00570 for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) { 00571 if (!(*uvi)->_duvs.empty()) { 00572 // Ditto: the vertex has some UV morphs; therefore the 00573 // primitive is dynamic. 00574 return NULL; 00575 } 00576 } 00577 00578 EggGroupNode *vertex_home; 00579 00580 if (vertex->gref_size() == 0) { 00581 // This vertex is not referenced at all, which means it belongs 00582 // right where it is. 00583 vertex_home = egg_primitive->get_parent(); 00584 } else { 00585 nassertr(vertex->gref_size() == 1, NULL); 00586 // This vertex is referenced exactly once. 00587 vertex_home = *vertex->gref_begin(); 00588 } 00589 00590 if (home != NULL && home != vertex_home) { 00591 // Oops, two vertices are referenced by different joints! The 00592 // primitive is dynamic. 00593 return NULL; 00594 } 00595 00596 home = vertex_home; 00597 } 00598 } 00599 00600 // This shouldn't be possible, unless there are no vertices--but we 00601 // eliminate invalid primitives before we begin, so all primitives 00602 // should have vertices, and all bins should have primitives. 00603 nassertr(home != NULL, NULL); 00604 00605 // So, all the vertices are assigned to the same group. This means 00606 // all the primitives in the bin belong entirely to one joint. 00607 00608 // If the group is not, in fact, a joint then we return the first 00609 // joint above the group. 00610 EggGroup *egg_group = (EggGroup *)NULL; 00611 if (home->is_of_type(EggGroup::get_class_type())) { 00612 egg_group = DCAST(EggGroup, home); 00613 } 00614 while (egg_group != (EggGroup *)NULL && 00615 egg_group->get_group_type() != EggGroup::GT_joint && 00616 egg_group->get_dart_type() == EggGroup::DT_none) { 00617 nassertr(egg_group->get_parent() != (EggGroupNode *)NULL, NULL); 00618 home = egg_group->get_parent(); 00619 egg_group = (EggGroup *)NULL; 00620 if (home->is_of_type(EggGroup::get_class_type())) { 00621 egg_group = DCAST(EggGroup, home); 00622 } 00623 } 00624 00625 if (egg_group != (EggGroup *)NULL && 00626 egg_group->get_group_type() == EggGroup::GT_joint && 00627 !egg_group->has_dcs_type()) { 00628 // If we have rigid geometry that is assigned to a joint without a 00629 // <DCS> flag, which means the joint didn't get created as its own 00630 // node, go ahead and make an implicit node for the joint. 00631 00632 if (egg_group->get_dcs_type() == EggGroup::DC_none) { 00633 // Unless the user specifically forbade exposing the joint by 00634 // putting an explicit "<DCS> { none }" entry in the joint. In 00635 // this case, we return NULL to treat the geometry as dynamic 00636 // (and animate it by animating its vertices), but display lists 00637 // and vertex buffers will perform better if more geometry is 00638 // rigid. There's a tradeoff, though, since the cull traverser 00639 // will have to do more work with additional transforms in the 00640 // scene graph, and this may also break up the geometry into 00641 // more individual pieces, which is the biggest limiting factor 00642 // on modern PC graphics cards. 00643 return NULL; 00644 } 00645 00646 CharacterJoint *joint; 00647 DCAST_INTO_R(joint, egg_to_part(egg_group), home); 00648 egg_group->set_dcs_type(EggGroup::DC_default); 00649 00650 PT(ModelNode) geom_node = new ModelNode(egg_group->get_name()); 00651 geom_node->set_preserve_transform(ModelNode::PT_local); 00652 joint->_geom_node = geom_node.p(); 00653 } 00654 00655 return home; 00656 } 00657 00658 //////////////////////////////////////////////////////////////////// 00659 // Function: CharacterMaker::get_identity_transform 00660 // Access: Private 00661 // Description: Returns a VertexTransform that represents the root of 00662 // the character--it never animates. 00663 //////////////////////////////////////////////////////////////////// 00664 VertexTransform *CharacterMaker:: 00665 get_identity_transform() { 00666 if (_identity_transform == (VertexTransform *)NULL) { 00667 _identity_transform = new UserVertexTransform("root"); 00668 } 00669 return _identity_transform; 00670 }