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