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