Panda3D
|
00001 // Filename: bamToEgg.cxx 00002 // Created by: drose (25Jun01) 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 "bamToEgg.h" 00016 00017 #include "pandaNode.h" 00018 #include "workingNodePath.h" 00019 #include "nodePath.h" 00020 #include "billboardEffect.h" 00021 #include "renderEffects.h" 00022 #include "transformState.h" 00023 #include "colorScaleAttrib.h" 00024 #include "colorAttrib.h" 00025 #include "textureAttrib.h" 00026 #include "cullFaceAttrib.h" 00027 #include "transparencyAttrib.h" 00028 #include "depthWriteAttrib.h" 00029 #include "lodNode.h" 00030 #include "switchNode.h" 00031 #include "sequenceNode.h" 00032 #include "collisionNode.h" 00033 #include "collisionPolygon.h" 00034 #include "collisionPlane.h" 00035 #include "collisionSphere.h" 00036 #include "collisionInvSphere.h" 00037 #include "collisionTube.h" 00038 #include "textureStage.h" 00039 #include "geomNode.h" 00040 #include "geom.h" 00041 #include "geomTriangles.h" 00042 #include "geomPoints.h" 00043 #include "geomLines.h" 00044 #include "geomVertexReader.h" 00045 #include "transformTable.h" 00046 #include "modelNode.h" 00047 #include "animBundleNode.h" 00048 #include "animChannelMatrixXfmTable.h" 00049 #include "characterJoint.h" 00050 #include "character.h" 00051 #include "string_utils.h" 00052 #include "bamFile.h" 00053 #include "bamCacheRecord.h" 00054 #include "eggSAnimData.h" 00055 #include "eggXfmAnimData.h" 00056 #include "eggXfmSAnim.h" 00057 #include "eggGroup.h" 00058 #include "eggVertexPool.h" 00059 #include "eggVertex.h" 00060 #include "eggPrimitive.h" 00061 #include "eggPolygon.h" 00062 #include "eggPoint.h" 00063 #include "eggLine.h" 00064 #include "eggTexture.h" 00065 #include "eggMaterial.h" 00066 #include "eggRenderMode.h" 00067 #include "eggTable.h" 00068 #include "somethingToEggConverter.h" 00069 #include "dcast.h" 00070 #include "pystub.h" 00071 00072 //////////////////////////////////////////////////////////////////// 00073 // Function: BamToEgg::Constructor 00074 // Access: Public 00075 // Description: 00076 //////////////////////////////////////////////////////////////////// 00077 BamToEgg:: 00078 BamToEgg() : 00079 SomethingToEgg("bam", ".bam") 00080 { 00081 add_path_replace_options(); 00082 add_path_store_options(); 00083 00084 set_program_description 00085 ("This program converts native Panda bam files to egg. The conversion " 00086 "is somewhat incomplete; running egg2bam followed by bam2egg should not " 00087 "be expected to yield the same egg file you started with."); 00088 00089 redescribe_option 00090 ("cs", 00091 "Specify the coordinate system of the input " + _format_name + 00092 " file. By default, this is taken from the Config.prc file, which " 00093 "is currently " + format_string(get_default_coordinate_system()) + "."); 00094 00095 _coordinate_system = get_default_coordinate_system(); 00096 } 00097 00098 //////////////////////////////////////////////////////////////////// 00099 // Function: BamToEgg::run 00100 // Access: Public 00101 // Description: 00102 //////////////////////////////////////////////////////////////////// 00103 void BamToEgg:: 00104 run() { 00105 BamFile bam_file; 00106 00107 if (!bam_file.open_read(_input_filename)) { 00108 nout << "Unable to read " << _input_filename << "\n"; 00109 exit(1); 00110 } 00111 00112 nout << _input_filename << " : Bam version " 00113 << bam_file.get_file_major_ver() << "." 00114 << bam_file.get_file_minor_ver() << "\n"; 00115 00116 typedef pvector<TypedWritable *> Objects; 00117 Objects objects; 00118 TypedWritable *object = bam_file.read_object(); 00119 00120 if (object != (TypedWritable *)NULL && 00121 object->is_exact_type(BamCacheRecord::get_class_type())) { 00122 // Here's a special case: if the first object in the file is a 00123 // BamCacheRecord, it's really a cache data file and not a true 00124 // bam file; but skip over the cache data record and let the user 00125 // treat it like an ordinary bam file. 00126 object = bam_file.read_object(); 00127 } 00128 00129 while (object != (TypedWritable *)NULL || !bam_file.is_eof()) { 00130 if (object != (TypedWritable *)NULL) { 00131 ReferenceCount *ref_ptr = object->as_reference_count(); 00132 if (ref_ptr != NULL) { 00133 ref_ptr->ref(); 00134 } 00135 objects.push_back(object); 00136 } 00137 object = bam_file.read_object(); 00138 } 00139 bam_file.resolve(); 00140 bam_file.close(); 00141 00142 _data->set_coordinate_system(_coordinate_system); 00143 _vpool = new EggVertexPool("vpool"); 00144 _data->add_child(_vpool); 00145 00146 if (objects.size() == 1 && 00147 objects[0]->is_of_type(PandaNode::get_class_type())) { 00148 PandaNode *node = DCAST(PandaNode, objects[0]); 00149 NodePath root(node); 00150 convert_node(WorkingNodePath(root), _data, false); 00151 00152 } else { 00153 nout << "File does not contain a scene graph.\n"; 00154 exit(1); 00155 } 00156 00157 // Remove the vertex pool if it has no vertices. 00158 if (_vpool->empty()) { 00159 _data->remove_child(_vpool); 00160 } 00161 00162 write_egg_file(); 00163 } 00164 00165 //////////////////////////////////////////////////////////////////// 00166 // Function: BamToEgg::convert_node 00167 // Access: Private 00168 // Description: Converts the indicated node to the corresponding Egg 00169 // constructs, by first determining what kind of node it 00170 // is. 00171 //////////////////////////////////////////////////////////////////// 00172 void BamToEgg:: 00173 convert_node(const WorkingNodePath &node_path, EggGroupNode *egg_parent, 00174 bool has_decal) { 00175 PandaNode *node = node_path.node(); 00176 if (node->is_geom_node()) { 00177 convert_geom_node(DCAST(GeomNode, node), node_path, egg_parent, has_decal); 00178 00179 } else if (node->is_of_type(LODNode::get_class_type())) { 00180 convert_lod_node(DCAST(LODNode, node), node_path, egg_parent, has_decal); 00181 00182 } else if (node->is_of_type(SequenceNode::get_class_type())) { 00183 convert_sequence_node(DCAST(SequenceNode, node), node_path, egg_parent, has_decal); 00184 00185 } else if (node->is_of_type(SwitchNode::get_class_type())) { 00186 convert_switch_node(DCAST(SwitchNode, node), node_path, egg_parent, has_decal); 00187 00188 } else if (node->is_of_type(CollisionNode::get_class_type())) { 00189 convert_collision_node(DCAST(CollisionNode, node), node_path, egg_parent, has_decal); 00190 00191 } else if (node->is_of_type(AnimBundleNode::get_class_type())) { 00192 convert_anim_node(DCAST(AnimBundleNode, node), node_path, egg_parent, has_decal); 00193 00194 } else if (node->is_of_type(Character::get_class_type())) { 00195 convert_character_node(DCAST(Character, node), node_path, egg_parent, has_decal); 00196 00197 } else { 00198 // Just a generic node. 00199 EggGroup *egg_group = new EggGroup(node->get_name()); 00200 egg_parent->add_child(egg_group); 00201 apply_node_properties(egg_group, node); 00202 00203 recurse_nodes(node_path, egg_group, has_decal); 00204 } 00205 } 00206 00207 //////////////////////////////////////////////////////////////////// 00208 // Function: BamToEgg::convert_lod_node 00209 // Access: Private 00210 // Description: Converts the indicated LODNode to the corresponding 00211 // Egg constructs. 00212 //////////////////////////////////////////////////////////////////// 00213 void BamToEgg:: 00214 convert_lod_node(LODNode *node, const WorkingNodePath &node_path, 00215 EggGroupNode *egg_parent, bool has_decal) { 00216 // An LOD node gets converted to an ordinary EggGroup, but we apply 00217 // the appropriate switch conditions to each of our children. 00218 EggGroup *egg_group = new EggGroup(node->get_name()); 00219 egg_parent->add_child(egg_group); 00220 apply_node_properties(egg_group, node); 00221 00222 int num_children = node->get_num_children(); 00223 int num_switches = node->get_num_switches(); 00224 00225 num_children = min(num_children, num_switches); 00226 00227 for (int i = 0; i < num_children; i++) { 00228 PandaNode *child = node->get_child(i); 00229 00230 // Convert just this one node to an EggGroup. 00231 PT(EggGroup) next_group = new EggGroup; 00232 convert_node(WorkingNodePath(node_path, child), next_group, has_decal); 00233 00234 if (next_group->size() == 1) { 00235 // If we have exactly one child, and that child is an EggGroup, 00236 // collapse. 00237 EggNode *child_node = *next_group->begin(); 00238 if (child_node->is_of_type(EggGroup::get_class_type())) { 00239 PT(EggGroup) child = DCAST(EggGroup, child_node); 00240 next_group->remove_child(child.p()); 00241 next_group = child; 00242 } 00243 } 00244 00245 // Now set up the switching properties appropriately. 00246 PN_stdfloat in = node->get_in(i); 00247 PN_stdfloat out = node->get_out(i); 00248 LPoint3 center = node->get_center(); 00249 EggSwitchConditionDistance dist(in, out, LCAST(double, center)); 00250 next_group->set_lod(dist); 00251 egg_group->add_child(next_group.p()); 00252 } 00253 } 00254 00255 //////////////////////////////////////////////////////////////////// 00256 // Function: BamToEgg::convert_sequence_node 00257 // Access: Private 00258 // Description: Converts the indicated SequenceNode to the corresponding 00259 // Egg constructs. 00260 //////////////////////////////////////////////////////////////////// 00261 void BamToEgg:: 00262 convert_sequence_node(SequenceNode *node, const WorkingNodePath &node_path, 00263 EggGroupNode *egg_parent, bool has_decal) { 00264 // A sequence node gets converted to an ordinary EggGroup, we only apply 00265 // the appropriate switch attributes to turn it into a sequence 00266 EggGroup *egg_group = new EggGroup(node->get_name()); 00267 egg_parent->add_child(egg_group); 00268 apply_node_properties(egg_group, node); 00269 00270 // turn it into a sequence with the right frame-rate 00271 egg_group->set_switch_flag(true); 00272 egg_group->set_switch_fps(node->get_frame_rate()); 00273 00274 int num_children = node->get_num_children(); 00275 00276 for (int i = 0; i < num_children; i++) { 00277 PandaNode *child = node->get_child(i); 00278 00279 // Convert just this one node to an EggGroup. 00280 PT(EggGroup) next_group = new EggGroup; 00281 convert_node(WorkingNodePath(node_path, child), next_group, has_decal); 00282 00283 egg_group->add_child(next_group.p()); 00284 } 00285 00286 } 00287 00288 //////////////////////////////////////////////////////////////////// 00289 // Function: BamToEgg::convert_switch_node 00290 // Access: Private 00291 // Description: Converts the indicated SwitchNode to the corresponding 00292 // Egg constructs. 00293 //////////////////////////////////////////////////////////////////// 00294 void BamToEgg:: 00295 convert_switch_node(SwitchNode *node, const WorkingNodePath &node_path, 00296 EggGroupNode *egg_parent, bool has_decal) { 00297 // A sequence node gets converted to an ordinary EggGroup, we only apply 00298 // the appropriate switch attributes to turn it into a sequence 00299 EggGroup *egg_group = new EggGroup(node->get_name()); 00300 egg_parent->add_child(egg_group); 00301 apply_node_properties(egg_group, node); 00302 00303 // turn it into a switch.. 00304 egg_group->set_switch_flag(true); 00305 00306 int num_children = node->get_num_children(); 00307 00308 for (int i = 0; i < num_children; i++) { 00309 PandaNode *child = node->get_child(i); 00310 00311 // Convert just this one node to an EggGroup. 00312 PT(EggGroup) next_group = new EggGroup; 00313 convert_node(WorkingNodePath(node_path, child), next_group, has_decal); 00314 00315 egg_group->add_child(next_group.p()); 00316 } 00317 } 00318 00319 //////////////////////////////////////////////////////////////////// 00320 // Function: BamToEgg::convert_animGroup_node 00321 // Access: Private 00322 // Description: Converts the indicated AnimationGroupNodes to the corresponding 00323 // Egg constructs. 00324 //////////////////////////////////////////////////////////////////// 00325 EggGroupNode * BamToEgg::convert_animGroup_node(AnimGroup *animGroup, double fps ) { 00326 int num_children = animGroup->get_num_children(); 00327 00328 EggGroupNode *eggNode = NULL; 00329 if (animGroup->is_of_type(AnimBundle::get_class_type())) { 00330 EggTable *eggTable = new EggTable(animGroup->get_name()); 00331 eggTable ->set_table_type(EggTable::TT_bundle); 00332 eggNode = eggTable; 00333 } else if (animGroup->is_of_type(AnimGroup::get_class_type())) { 00334 EggTable *eggTable = new EggTable(animGroup->get_name()); 00335 eggTable ->set_table_type(EggTable::TT_table); 00336 eggNode = eggTable; 00337 } 00338 00339 if (animGroup->is_of_type(AnimChannelMatrixXfmTable::get_class_type())) { 00340 AnimChannelMatrixXfmTable *xmfTable = DCAST(AnimChannelMatrixXfmTable, animGroup); 00341 EggXfmSAnim *egg_anim = new EggXfmSAnim("xform"); 00342 egg_anim->set_fps(fps); 00343 for (int i = 0; i < num_matrix_components; i++) { 00344 string componentName(1, matrix_component_letters[i]); 00345 char table_id = matrix_component_letters[i]; 00346 CPTA_stdfloat table = xmfTable->get_table(table_id); 00347 00348 if (xmfTable->has_table(table_id)) { 00349 for (unsigned int j = 0; j < table.size(); j++) { 00350 egg_anim->add_component_data(componentName, table[(int)j]); 00351 } 00352 } 00353 } 00354 eggNode->add_child(egg_anim); 00355 } 00356 for (int i = 0; i < num_children; i++) { 00357 AnimGroup *animChild = animGroup->get_child(i); 00358 EggGroupNode *eggChildNode = convert_animGroup_node(animChild, fps); 00359 if (eggChildNode!=NULL) { 00360 nassertr(eggNode!=NULL, NULL); 00361 eggNode->add_child(eggChildNode); 00362 } 00363 } 00364 return eggNode; 00365 } 00366 00367 //////////////////////////////////////////////////////////////////// 00368 // Function: BamToEgg::convert_anim_node 00369 // Access: Private 00370 // Description: Converts the indicated AnimNode to the corresponding 00371 // Egg constructs. 00372 //////////////////////////////////////////////////////////////////// 00373 void BamToEgg:: 00374 convert_anim_node(AnimBundleNode *node, const WorkingNodePath &node_path, 00375 EggGroupNode *egg_parent, bool has_decal) { 00376 00377 // A sequence node gets converted to an ordinary EggGroup, we only apply 00378 // the appropriate switch attributes to turn it into a sequence 00379 EggTable *eggTable = new EggTable(); 00380 //egg_parent->add_child(eggTable); 00381 _data->add_child(eggTable); 00382 00383 AnimBundle *animBundle = node->get_bundle(); 00384 // turn it into a switch.. 00385 //egg_group->set_switch_flag(true); 00386 00387 EggGroupNode *eggAnimation = convert_animGroup_node(animBundle, animBundle->get_base_frame_rate()); 00388 eggTable->add_child(eggAnimation); 00389 } 00390 00391 //////////////////////////////////////////////////////////////////// 00392 // Function: BamToEgg::convert_character_bundle 00393 // Access: Private 00394 // Description: Converts the indicated Character Bundle to the corresponding 00395 // Egg joints structure. 00396 //////////////////////////////////////////////////////////////////// 00397 void BamToEgg:: 00398 convert_character_bundle(PartGroup *bundleNode, EggGroupNode *egg_parent, CharacterJointMap *jointMap) { 00399 int num_children = bundleNode->get_num_children(); 00400 00401 EggGroupNode *joint_group = egg_parent; 00402 if (bundleNode->is_of_type(CharacterJoint::get_class_type())) { 00403 CharacterJoint *character_joint = DCAST(CharacterJoint, bundleNode); 00404 00405 LMatrix4 transformf; 00406 character_joint->get_net_transform(transformf); 00407 LMatrix4d transformd(LCAST(double, transformf)); 00408 EggGroup *joint = new EggGroup(bundleNode->get_name()); 00409 joint->add_matrix4(transformd); 00410 joint->set_group_type(EggGroup::GT_joint); 00411 joint_group = joint; 00412 egg_parent->add_child(joint_group); 00413 if (jointMap!=NULL) { 00414 CharacterJointMap::iterator mi = jointMap->find(character_joint); 00415 if (mi != jointMap->end()) { 00416 pvector<pair<EggVertex*,PN_stdfloat> > &joint_vertices = (*mi).second; 00417 pvector<pair<EggVertex*,PN_stdfloat> >::const_iterator vi; 00418 for (vi = joint_vertices.begin(); vi != joint_vertices.end(); ++vi) { 00419 joint->set_vertex_membership((*vi).first, (*vi).second); 00420 } 00421 } 00422 } 00423 } 00424 00425 for (int i = 0; i < num_children ; i++) { 00426 PartGroup *partGroup= bundleNode->get_child(i); 00427 convert_character_bundle(partGroup, joint_group, jointMap); 00428 } 00429 00430 } 00431 00432 //////////////////////////////////////////////////////////////////// 00433 // Function: BamToEgg::convert_character_node 00434 // Access: Private 00435 // Description: Converts the indicated Character to the corresponding 00436 // Egg constructs. 00437 //////////////////////////////////////////////////////////////////// 00438 void BamToEgg:: 00439 convert_character_node(Character *node, const WorkingNodePath &node_path, 00440 EggGroupNode *egg_parent, bool has_decal) { 00441 00442 // A sequence node gets converted to an ordinary EggGroup, we only apply 00443 // the appropriate switch attributes to turn it into a sequence 00444 EggGroup *egg_group = new EggGroup(node->get_name()); 00445 egg_group->set_dart_type(EggGroup::DT_default); 00446 egg_parent->add_child(egg_group); 00447 apply_node_properties(egg_group, node); 00448 00449 CharacterJointMap jointMap; 00450 00451 // turn it into a switch.. 00452 //egg_group->set_switch_flag(true); 00453 00454 int num_children = node->get_num_children(); 00455 int num_bundles = node->get_num_bundles(); 00456 00457 for (int i = 0; i < num_children; i++) { 00458 PandaNode *child = node->get_child(i); 00459 00460 if (child->is_geom_node()) { 00461 convert_geom_node(DCAST(GeomNode, child), WorkingNodePath(node_path, child), egg_group, has_decal, &jointMap); 00462 } 00463 } 00464 00465 for (int i = 0; i < num_bundles ; i++) { 00466 PartBundle *bundle= node->get_bundle(i); 00467 convert_character_bundle(bundle, egg_group, &jointMap); 00468 } 00469 00470 } 00471 00472 00473 //////////////////////////////////////////////////////////////////// 00474 // Function: BamToEgg::convert_collision_node 00475 // Access: Private 00476 // Description: Converts the indicated CollisionNode to the corresponding 00477 // Egg constructs. 00478 //////////////////////////////////////////////////////////////////// 00479 void BamToEgg:: 00480 convert_collision_node(CollisionNode *node, const WorkingNodePath &node_path, 00481 EggGroupNode *egg_parent, bool has_decal) { 00482 // A sequence node gets converted to an ordinary EggGroup, we only apply 00483 // the appropriate switch attributes to turn it into a sequence 00484 EggGroup *egg_group = new EggGroup(node->get_name()); 00485 egg_parent->add_child(egg_group); 00486 apply_node_properties(egg_group, node, false); 00487 00488 // turn it into a collision node 00489 egg_group->set_cs_type(EggGroup::CST_polyset); 00490 egg_group->set_collide_flags(EggGroup::CF_descend); 00491 00492 NodePath np = node_path.get_node_path(); 00493 CPT(TransformState) net_transform = np.get_net_transform(); 00494 LMatrix4 net_mat = net_transform->get_mat(); 00495 LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv()); 00496 net_mat = net_mat * inv; 00497 00498 int num_solids = node->get_num_solids(); 00499 00500 if (num_solids > 0) { 00501 // create vertex pool for collisions 00502 EggVertexPool *cvpool = new EggVertexPool("vpool-collision"); 00503 egg_group->add_child(cvpool); 00504 00505 // traverse solids 00506 for (int i = 0; i < num_solids; i++) { 00507 CPT(CollisionSolid) child = node->get_solid(i); 00508 if (child->is_of_type(CollisionPolygon::get_class_type())) { 00509 EggPolygon *egg_poly = new EggPolygon; 00510 egg_group->add_child(egg_poly); 00511 00512 CPT(CollisionPolygon) poly = DCAST(CollisionPolygon, child); 00513 int num_points = poly->get_num_points(); 00514 for (int j = 0; j < num_points; j++) { 00515 EggVertex egg_vert; 00516 egg_vert.set_pos(LCAST(double, poly->get_point(j) * net_mat)); 00517 egg_vert.set_normal(LCAST(double, poly->get_normal() * net_mat)); 00518 00519 EggVertex *new_egg_vert = cvpool->create_unique_vertex(egg_vert); 00520 egg_poly->add_vertex(new_egg_vert); 00521 } 00522 } else if (child->is_of_type(CollisionPlane::get_class_type())) { 00523 nout << "Encountered unhandled collsion type: CollisionPlane" << "\n"; 00524 } else if (child->is_of_type(CollisionSphere::get_class_type())) { 00525 nout << "Encountered unhandled collsion type: CollisionSphere" << "\n"; 00526 } else if (child->is_of_type(CollisionInvSphere::get_class_type())) { 00527 nout << "Encountered unhandled collsion type: CollisionInvSphere" << "\n"; 00528 } else if (child->is_of_type(CollisionTube::get_class_type())) { 00529 nout << "Encountered unhandled collsion type: CollisionTube" << "\n"; 00530 } else { 00531 nout << "Encountered unknown CollisionSolid" << "\n"; 00532 } 00533 } 00534 } 00535 00536 // recurse over children - hm. do I need to do this? 00537 recurse_nodes(node_path, egg_group, has_decal); 00538 } 00539 00540 //////////////////////////////////////////////////////////////////// 00541 // Function: BamToEgg::convert_geom_node 00542 // Access: Private 00543 // Description: Converts a GeomNode to the corresponding egg 00544 // structures. 00545 //////////////////////////////////////////////////////////////////// 00546 void BamToEgg:: 00547 convert_geom_node(GeomNode *node, const WorkingNodePath &node_path, 00548 EggGroupNode *egg_parent, bool has_decal, CharacterJointMap *jointMap) { 00549 PT(EggGroup) egg_group = new EggGroup(node->get_name()); 00550 bool fancy_attributes = apply_node_properties(egg_group, node); 00551 00552 if (node->get_effects()->has_decal()) { 00553 has_decal = true; 00554 } 00555 00556 if (has_decal) { 00557 egg_group->set_decal_flag(true); 00558 } 00559 00560 if (fancy_attributes || has_decal) { 00561 // If we have any fancy attributes on the node, or if we're making 00562 // decal geometry, we have to make a special node to hold the 00563 // geometry (normally it would just appear within its parent). 00564 egg_parent->add_child(egg_group.p()); 00565 egg_parent = egg_group; 00566 } 00567 00568 NodePath np = node_path.get_node_path(); 00569 CPT(RenderState) net_state = np.get_net_state(); 00570 CPT(TransformState) net_transform = np.get_net_transform(); 00571 LMatrix4 net_mat = net_transform->get_mat(); 00572 LMatrix4 inv = LCAST(PN_stdfloat, egg_parent->get_vertex_frame_inv()); 00573 net_mat = net_mat * inv; 00574 00575 // Now get out all the various kinds of geometry. 00576 int num_geoms = node->get_num_geoms(); 00577 for (int i = 0; i < num_geoms; ++i) { 00578 CPT(RenderState) geom_state = net_state->compose(node->get_geom_state(i)); 00579 00580 const Geom *geom = node->get_geom(i); 00581 int num_primitives = geom->get_num_primitives(); 00582 for (int j = 0; j < num_primitives; ++j) { 00583 const GeomPrimitive *primitive = geom->get_primitive(j); 00584 CPT(GeomPrimitive) simple = primitive->decompose(); 00585 CPT(GeomVertexData) vdata = geom->get_vertex_data(); 00586 // vdata = vdata->animate_vertices(true, Thread::get_current_thread()); 00587 convert_primitive(vdata, simple, geom_state, 00588 net_mat, egg_parent, jointMap); 00589 } 00590 } 00591 00592 recurse_nodes(node_path, egg_parent, has_decal); 00593 } 00594 00595 //////////////////////////////////////////////////////////////////// 00596 // Function: BamToEgg::convert_primitive 00597 // Access: Private 00598 // Description: 00599 //////////////////////////////////////////////////////////////////// 00600 void BamToEgg:: 00601 convert_primitive(const GeomVertexData *vertex_data, 00602 const GeomPrimitive *primitive, 00603 const RenderState *net_state, 00604 const LMatrix4 &net_mat, EggGroupNode *egg_parent, 00605 CharacterJointMap *jointMap) { 00606 GeomVertexReader reader(vertex_data); 00607 00608 // Check for a color scale. 00609 LVecBase4 color_scale(1.0f, 1.0f, 1.0f, 1.0f); 00610 const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, net_state->get_attrib(ColorScaleAttrib::get_class_type())); 00611 if (csa != (const ColorScaleAttrib *)NULL) { 00612 color_scale = csa->get_scale(); 00613 } 00614 00615 // Check for a color override. 00616 bool has_color_override = false; 00617 bool has_color_off = false; 00618 LColor color_override; 00619 const ColorAttrib *ca = DCAST(ColorAttrib, net_state->get_attrib(ColorAttrib::get_class_type())); 00620 if (ca != (const ColorAttrib *)NULL) { 00621 if (ca->get_color_type() == ColorAttrib::T_flat) { 00622 has_color_override = true; 00623 color_override = ca->get_color(); 00624 color_override.set(color_override[0] * color_scale[0], 00625 color_override[1] * color_scale[1], 00626 color_override[2] * color_scale[2], 00627 color_override[3] * color_scale[3]); 00628 00629 } else if (ca->get_color_type() == ColorAttrib::T_off) { 00630 has_color_off = true; 00631 } 00632 } 00633 00634 // Check for a texture. 00635 EggTexture *egg_tex = (EggTexture *)NULL; 00636 const TextureAttrib *ta = DCAST(TextureAttrib, net_state->get_attrib(TextureAttrib::get_class_type())); 00637 if (ta != (const TextureAttrib *)NULL) { 00638 egg_tex = get_egg_texture(ta->get_texture()); 00639 } 00640 00641 // Check the texture environment 00642 if ((ta != (const TextureAttrib *)NULL) && (egg_tex != (const EggTexture *)NULL)) { 00643 TextureStage* tex_stage = ta->get_on_stage(0); 00644 if (tex_stage != (const TextureStage *)NULL) { 00645 switch (tex_stage->get_mode()) { 00646 case TextureStage::M_modulate: 00647 if (has_color_off == true) { 00648 egg_tex->set_env_type(EggTexture::ET_replace); 00649 } else { 00650 egg_tex->set_env_type(EggTexture::ET_modulate); 00651 } 00652 break; 00653 case TextureStage::M_decal: 00654 egg_tex->set_env_type(EggTexture::ET_decal); 00655 break; 00656 case TextureStage::M_blend: 00657 egg_tex->set_env_type(EggTexture::ET_blend); 00658 break; 00659 case TextureStage::M_replace: 00660 egg_tex->set_env_type(EggTexture::ET_replace); 00661 break; 00662 case TextureStage::M_add: 00663 egg_tex->set_env_type(EggTexture::ET_add); 00664 break; 00665 case TextureStage::M_blend_color_scale: 00666 egg_tex->set_env_type(EggTexture::ET_blend_color_scale); 00667 break; 00668 default: 00669 break; 00670 } 00671 } 00672 } 00673 00674 // Check the backface flag. 00675 bool bface = false; 00676 const RenderAttrib *cf_attrib = net_state->get_attrib(CullFaceAttrib::get_class_type()); 00677 if (cf_attrib != (const RenderAttrib *)NULL) { 00678 const CullFaceAttrib *cfa = DCAST(CullFaceAttrib, cf_attrib); 00679 if (cfa->get_effective_mode() == CullFaceAttrib::M_cull_none) { 00680 bface = true; 00681 } 00682 } 00683 00684 // Check the depth write flag - only needed for AM_blend_no_occlude 00685 bool has_depthwrite = false; 00686 DepthWriteAttrib::Mode depthwrite = DepthWriteAttrib::M_on; 00687 const RenderAttrib *dw_attrib = net_state->get_attrib(DepthWriteAttrib::get_class_type()); 00688 if (dw_attrib != (const RenderAttrib *)NULL) { 00689 const DepthWriteAttrib *dwa = DCAST(DepthWriteAttrib, dw_attrib); 00690 depthwrite = dwa->get_mode(); 00691 has_depthwrite = true; 00692 } 00693 00694 // Check the transparency flag. 00695 bool has_transparency = false; 00696 TransparencyAttrib::Mode transparency = TransparencyAttrib::M_none; 00697 const RenderAttrib *tr_attrib = net_state->get_attrib(TransparencyAttrib::get_class_type()); 00698 if (tr_attrib != (const RenderAttrib *)NULL) { 00699 const TransparencyAttrib *tra = DCAST(TransparencyAttrib, tr_attrib); 00700 transparency = tra->get_mode(); 00701 has_transparency = true; 00702 } 00703 if (has_transparency && (egg_tex != (EggTexture *)NULL)) { 00704 EggRenderMode::AlphaMode tex_trans = EggRenderMode::AM_unspecified; 00705 switch (transparency) { 00706 case TransparencyAttrib::M_none: 00707 tex_trans = EggRenderMode::AM_off; 00708 break; 00709 case TransparencyAttrib::M_alpha: 00710 if (has_depthwrite && (depthwrite == DepthWriteAttrib::M_off)) { 00711 tex_trans = EggRenderMode::AM_blend_no_occlude; 00712 has_depthwrite = false; 00713 } else { 00714 tex_trans = EggRenderMode::AM_blend; 00715 } 00716 break; 00717 case TransparencyAttrib::M_multisample: 00718 tex_trans = EggRenderMode::AM_ms; 00719 break; 00720 case TransparencyAttrib::M_multisample_mask: 00721 tex_trans = EggRenderMode::AM_ms_mask; 00722 break; 00723 case TransparencyAttrib::M_binary: 00724 tex_trans = EggRenderMode::AM_binary; 00725 break; 00726 case TransparencyAttrib::M_dual: 00727 tex_trans = EggRenderMode::AM_dual; 00728 break; 00729 default: // intentional fall-through 00730 case TransparencyAttrib::M_notused: 00731 break; 00732 } 00733 if (tex_trans != EggRenderMode::AM_unspecified) { 00734 egg_tex->set_alpha_mode(tex_trans); 00735 } 00736 } 00737 00738 00739 LNormal normal; 00740 LColor color; 00741 CPT(TransformBlendTable) transformBlendTable = vertex_data->get_transform_blend_table(); 00742 00743 int num_primitives = primitive->get_num_primitives(); 00744 int num_vertices = primitive->get_num_vertices_per_primitive(); 00745 00746 EggPrimitive *(*make_func)(void); 00747 00748 if (primitive->is_of_type(GeomTriangles::get_class_type())) { 00749 make_func = make_egg_polygon; 00750 } else if (primitive->is_of_type(GeomPoints::get_class_type())) { 00751 make_func = make_egg_point; 00752 } else if (primitive->is_of_type(GeomLines::get_class_type())) { 00753 make_func = make_egg_line; 00754 } else { 00755 // Huh, an unknown geometry type. 00756 return; 00757 } 00758 00759 for (int i = 0; i < num_primitives; ++i) { 00760 PT(EggPrimitive) egg_prim = (*make_func)(); 00761 00762 egg_parent->add_child(egg_prim); 00763 if (egg_tex != (EggTexture *)NULL) { 00764 egg_prim->set_texture(egg_tex); 00765 } 00766 00767 if (bface) { 00768 egg_prim->set_bface_flag(true); 00769 } 00770 00771 for (int j = 0; j < num_vertices; j++) { 00772 EggVertex egg_vert; 00773 00774 // Get per-vertex properties. 00775 reader.set_row(primitive->get_vertex(i * num_vertices + j)); 00776 00777 reader.set_column(InternalName::get_vertex()); 00778 LVertex vertex = reader.get_data3(); 00779 egg_vert.set_pos(LCAST(double, vertex * net_mat)); 00780 00781 if (vertex_data->has_column(InternalName::get_normal())) { 00782 reader.set_column(InternalName::get_normal()); 00783 LNormal normal = reader.get_data3(); 00784 egg_vert.set_normal(LCAST(double, normal * net_mat)); 00785 } 00786 if (has_color_override) { 00787 egg_vert.set_color(color_override); 00788 00789 } else if (!has_color_off) { 00790 LColor color(1.0f, 1.0f, 1.0f, 1.0f); 00791 if (vertex_data->has_column(InternalName::get_color())) { 00792 reader.set_column(InternalName::get_color()); 00793 color = reader.get_data4(); 00794 } 00795 egg_vert.set_color(LColor(color[0] * color_scale[0], 00796 color[1] * color_scale[1], 00797 color[2] * color_scale[2], 00798 color[3] * color_scale[3])); 00799 } 00800 00801 if (vertex_data->has_column(InternalName::get_texcoord())) { 00802 reader.set_column(InternalName::get_texcoord()); 00803 LTexCoord uv = reader.get_data2(); 00804 egg_vert.set_uv(LCAST(double, uv)); 00805 } 00806 00807 EggVertex *new_egg_vert = _vpool->create_unique_vertex(egg_vert); 00808 00809 if ((vertex_data->has_column(InternalName::get_transform_blend())) && 00810 (jointMap!=NULL) && (transformBlendTable!=NULL)) { 00811 reader.set_column(InternalName::get_transform_blend()); 00812 int idx = reader.get_data1i(); 00813 const TransformBlend &blend = transformBlendTable->get_blend(idx); 00814 int num_weights = blend.get_num_transforms(); 00815 for (int k = 0; k < num_weights; ++k) { 00816 PN_stdfloat weight = blend.get_weight(k); 00817 if (weight!=0) { 00818 const VertexTransform *vertex_transform = blend.get_transform(k); 00819 if (vertex_transform->is_of_type(JointVertexTransform::get_class_type())) { 00820 const JointVertexTransform *joint_vertex_transform = DCAST(const JointVertexTransform, vertex_transform); 00821 00822 CharacterJointMap::iterator mi = jointMap->find(joint_vertex_transform->get_joint()); 00823 if (mi == jointMap->end()) { 00824 mi = jointMap->insert(CharacterJointMap::value_type(joint_vertex_transform->get_joint(), pvector<pair<EggVertex*,PN_stdfloat> >())).first; 00825 } 00826 pvector<pair<EggVertex*,PN_stdfloat> > &joint_vertices = (*mi).second; 00827 joint_vertices.push_back(pair<EggVertex*,PN_stdfloat>(new_egg_vert, weight)); 00828 } 00829 } 00830 } 00831 } 00832 00833 egg_prim->add_vertex(new_egg_vert); 00834 } 00835 } 00836 } 00837 00838 //////////////////////////////////////////////////////////////////// 00839 // Function: BamToEgg::recurse_nodes 00840 // Access: Private 00841 // Description: Converts all the children of the indicated node. 00842 //////////////////////////////////////////////////////////////////// 00843 void BamToEgg:: 00844 recurse_nodes(const WorkingNodePath &node_path, EggGroupNode *egg_parent, 00845 bool has_decal) { 00846 PandaNode *node = node_path.node(); 00847 int num_children = node->get_num_children(); 00848 00849 for (int i = 0; i < num_children; i++) { 00850 PandaNode *child = node->get_child(i); 00851 convert_node(WorkingNodePath(node_path, child), egg_parent, has_decal); 00852 } 00853 } 00854 00855 //////////////////////////////////////////////////////////////////// 00856 // Function: BamToEgg::apply_node_properties 00857 // Access: Public 00858 // Description: Applies any special properties that might be stored 00859 // on the node, like billboarding. Returns true if any 00860 // were applied, false otherwise. 00861 //////////////////////////////////////////////////////////////////// 00862 bool BamToEgg:: 00863 apply_node_properties(EggGroup *egg_group, PandaNode *node, bool allow_backstage) { 00864 bool any_applied = false; 00865 00866 if (node->is_overall_hidden() && allow_backstage) { 00867 // This node is hidden. We'll go ahead and convert it, but we'll 00868 // put in the "backstage" flag to mean it's not real geometry. 00869 // unless the caller wants to keep it (by setting allow_backstage to false) 00870 egg_group->add_object_type("backstage"); 00871 } 00872 00873 if (node->has_tags()) { 00874 if (apply_tags(egg_group, node)) { 00875 any_applied = true; 00876 } 00877 } 00878 00879 if (node->is_of_type(ModelNode::get_class_type())) { 00880 ModelNode *model_node = DCAST(ModelNode, node); 00881 switch (model_node->get_preserve_transform()) { 00882 case ModelNode::PT_none: 00883 case ModelNode::PT_drop_node: 00884 break; 00885 00886 case ModelNode::PT_net: 00887 egg_group->set_dcs_type(EggGroup::DC_net); 00888 break; 00889 00890 case ModelNode::PT_local: 00891 egg_group->set_dcs_type(EggGroup::DC_local); 00892 break; 00893 00894 case ModelNode::PT_no_touch: 00895 egg_group->set_dcs_type(EggGroup::DC_no_touch); 00896 break; 00897 } 00898 } 00899 00900 const RenderEffects *effects = node->get_effects(); 00901 const RenderEffect *effect = effects->get_effect(BillboardEffect::get_class_type()); 00902 if (effect != (RenderEffect *)NULL) { 00903 const BillboardEffect *bbe = DCAST(BillboardEffect, effect); 00904 if (bbe->get_axial_rotate()) { 00905 egg_group->set_billboard_type(EggGroup::BT_axis); 00906 any_applied = true; 00907 00908 } else if (bbe->get_eye_relative()) { 00909 egg_group->set_billboard_type(EggGroup::BT_point_camera_relative); 00910 any_applied = true; 00911 00912 } else { 00913 egg_group->set_billboard_type(EggGroup::BT_point_world_relative); 00914 any_applied = true; 00915 } 00916 } 00917 00918 const TransformState *transform = node->get_transform(); 00919 if (!transform->is_identity()) { 00920 if (transform->has_components()) { 00921 // If the transform can be represented componentwise, we prefer 00922 // storing it that way in the egg file. 00923 const LVecBase3 &scale = transform->get_scale(); 00924 const LQuaternion &quat = transform->get_quat(); 00925 const LVecBase3 &pos = transform->get_pos(); 00926 if (!scale.almost_equal(LVecBase3(1.0f, 1.0f, 1.0f))) { 00927 egg_group->add_scale3d(LCAST(double, scale)); 00928 } 00929 if (!quat.is_identity()) { 00930 egg_group->add_rotate3d(LCAST(double, quat)); 00931 } 00932 if (!pos.almost_equal(LVecBase3::zero())) { 00933 egg_group->add_translate3d(LCAST(double, pos)); 00934 } 00935 00936 } else if (transform->has_mat()) { 00937 // Otherwise, we store the raw matrix. 00938 const LMatrix4 &mat = transform->get_mat(); 00939 egg_group->set_transform3d(LCAST(double, mat)); 00940 } 00941 any_applied = true; 00942 } 00943 00944 return any_applied; 00945 } 00946 00947 //////////////////////////////////////////////////////////////////// 00948 // Function: BamToEgg::apply_tags 00949 // Access: Public 00950 // Description: Applies string tags to the egg file. Returns true if 00951 // any were applied, false otherwise. 00952 //////////////////////////////////////////////////////////////////// 00953 bool BamToEgg:: 00954 apply_tags(EggGroup *egg_group, PandaNode *node) { 00955 ostringstream strm; 00956 char delimiter = '\n'; 00957 string delimiter_str(1, delimiter); 00958 node->list_tags(strm, delimiter_str); 00959 00960 string data = strm.str(); 00961 if (data.empty()) { 00962 return false; 00963 } 00964 00965 bool any_applied = false; 00966 00967 size_t p = 0; 00968 size_t q = data.find(delimiter); 00969 while (q != string::npos) { 00970 string tag = data.substr(p, q); 00971 if (apply_tag(egg_group, node, tag)) { 00972 any_applied = true; 00973 } 00974 p = q + 1; 00975 q = data.find(delimiter, p); 00976 } 00977 00978 string tag = data.substr(p); 00979 if (apply_tag(egg_group, node, tag)) { 00980 any_applied = true; 00981 } 00982 00983 return any_applied; 00984 } 00985 00986 //////////////////////////////////////////////////////////////////// 00987 // Function: BamToEgg::apply_tag 00988 // Access: Public 00989 // Description: Applies the named string tags to the egg file. 00990 //////////////////////////////////////////////////////////////////// 00991 bool BamToEgg:: 00992 apply_tag(EggGroup *egg_group, PandaNode *node, const string &tag) { 00993 if (!node->has_tag(tag)) { 00994 return false; 00995 } 00996 00997 string value = node->get_tag(tag); 00998 egg_group->set_tag(tag, value); 00999 return true; 01000 } 01001 01002 //////////////////////////////////////////////////////////////////// 01003 // Function: BamToEgg::get_egg_texture 01004 // Access: Public 01005 // Description: Returns an EggTexture pointer that corresponds to the 01006 // indicated Texture. 01007 //////////////////////////////////////////////////////////////////// 01008 EggTexture *BamToEgg:: 01009 get_egg_texture(Texture *tex) { 01010 if (tex != (Texture *)NULL) { 01011 if (tex->has_filename()) { 01012 Filename filename = _path_replace->convert_path(tex->get_filename()); 01013 EggTexture temp(filename.get_basename_wo_extension(), filename); 01014 if (tex->has_alpha_filename()) { 01015 Filename alpha = _path_replace->convert_path(tex->get_alpha_filename()); 01016 temp.set_alpha_filename(alpha); 01017 } 01018 01019 switch (tex->get_minfilter()) { 01020 case Texture::FT_nearest: 01021 temp.set_minfilter(EggTexture::FT_nearest); 01022 break; 01023 case Texture::FT_linear: 01024 temp.set_minfilter(EggTexture::FT_linear); 01025 break; 01026 case Texture::FT_nearest_mipmap_nearest: 01027 temp.set_minfilter(EggTexture::FT_nearest_mipmap_nearest); 01028 break; 01029 case Texture::FT_linear_mipmap_nearest: 01030 temp.set_minfilter(EggTexture::FT_linear_mipmap_nearest); 01031 break; 01032 case Texture::FT_nearest_mipmap_linear: 01033 temp.set_minfilter(EggTexture::FT_nearest_mipmap_linear); 01034 break; 01035 case Texture::FT_linear_mipmap_linear: 01036 temp.set_minfilter(EggTexture::FT_linear_mipmap_linear); 01037 break; 01038 01039 default: 01040 break; 01041 } 01042 01043 switch (tex->get_magfilter()) { 01044 case Texture::FT_nearest: 01045 temp.set_magfilter(EggTexture::FT_nearest); 01046 break; 01047 case Texture::FT_linear: 01048 temp.set_magfilter(EggTexture::FT_linear); 01049 break; 01050 01051 default: 01052 break; 01053 } 01054 01055 switch (tex->get_wrap_u()) { 01056 case Texture::WM_clamp: 01057 temp.set_wrap_u(EggTexture::WM_clamp); 01058 break; 01059 case Texture::WM_repeat: 01060 temp.set_wrap_u(EggTexture::WM_repeat); 01061 break; 01062 01063 default: 01064 // There are some new wrap options on Texture that aren't yet 01065 // supported in egg. 01066 break; 01067 } 01068 01069 switch (tex->get_wrap_v()) { 01070 case Texture::WM_clamp: 01071 temp.set_wrap_v(EggTexture::WM_clamp); 01072 break; 01073 case Texture::WM_repeat: 01074 temp.set_wrap_v(EggTexture::WM_repeat); 01075 break; 01076 01077 default: 01078 // There are some new wrap options on Texture that aren't yet 01079 // supported in egg. 01080 break; 01081 } 01082 01083 switch (tex->get_format()) { 01084 case Texture::F_red: 01085 temp.set_format(EggTexture::F_red); 01086 break; 01087 case Texture::F_green: 01088 temp.set_format(EggTexture::F_green); 01089 break; 01090 case Texture::F_blue: 01091 temp.set_format(EggTexture::F_blue); 01092 break; 01093 case Texture::F_alpha: 01094 temp.set_format(EggTexture::F_alpha); 01095 break; 01096 case Texture::F_rgb: 01097 temp.set_format(EggTexture::F_rgb); 01098 break; 01099 case Texture::F_rgb5: 01100 temp.set_format(EggTexture::F_rgb5); 01101 break; 01102 case Texture::F_rgb8: 01103 temp.set_format(EggTexture::F_rgb8); 01104 break; 01105 case Texture::F_rgb12: 01106 temp.set_format(EggTexture::F_rgb12); 01107 break; 01108 case Texture::F_rgb332: 01109 temp.set_format(EggTexture::F_rgb332); 01110 break; 01111 case Texture::F_rgba: 01112 temp.set_format(EggTexture::F_rgba); 01113 break; 01114 case Texture::F_rgbm: 01115 temp.set_format(EggTexture::F_rgbm); 01116 break; 01117 case Texture::F_rgba4: 01118 temp.set_format(EggTexture::F_rgba4); 01119 break; 01120 case Texture::F_rgba5: 01121 temp.set_format(EggTexture::F_rgba5); 01122 break; 01123 case Texture::F_rgba8: 01124 temp.set_format(EggTexture::F_rgba8); 01125 break; 01126 case Texture::F_rgba12: 01127 temp.set_format(EggTexture::F_rgba12); 01128 break; 01129 case Texture::F_luminance: 01130 temp.set_format(EggTexture::F_luminance); 01131 break; 01132 case Texture::F_luminance_alpha: 01133 temp.set_format(EggTexture::F_luminance_alpha); 01134 break; 01135 case Texture::F_luminance_alphamask: 01136 temp.set_format(EggTexture::F_luminance_alphamask); 01137 break; 01138 default: 01139 break; 01140 } 01141 01142 return _textures.create_unique_texture(temp, ~EggTexture::E_tref_name); 01143 } 01144 } 01145 01146 return NULL; 01147 } 01148 01149 //////////////////////////////////////////////////////////////////// 01150 // Function: BamToEgg::make_egg_polygon 01151 // Access: Public, Static 01152 // Description: A factory function to make a new EggPolygon instance. 01153 //////////////////////////////////////////////////////////////////// 01154 EggPrimitive *BamToEgg:: 01155 make_egg_polygon() { 01156 return new EggPolygon; 01157 } 01158 01159 //////////////////////////////////////////////////////////////////// 01160 // Function: BamToEgg::make_egg_point 01161 // Access: Public, Static 01162 // Description: A factory function to make a new EggPoint instance. 01163 //////////////////////////////////////////////////////////////////// 01164 EggPrimitive *BamToEgg:: 01165 make_egg_point() { 01166 return new EggPoint; 01167 } 01168 01169 //////////////////////////////////////////////////////////////////// 01170 // Function: BamToEgg::make_egg_line 01171 // Access: Public, Static 01172 // Description: A factory function to make a new EggLine instance. 01173 //////////////////////////////////////////////////////////////////// 01174 EggPrimitive *BamToEgg:: 01175 make_egg_line() { 01176 return new EggLine; 01177 } 01178 01179 int main(int argc, char *argv[]) { 01180 // A call to pystub() to force libpystub.so to be linked in. 01181 pystub(); 01182 01183 BamToEgg prog; 01184 prog.parse_command_line(argc, argv); 01185 prog.run(); 01186 return 0; 01187 }