Panda3D

bamToEgg.cxx

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 }
 All Classes Functions Variables Enumerations