Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/egg2pg/eggLoader.cxx

Go to the documentation of this file.
00001 // Filename: EggLoader.cxx
00002 // Created by:  drose (26Feb02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "pandabase.h"
00020 
00021 #include "eggLoader.h"
00022 #include "config_egg2pg.h"
00023 #include "nodePath.h"
00024 #include "renderState.h"
00025 #include "transformState.h"
00026 #include "textureAttrib.h"
00027 #include "textureApplyAttrib.h"
00028 #include "texturePool.h"
00029 #include "billboardEffect.h"
00030 #include "cullFaceAttrib.h"
00031 #include "cullBinAttrib.h"
00032 #include "transparencyAttrib.h"
00033 #include "decalEffect.h"
00034 #include "depthTestAttrib.h"
00035 #include "depthWriteAttrib.h"
00036 #include "materialAttrib.h"
00037 #include "materialPool.h"
00038 #include "geomNode.h"
00039 #include "sequenceNode.h"
00040 #include "switchNode.h"
00041 #include "lodNode.h"
00042 #include "modelNode.h"
00043 #include "modelRoot.h"
00044 #include "string_utils.h"
00045 #include "eggPrimitive.h"
00046 #include "eggPoint.h"
00047 #include "eggTextureCollection.h"
00048 #include "eggNurbsCurve.h"
00049 #include "eggGroupNode.h"
00050 #include "eggGroup.h"
00051 #include "eggPolygon.h"
00052 #include "eggBin.h"
00053 #include "eggTable.h"
00054 #include "eggBinner.h"
00055 #include "eggVertexPool.h"
00056 #include "characterMaker.h"
00057 #include "character.h"
00058 #include "animBundleMaker.h"
00059 #include "animBundleNode.h"
00060 #include "selectiveChildNode.h"
00061 #include "collisionNode.h"
00062 #include "collisionSphere.h"
00063 #include "collisionPlane.h"
00064 #include "collisionPolygon.h"
00065 #include "parametricCurve.h"
00066 #include "nurbsCurve.h"
00067 #include "classicNurbsCurve.h"
00068 #include "nurbsCurveInterface.h"
00069 
00070 #include <ctype.h>
00071 #include <algorithm>
00072 
00073 // This class is used in make_node(EggBin *) to sort LOD instances in
00074 // order by switching distance.
00075 class LODInstance {
00076 public:
00077   LODInstance(EggNode *egg_node);
00078   bool operator < (const LODInstance &other) const {
00079     return _d->_switch_in < other._d->_switch_in;
00080   }
00081 
00082   EggNode *_egg_node;
00083   const EggSwitchConditionDistance *_d;
00084 };
00085 
00086 LODInstance::
00087 LODInstance(EggNode *egg_node) {
00088   nassertv(egg_node != NULL);
00089   _egg_node = egg_node;
00090 
00091   // We expect this egg node to be an EggGroup with an LOD
00092   // specification.  That's what the EggBinner collected together,
00093   // after all.
00094   EggGroup *egg_group = DCAST(EggGroup, egg_node);
00095   nassertv(egg_group->has_lod());
00096   const EggSwitchCondition &sw = egg_group->get_lod();
00097 
00098   // For now, this is the only kind of switch condition there is.
00099   _d = DCAST(EggSwitchConditionDistance, &sw);
00100 }
00101 
00102 
00103 ////////////////////////////////////////////////////////////////////
00104 //     Function: EggLoader::Constructor
00105 //       Access: Public
00106 //  Description:
00107 ////////////////////////////////////////////////////////////////////
00108 EggLoader::
00109 EggLoader() {
00110   // We need to enforce whatever coordinate system the user asked for.
00111   _data.set_coordinate_system(egg_coordinate_system);
00112   _error = false;
00113 }
00114 
00115 ////////////////////////////////////////////////////////////////////
00116 //     Function: EggLoader::Constructor
00117 //       Access: Public
00118 //  Description:
00119 ////////////////////////////////////////////////////////////////////
00120 EggLoader::
00121 EggLoader(const EggData &data) :
00122   _data(data)
00123 {
00124   _error = false;
00125 }
00126 
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: EggLoader::build_graph
00130 //       Access: Public
00131 //  Description:
00132 ////////////////////////////////////////////////////////////////////
00133 void EggLoader::
00134 build_graph() {
00135   _deferred_nodes.clear();
00136 
00137   // First, bin up the LOD nodes.
00138   EggBinner binner;
00139   binner.make_bins(&_data);
00140 
00141   // Then load up all of the textures.
00142   load_textures();
00143 
00144   // Now build up the scene graph.
00145   _root = new ModelRoot(_data.get_egg_filename().get_basename());
00146   make_node(&_data, _root);
00147   _builder.build();
00148 
00149   reparent_decals();
00150 
00151   apply_deferred_nodes(_root, DeferredNodeProperty());
00152 }
00153 
00154 
00155 ////////////////////////////////////////////////////////////////////
00156 //     Function: EggLoader::reparent_decals
00157 //       Access: Public
00158 //  Description: For each node representing a decal base geometry
00159 //               (i.e. a node corresponding to an EggGroup with the
00160 //               decal flag set), move all of its nested geometry
00161 //               directly below the GeomNode representing the group.
00162 ////////////////////////////////////////////////////////////////////
00163 void EggLoader::
00164 reparent_decals() {
00165   Decals::const_iterator di;
00166   for (di = _decals.begin(); di != _decals.end(); ++di) {
00167     PandaNode *node = (*di);
00168     nassertv(node != (PandaNode *)NULL);
00169 
00170     // The NodePath interface is best for this.
00171     NodePath parent(node);
00172 
00173     // First, search for the GeomNode.
00174     NodePath geom_parent;
00175     int num_children = parent.get_num_children();
00176     for (int i = 0; i < num_children; i++) {
00177       NodePath child = parent.get_child(i);
00178 
00179       if (child.node()->is_of_type(GeomNode::get_class_type())) {
00180         if (!geom_parent.is_empty()) {
00181           // Oops, too many GeomNodes.
00182           egg2pg_cat.error()
00183             << "Decal onto " << parent.node()->get_name()
00184             << " uses base geometry with multiple GeomNodes.\n";
00185           _error = true;
00186         }
00187         geom_parent = child;
00188       }
00189     }
00190 
00191     if (geom_parent.is_empty()) {
00192       // No children were GeomNodes.
00193       egg2pg_cat.error()
00194         << "Ignoring decal onto " << parent.node()->get_name()
00195         << "; no geometry within group.\n";
00196       _error = true;
00197     } else {
00198       // Now reparent all of the non-GeomNodes to this node.  We have
00199       // to be careful so we don't get lost as we self-modify this
00200       // list.
00201       int i = 0;
00202       while (i < num_children) {
00203         NodePath child = parent.get_child(i);
00204 
00205         if (child.node()->is_of_type(GeomNode::get_class_type())) {
00206           i++;
00207         } else {
00208           child.reparent_to(geom_parent);
00209           num_children--;
00210         }
00211       }
00212 
00213       // Finally, set the DecalEffect on the base geometry.
00214       geom_parent.node()->set_effect(DecalEffect::make());
00215     }
00216   }
00217 }
00218 
00219 
00220 ////////////////////////////////////////////////////////////////////
00221 //     Function: EggLoader::make_nonindexed_primitive
00222 //       Access: Public
00223 //  Description:
00224 ////////////////////////////////////////////////////////////////////
00225 void EggLoader::
00226 make_nonindexed_primitive(EggPrimitive *egg_prim, PandaNode *parent,
00227                           const LMatrix4d *transform) {
00228   BuilderBucket bucket;
00229   setup_bucket(bucket, parent, egg_prim);
00230 
00231   LMatrix4d mat;
00232 
00233   if (transform != NULL) {
00234     mat = (*transform);
00235   } else {
00236     mat = egg_prim->get_vertex_to_node();
00237   }
00238 
00239   BuilderPrim bprim;
00240   bprim.set_type(BPT_poly);
00241   if (egg_prim->is_of_type(EggPoint::get_class_type())) {
00242     bprim.set_type(BPT_point);
00243   }
00244 
00245   if (egg_prim->has_normal()) {
00246     Normald norm = egg_prim->get_normal() * mat;
00247     norm.normalize();
00248     bprim.set_normal(LCAST(float, norm));
00249   }
00250   if (egg_prim->has_color() && !egg_false_color) {
00251     bprim.set_color(egg_prim->get_color());
00252   }
00253 
00254   bool has_vert_color = true;
00255   EggPrimitive::const_iterator vi;
00256   for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
00257     EggVertex *egg_vert = *vi;
00258     if (egg_vert->get_num_dimensions() != 3) {
00259       egg2pg_cat.error()
00260         << "Vertex " << egg_vert->get_pool()->get_name() 
00261         << ":" << egg_vert->get_index() << " has dimension " 
00262         << egg_vert->get_num_dimensions() << "\n";
00263     } else {
00264       BuilderVertex bvert(LCAST(float, egg_vert->get_pos3() * mat));
00265 
00266       if (egg_vert->has_normal()) {
00267         Normald norm = egg_vert->get_normal() * mat;
00268         norm.normalize();
00269         bvert.set_normal(LCAST(float, norm));
00270       }
00271       if (egg_vert->has_color() && !egg_false_color) {
00272         bvert.set_color(egg_vert->get_color());
00273       } else {
00274         // If any vertex doesn't have a color, we can't use any of the
00275         // vertex colors.
00276         has_vert_color = false;
00277       }
00278       if (egg_vert->has_uv()) {
00279         TexCoordd uv = egg_vert->get_uv();
00280         if (egg_prim->has_texture() &&
00281             egg_prim->get_texture()->has_transform()) {
00282           // If we have a texture matrix, apply it.
00283           uv = uv * egg_prim->get_texture()->get_transform();
00284         }
00285         bvert.set_texcoord(LCAST(float, uv));
00286       }
00287     
00288       bprim.add_vertex(bvert);
00289     }
00290   }
00291 
00292   // Finally, if the primitive didn't have a color, and it didn't have
00293   // vertex color, make it white.
00294   if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) {
00295     bprim.set_color(Colorf(1.0, 1.0, 1.0, 1.0));
00296   }
00297 
00298   _builder.add_prim(bucket, bprim);
00299 }
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: EggLoader::make_indexed_primitive
00303 //       Access: Public
00304 //  Description:
00305 ////////////////////////////////////////////////////////////////////
00306 void EggLoader::
00307 make_indexed_primitive(EggPrimitive *egg_prim, PandaNode *parent,
00308                        const LMatrix4d *transform,
00309                        ComputedVerticesMaker &_comp_verts_maker) {
00310   BuilderBucket bucket;
00311   setup_bucket(bucket, parent, egg_prim);
00312 
00313   bucket.set_coords(_comp_verts_maker._coords);
00314   bucket.set_normals(_comp_verts_maker._norms);
00315   bucket.set_texcoords(_comp_verts_maker._texcoords);
00316   bucket.set_colors(_comp_verts_maker._colors);
00317 
00318   LMatrix4d mat;
00319 
00320   if (transform != NULL) {
00321     mat = (*transform);
00322   } else {
00323     mat = egg_prim->get_vertex_to_node();
00324   }
00325 
00326   BuilderPrimI bprim;
00327   bprim.set_type(BPT_poly);
00328   if (egg_prim->is_of_type(EggPoint::get_class_type())) {
00329     bprim.set_type(BPT_point);
00330   }
00331 
00332   if (egg_prim->has_normal()) {
00333     // Define the transform space of the polygon normal.  This will be
00334     // the average of all the vertex transform spaces.
00335     _comp_verts_maker.begin_new_space();
00336     EggPrimitive::const_iterator vi;
00337     for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
00338       EggVertex *egg_vert = *vi;
00339       _comp_verts_maker.add_vertex_joints(egg_vert, egg_prim);
00340     }
00341     _comp_verts_maker.mark_space();
00342 
00343     int nindex =
00344       _comp_verts_maker.add_normal(egg_prim->get_normal(),
00345                                    egg_prim->_dnormals, mat);
00346 
00347     bprim.set_normal(nindex);
00348   }
00349 
00350   if (egg_prim->has_color() && !egg_false_color) {
00351     int cindex =
00352       _comp_verts_maker.add_color(egg_prim->get_color(),
00353                                   egg_prim->_drgbas);
00354     bprim.set_color(cindex);
00355   }
00356 
00357   bool has_vert_color = true;
00358   EggPrimitive::const_iterator vi;
00359   for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
00360     EggVertex *egg_vert = *vi;
00361 
00362     if (egg_vert->get_num_dimensions() != 3) {
00363       egg2pg_cat.error()
00364         << "Vertex " << egg_vert->get_pool()->get_name() 
00365         << ":" << egg_vert->get_index() << " has dimension " 
00366         << egg_vert->get_num_dimensions() << "\n";
00367     } else {
00368       // Set up the ComputedVerticesMaker for the coordinate space of
00369       // the vertex.
00370       _comp_verts_maker.begin_new_space();
00371       _comp_verts_maker.add_vertex_joints(egg_vert, egg_prim);
00372       _comp_verts_maker.mark_space();
00373       
00374       int vindex =
00375         _comp_verts_maker.add_vertex(egg_vert->get_pos3(),
00376                                      egg_vert->_dxyzs, mat);
00377       BuilderVertexI bvert(vindex);
00378       
00379       if (egg_vert->has_normal()) {
00380         int nindex =
00381           _comp_verts_maker.add_normal(egg_vert->get_normal(),
00382                                        egg_vert->_dnormals,
00383                                        mat);
00384         bvert.set_normal(nindex);
00385       }
00386       
00387       if (egg_vert->has_color() && !egg_false_color) {
00388         int cindex =
00389           _comp_verts_maker.add_color(egg_vert->get_color(),
00390                                       egg_vert->_drgbas);
00391         bvert.set_color(cindex);
00392       } else {
00393         // If any vertex doesn't have a color, we can't use any of the
00394         // vertex colors.
00395         has_vert_color = false;
00396       }
00397 
00398       if (egg_vert->has_uv()) {
00399         TexCoordd uv = egg_vert->get_uv();
00400         LMatrix3d mat;
00401         
00402         if (egg_prim->has_texture() &&
00403             egg_prim->get_texture()->has_transform()) {
00404           // If we have a texture matrix, apply it.
00405           mat = egg_prim->get_texture()->get_transform();
00406         } else {
00407           mat = LMatrix3d::ident_mat();
00408         }
00409         
00410         int tindex =
00411           _comp_verts_maker.add_texcoord(uv, egg_vert->_duvs, mat);
00412         bvert.set_texcoord(tindex);
00413       }
00414       
00415       bprim.add_vertex(bvert);
00416     }
00417   }
00418 
00419   // Finally, if the primitive didn't have a color, and it didn't have
00420   // vertex color, make it white.
00421   if (!egg_prim->has_color() && !has_vert_color && !egg_false_color) {
00422     int cindex =
00423       _comp_verts_maker.add_color(Colorf(1.0, 1.0, 1.0, 1.0),
00424                                   EggMorphColorList());
00425     bprim.set_color(cindex);
00426   }
00427 
00428   _builder.add_prim(bucket, bprim);
00429 }
00430 
00431 ////////////////////////////////////////////////////////////////////
00432 //     Function: EggLoader::load_textures
00433 //       Access: Private
00434 //  Description:
00435 ////////////////////////////////////////////////////////////////////
00436 void EggLoader::
00437 load_textures() {
00438   // First, collect all the textures that are referenced.
00439   EggTextureCollection tc;
00440   tc.find_used_textures(&_data);
00441 
00442   // Collapse the textures down by filename only.  Should we also
00443   // differentiate by attributes?  Maybe.
00444   EggTextureCollection::TextureReplacement replace;
00445   tc.collapse_equivalent_textures(EggTexture::E_complete_filename,
00446                                   replace);
00447 
00448   EggTextureCollection::iterator ti;
00449   for (ti = tc.begin(); ti != tc.end(); ++ti) {
00450     PT(EggTexture) egg_tex = (*ti);
00451 
00452     TextureDef def;
00453     if (load_texture(def, egg_tex)) {
00454       // Now associate the pointers, so we'll be able to look up the
00455       // Texture pointer given an EggTexture pointer, later.
00456       _textures[egg_tex] = def;
00457     }
00458   }
00459 
00460   // Finally, associate all of the removed texture references back to
00461   // the same pointers as the others.
00462   EggTextureCollection::TextureReplacement::const_iterator ri;
00463   for (ri = replace.begin(); ri != replace.end(); ++ri) {
00464     PT(EggTexture) orig = (*ri).first;
00465     PT(EggTexture) repl = (*ri).second;
00466 
00467     _textures[orig] = _textures[repl];
00468   }
00469 }
00470 
00471 
00472 ////////////////////////////////////////////////////////////////////
00473 //     Function: EggLoader::load_texture
00474 //       Access: Private
00475 //  Description:
00476 ////////////////////////////////////////////////////////////////////
00477 bool EggLoader::
00478 load_texture(TextureDef &def, const EggTexture *egg_tex) {
00479   // Check to see if we should reduce the number of channels in
00480   // the texture.
00481   int wanted_channels = 0;
00482   bool wanted_alpha = false;
00483   switch (egg_tex->get_format()) {
00484   case EggTexture::F_red:
00485   case EggTexture::F_green:
00486   case EggTexture::F_blue:
00487   case EggTexture::F_alpha:
00488   case EggTexture::F_luminance:
00489     wanted_channels = 1;
00490     wanted_alpha = false;
00491     break;
00492 
00493   case EggTexture::F_luminance_alpha:
00494   case EggTexture::F_luminance_alphamask:
00495     wanted_channels = 2;
00496     wanted_alpha = true;
00497     break;
00498 
00499   case EggTexture::F_rgb:
00500   case EggTexture::F_rgb12:
00501   case EggTexture::F_rgb8:
00502   case EggTexture::F_rgb5:
00503   case EggTexture::F_rgb332:
00504     wanted_channels = 3;
00505     wanted_alpha = false;
00506     break;
00507 
00508   case EggTexture::F_rgba:
00509   case EggTexture::F_rgbm:
00510   case EggTexture::F_rgba12:
00511   case EggTexture::F_rgba8:
00512   case EggTexture::F_rgba4:
00513   case EggTexture::F_rgba5:
00514     wanted_channels = 4;
00515     wanted_alpha = true;
00516     break;
00517 
00518   case EggTexture::F_unspecified:
00519     break;
00520   }
00521 
00522   Texture *tex;
00523   if (egg_tex->has_alpha_filename() && wanted_alpha) {
00524     tex = TexturePool::load_texture(egg_tex->get_fullpath(),
00525                                     egg_tex->get_alpha_fullpath(),
00526                                     wanted_channels,
00527                                     egg_tex->get_alpha_file_channel());
00528   } else {
00529     tex = TexturePool::load_texture(egg_tex->get_fullpath(),
00530                                     wanted_channels);
00531   }
00532   if (tex == (Texture *)NULL) {
00533     return false;
00534   }
00535 
00536   // Record the original filenames in the textures (as loaded from the
00537   // egg file).  These filenames will be written back to the bam file
00538   // if the bam file is written out.
00539   tex->set_filename(egg_tex->get_filename());
00540   if (egg_tex->has_alpha_filename() && wanted_alpha) {
00541     tex->set_alpha_filename(egg_tex->get_alpha_filename());
00542   }
00543 
00544   apply_texture_attributes(tex, egg_tex);
00545   CPT(RenderAttrib) apply = get_texture_apply_attributes(egg_tex);
00546 
00547   def._texture = TextureAttrib::make(tex);
00548   def._apply = apply;
00549 
00550   return true;
00551 }
00552 
00553 
00554 ////////////////////////////////////////////////////////////////////
00555 //     Function: EggLoader::apply_texture_attributes
00556 //       Access: Private
00557 //  Description:
00558 ////////////////////////////////////////////////////////////////////
00559 void EggLoader::
00560 apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
00561   switch (egg_tex->determine_wrap_u()) {
00562   case EggTexture::WM_repeat:
00563     tex->set_wrapu(Texture::WM_repeat);
00564     break;
00565 
00566   case EggTexture::WM_clamp:
00567     if (egg_ignore_clamp) {
00568       egg2pg_cat.warning()
00569         << "Ignoring clamp request\n";
00570       tex->set_wrapu(Texture::WM_repeat);
00571     } else {
00572       tex->set_wrapu(Texture::WM_clamp);
00573     }
00574     break;
00575 
00576   case EggTexture::WM_unspecified:
00577     break;
00578 
00579   default:
00580     egg2pg_cat.warning()
00581       << "Unexpected texture wrap flag: "
00582       << (int)egg_tex->determine_wrap_u() << "\n";
00583   }
00584 
00585   switch (egg_tex->determine_wrap_v()) {
00586   case EggTexture::WM_repeat:
00587     tex->set_wrapv(Texture::WM_repeat);
00588     break;
00589 
00590   case EggTexture::WM_clamp:
00591     if (egg_ignore_clamp) {
00592       egg2pg_cat.warning()
00593         << "Ignoring clamp request\n";
00594       tex->set_wrapv(Texture::WM_repeat);
00595     } else {
00596       tex->set_wrapv(Texture::WM_clamp);
00597     }
00598     break;
00599 
00600   case EggTexture::WM_unspecified:
00601     break;
00602 
00603   default:
00604     egg2pg_cat.warning()
00605       << "Unexpected texture wrap flag: "
00606       << (int)egg_tex->determine_wrap_v() << "\n";
00607   }
00608 
00609   switch (egg_tex->get_minfilter()) {
00610   case EggTexture::FT_nearest:
00611     tex->set_minfilter(Texture::FT_nearest);
00612     break;
00613 
00614   case EggTexture::FT_linear:
00615     if (egg_ignore_filters) {
00616       egg2pg_cat.warning()
00617         << "Ignoring minfilter request\n";
00618       tex->set_minfilter(Texture::FT_nearest);
00619     } else {
00620       tex->set_minfilter(Texture::FT_linear);
00621     }
00622     break;
00623 
00624   case EggTexture::FT_nearest_mipmap_nearest:
00625     if (egg_ignore_filters) {
00626       egg2pg_cat.warning()
00627         << "Ignoring minfilter request\n";
00628       tex->set_minfilter(Texture::FT_nearest);
00629     } else if (egg_ignore_mipmaps) {
00630       egg2pg_cat.warning()
00631         << "Ignoring mipmap request\n";
00632       tex->set_minfilter(Texture::FT_nearest);
00633     } else {
00634       tex->set_minfilter(Texture::FT_nearest_mipmap_nearest);
00635     }
00636     break;
00637 
00638   case EggTexture::FT_linear_mipmap_nearest:
00639     if (egg_ignore_filters) {
00640       egg2pg_cat.warning()
00641         << "Ignoring minfilter request\n";
00642       tex->set_minfilter(Texture::FT_nearest);
00643     } else if (egg_ignore_mipmaps) {
00644       egg2pg_cat.warning()
00645         << "Ignoring mipmap request\n";
00646       tex->set_minfilter(Texture::FT_linear);
00647     } else {
00648       tex->set_minfilter(Texture::FT_linear_mipmap_nearest);
00649     }
00650     break;
00651 
00652   case EggTexture::FT_nearest_mipmap_linear:
00653     if (egg_ignore_filters) {
00654       egg2pg_cat.warning()
00655         << "Ignoring minfilter request\n";
00656       tex->set_minfilter(Texture::FT_nearest);
00657     } else if (egg_ignore_mipmaps) {
00658       egg2pg_cat.warning()
00659         << "Ignoring mipmap request\n";
00660       tex->set_minfilter(Texture::FT_nearest);
00661     } else {
00662       tex->set_minfilter(Texture::FT_nearest_mipmap_linear);
00663     }
00664     break;
00665 
00666   case EggTexture::FT_linear_mipmap_linear:
00667     if (egg_ignore_filters) {
00668       egg2pg_cat.warning()
00669         << "Ignoring minfilter request\n";
00670       tex->set_minfilter(Texture::FT_nearest);
00671     } else if (egg_ignore_mipmaps) {
00672       egg2pg_cat.warning()
00673         << "Ignoring mipmap request\n";
00674       tex->set_minfilter(Texture::FT_linear);
00675     } else {
00676       tex->set_minfilter(Texture::FT_linear_mipmap_linear);
00677     }
00678     break;
00679 
00680   case EggTexture::FT_unspecified:
00681     // Default is bilinear, unless egg_ignore_filters is specified.
00682     if (egg_ignore_filters) {
00683       tex->set_minfilter(Texture::FT_nearest);
00684     } else {
00685       tex->set_minfilter(Texture::FT_linear);
00686     }
00687   }
00688 
00689   switch (egg_tex->get_magfilter()) {
00690   case EggTexture::FT_nearest:
00691   case EggTexture::FT_nearest_mipmap_nearest:
00692   case EggTexture::FT_nearest_mipmap_linear:
00693     tex->set_magfilter(Texture::FT_nearest);
00694     break;
00695 
00696   case EggTexture::FT_linear:
00697   case EggTexture::FT_linear_mipmap_nearest:
00698   case EggTexture::FT_linear_mipmap_linear:
00699     if (egg_ignore_filters) {
00700       egg2pg_cat.warning()
00701         << "Ignoring magfilter request\n";
00702       tex->set_magfilter(Texture::FT_nearest);
00703     } else {
00704       tex->set_magfilter(Texture::FT_linear);
00705     }
00706     break;
00707 
00708   case EggTexture::FT_unspecified:
00709     // Default is bilinear, unless egg_ignore_filters is specified.
00710     if (egg_ignore_filters) {
00711       tex->set_magfilter(Texture::FT_nearest);
00712     } else {
00713       tex->set_magfilter(Texture::FT_linear);
00714     }
00715   }
00716 
00717   if (egg_tex->has_anisotropic_degree()) {
00718     tex->set_anisotropic_degree(egg_tex->get_anisotropic_degree());
00719   }
00720 
00721   if (tex->_pbuffer->get_num_components() == 1) {
00722     switch (egg_tex->get_format()) {
00723     case EggTexture::F_red:
00724       tex->_pbuffer->set_format(PixelBuffer::F_red);
00725       break;
00726     case EggTexture::F_green:
00727       tex->_pbuffer->set_format(PixelBuffer::F_green);
00728       break;
00729     case EggTexture::F_blue:
00730       tex->_pbuffer->set_format(PixelBuffer::F_blue);
00731       break;
00732     case EggTexture::F_alpha:
00733       tex->_pbuffer->set_format(PixelBuffer::F_alpha);
00734       break;
00735     case EggTexture::F_luminance:
00736       tex->_pbuffer->set_format(PixelBuffer::F_luminance);
00737       break;
00738 
00739     case EggTexture::F_unspecified:
00740       break;
00741 
00742     default:
00743       egg2pg_cat.warning()
00744         << "Ignoring inappropriate format " << egg_tex->get_format()
00745         << " for 1-component texture " << egg_tex->get_name() << "\n";
00746     }
00747 
00748   } else if (tex->_pbuffer->get_num_components() == 2) {
00749     switch (egg_tex->get_format()) {
00750     case EggTexture::F_luminance_alpha:
00751       tex->_pbuffer->set_format(PixelBuffer::F_luminance_alpha);
00752       break;
00753 
00754     case EggTexture::F_luminance_alphamask:
00755       tex->_pbuffer->set_format(PixelBuffer::F_luminance_alphamask);
00756       break;
00757 
00758     case EggTexture::F_unspecified:
00759       break;
00760 
00761     default:
00762       egg2pg_cat.warning()
00763         << "Ignoring inappropriate format " << egg_tex->get_format()
00764         << " for 2-component texture " << egg_tex->get_name() << "\n";
00765     }
00766 
00767   } else if (tex->_pbuffer->get_num_components() == 3) {
00768     switch (egg_tex->get_format()) {
00769     case EggTexture::F_rgb:
00770       tex->_pbuffer->set_format(PixelBuffer::F_rgb);
00771       break;
00772     case EggTexture::F_rgb12:
00773       if (tex->_pbuffer->get_component_width() >= 2) {
00774         // Only do this if the component width supports it.
00775         tex->_pbuffer->set_format(PixelBuffer::F_rgb12);
00776       } else {
00777         egg2pg_cat.warning()
00778           << "Ignoring inappropriate format " << egg_tex->get_format()
00779           << " for 8-bit texture " << egg_tex->get_name() << "\n";
00780       }
00781       break;
00782     case EggTexture::F_rgb8:
00783     case EggTexture::F_rgba8:
00784       // We'll quietly accept RGBA8 for a 3-component texture, since
00785       // flt2egg generates these for 3-component as well as for
00786       // 4-component textures.
00787       tex->_pbuffer->set_format(PixelBuffer::F_rgb8);
00788       break;
00789     case EggTexture::F_rgb5:
00790       tex->_pbuffer->set_format(PixelBuffer::F_rgb5);
00791       break;
00792     case EggTexture::F_rgb332:
00793       tex->_pbuffer->set_format(PixelBuffer::F_rgb332);
00794       break;
00795 
00796     case EggTexture::F_unspecified:
00797       break;
00798 
00799     default:
00800       egg2pg_cat.warning()
00801         << "Ignoring inappropriate format " << egg_tex->get_format()
00802         << " for 3-component texture " << egg_tex->get_name() << "\n";
00803     }
00804 
00805   } else if (tex->_pbuffer->get_num_components() == 4) {
00806     switch (egg_tex->get_format()) {
00807     case EggTexture::F_rgba:
00808       tex->_pbuffer->set_format(PixelBuffer::F_rgba);
00809       break;
00810     case EggTexture::F_rgbm:
00811       tex->_pbuffer->set_format(PixelBuffer::F_rgbm);
00812       break;
00813     case EggTexture::F_rgba12:
00814       if (tex->_pbuffer->get_component_width() >= 2) {
00815         // Only do this if the component width supports it.
00816         tex->_pbuffer->set_format(PixelBuffer::F_rgba12);
00817       } else {
00818         egg2pg_cat.warning()
00819           << "Ignoring inappropriate format " << egg_tex->get_format()
00820           << " for 8-bit texture " << egg_tex->get_name() << "\n";
00821       }
00822       break;
00823     case EggTexture::F_rgba8:
00824       tex->_pbuffer->set_format(PixelBuffer::F_rgba8);
00825       break;
00826     case EggTexture::F_rgba4:
00827       tex->_pbuffer->set_format(PixelBuffer::F_rgba4);
00828       break;
00829     case EggTexture::F_rgba5:
00830       tex->_pbuffer->set_format(PixelBuffer::F_rgba5);
00831       break;
00832 
00833     case EggTexture::F_unspecified:
00834       break;
00835 
00836     default:
00837       egg2pg_cat.warning()
00838         << "Ignoring inappropriate format " << egg_tex->get_format()
00839         << " for 4-component texture " << egg_tex->get_name() << "\n";
00840     }
00841   }
00842 }
00843 
00844 ////////////////////////////////////////////////////////////////////
00845 //     Function: EggLoader::apply_texture_apply_attributes
00846 //       Access: Private
00847 //  Description:
00848 ////////////////////////////////////////////////////////////////////
00849 CPT(RenderAttrib) EggLoader::
00850 get_texture_apply_attributes(const EggTexture *egg_tex) {
00851   CPT(RenderAttrib) result = TextureApplyAttrib::make(TextureApplyAttrib::M_modulate);
00852   if (egg_always_decal_textures) {
00853     result = TextureApplyAttrib::make(TextureApplyAttrib::M_decal);
00854 
00855   } else {
00856     switch (egg_tex->get_env_type()) {
00857     case EggTexture::ET_modulate:
00858       result = TextureApplyAttrib::make(TextureApplyAttrib::M_modulate);
00859       break;
00860 
00861     case EggTexture::ET_decal:
00862       result = TextureApplyAttrib::make(TextureApplyAttrib::M_decal);
00863       break;
00864 
00865     case EggTexture::ET_unspecified:
00866       break;
00867 
00868     default:
00869       egg2pg_cat.warning()
00870         << "Invalid texture environment "
00871         << (int)egg_tex->get_env_type() << "\n";
00872     }
00873   }
00874 
00875   return result;
00876 }
00877 
00878 ////////////////////////////////////////////////////////////////////
00879 //     Function: EggLoader::get_material_attrib
00880 //       Access: Private
00881 //  Description: Returns a RenderAttrib suitable for enabling the
00882 //               material indicated by the given EggMaterial, and with
00883 //               the indicated backface flag.
00884 ////////////////////////////////////////////////////////////////////
00885 CPT(RenderAttrib) EggLoader::
00886 get_material_attrib(const EggMaterial *egg_mat, bool bface) {
00887   Materials &materials = bface ? _materials_bface : _materials;
00888 
00889   // First, check whether we've seen this material before.
00890   Materials::const_iterator mi;
00891   mi = materials.find(egg_mat);
00892   if (mi != materials.end()) {
00893     return (*mi).second;
00894   }
00895 
00896   // Ok, this is the first time we've seen this particular
00897   // EggMaterial.  Create a new Material that matches it.
00898   PT(Material) mat = new Material;
00899   if (egg_mat->has_diff()) {
00900     mat->set_diffuse(egg_mat->get_diff());
00901     // By default, ambient is the same as diffuse, if diffuse is
00902     // specified but ambient is not.
00903     mat->set_ambient(egg_mat->get_diff());
00904   }
00905   if (egg_mat->has_amb()) {
00906     mat->set_ambient(egg_mat->get_amb());
00907   }
00908   if (egg_mat->has_emit()) {
00909     mat->set_emission(egg_mat->get_emit());
00910   }
00911   if (egg_mat->has_spec()) {
00912     mat->set_specular(egg_mat->get_spec());
00913   }
00914   if (egg_mat->has_shininess()) {
00915     mat->set_shininess(egg_mat->get_shininess());
00916   }
00917   if (egg_mat->has_local()) {
00918     mat->set_local(egg_mat->get_local());
00919   }
00920 
00921   mat->set_twoside(bface);
00922 
00923   // Now get a global Material pointer, shared with other models.
00924   const Material *shared_mat = MaterialPool::get_material(mat);
00925 
00926   // And create a MaterialAttrib for this Material.
00927   CPT(RenderAttrib) mt = MaterialAttrib::make(shared_mat);
00928   materials.insert(Materials::value_type(egg_mat, mt));
00929 
00930   return mt;
00931 }
00932 
00933 
00934 ////////////////////////////////////////////////////////////////////
00935 //     Function: EggLoader::setup_bucket
00936 //       Access: Private
00937 //  Description:
00938 ////////////////////////////////////////////////////////////////////
00939 void EggLoader::
00940 setup_bucket(BuilderBucket &bucket, PandaNode *parent,
00941              EggPrimitive *egg_prim) {
00942   bucket._node = parent;
00943   bucket._mesh = egg_mesh;
00944   bucket._retesselate_coplanar = egg_retesselate_coplanar;
00945   bucket._unroll_fans = egg_unroll_fans;
00946   bucket._show_tstrips = egg_show_tstrips;
00947   bucket._show_qsheets = egg_show_qsheets;
00948   bucket._show_quads = egg_show_quads;
00949   bucket._show_normals = egg_show_normals;
00950   bucket._normal_scale = egg_normal_scale;
00951   bucket._subdivide_polys = egg_subdivide_polys;
00952   bucket._consider_fans = egg_consider_fans;
00953   bucket._max_tfan_angle = egg_max_tfan_angle;
00954   bucket._min_tfan_tris = egg_min_tfan_tris;
00955   bucket._coplanar_threshold = egg_coplanar_threshold;
00956 
00957   // If a primitive has a name that does not begin with a digit, it
00958   // should be used to group primitives together--i.e. each primitive
00959   // with the same name gets placed into the same GeomNode.  However,
00960   // if a prim's name begins with a digit, just ignore it.
00961   if (egg_prim->has_name() && !isdigit(egg_prim->get_name()[0])) {
00962     bucket.set_name(egg_prim->get_name());
00963   }
00964 
00965   // Assign the appropriate properties to the bucket.
00966 
00967   // The various EggRenderMode properties can be defined directly at
00968   // the primitive, at a group above the primitive, or an a texture
00969   // applied to the primitive.  The EggNode::determine_*() functions
00970   // can find the right pointer to the level at which this is actually
00971   // defined for a given primitive.
00972   EggRenderMode::AlphaMode am = EggRenderMode::AM_unspecified;
00973   EggRenderMode::DepthWriteMode dwm = EggRenderMode::DWM_unspecified;
00974   EggRenderMode::DepthTestMode dtm = EggRenderMode::DTM_unspecified;
00975   bool implicit_alpha = false;
00976   bool has_draw_order = false;
00977   int draw_order = 0;
00978   bool has_bin = false;
00979   string bin;
00980 
00981   EggRenderMode *render_mode;
00982   render_mode = egg_prim->determine_alpha_mode();
00983   if (render_mode != (EggRenderMode *)NULL) {
00984     am = render_mode->get_alpha_mode();
00985   }
00986   render_mode = egg_prim->determine_depth_write_mode();
00987   if (render_mode != (EggRenderMode *)NULL) {
00988     dwm = render_mode->get_depth_write_mode();
00989   }
00990   render_mode = egg_prim->determine_depth_test_mode();
00991   if (render_mode != (EggRenderMode *)NULL) {
00992     dtm = render_mode->get_depth_test_mode();
00993   }
00994   render_mode = egg_prim->determine_draw_order();
00995   if (render_mode != (EggRenderMode *)NULL) {
00996     has_draw_order = true;
00997     draw_order = render_mode->get_draw_order();
00998   }
00999   render_mode = egg_prim->determine_bin();
01000   if (render_mode != (EggRenderMode *)NULL) {
01001     has_bin = true;
01002     bin = render_mode->get_bin();
01003   }
01004 
01005   bucket.add_attrib(TextureAttrib::make_off());
01006   if (egg_prim->has_texture()) {
01007     PT(EggTexture) egg_tex = egg_prim->get_texture();
01008 
01009     const TextureDef &def = _textures[egg_tex];
01010     if (def._texture != (const RenderAttrib *)NULL) {
01011       bucket.add_attrib(def._texture);
01012       bucket.add_attrib(def._apply);
01013 
01014       // If neither the primitive nor the texture specified an alpha
01015       // mode, assume it should be alpha'ed if the texture has an
01016       // alpha channel.
01017       if (am == EggRenderMode::AM_unspecified) {
01018         const TextureAttrib *tex_attrib = DCAST(TextureAttrib, def._texture);
01019         Texture *tex = tex_attrib->get_texture();
01020         nassertv(tex != (Texture *)NULL);
01021         int num_components = tex->_pbuffer->get_num_components();
01022         if (egg_tex->has_alpha_channel(num_components)) {
01023           implicit_alpha = true;
01024         }
01025       }
01026     }
01027   }
01028 
01029   if (egg_prim->has_material()) {
01030     CPT(RenderAttrib) mt =
01031       get_material_attrib(egg_prim->get_material(),
01032                           egg_prim->get_bface_flag());
01033     bucket.add_attrib(mt);
01034   }
01035 
01036 
01037   // Also check the color of the primitive to see if we should assume
01038   // alpha based on the alpha values specified in the egg file.
01039   if (am == EggRenderMode::AM_unspecified) {
01040     if (egg_prim->has_color()) {
01041       if (egg_prim->get_color()[3] != 1.0) {
01042         implicit_alpha = true;
01043       }
01044     }
01045     EggPrimitive::const_iterator vi;
01046     for (vi = egg_prim->begin();
01047          !implicit_alpha && vi != egg_prim->end();
01048          ++vi) {
01049       if ((*vi)->has_color()) {
01050         if ((*vi)->get_color()[3] != 1.0) {
01051           implicit_alpha = true;
01052         }
01053       }
01054     }
01055 
01056     if (implicit_alpha) {
01057       am = EggRenderMode::AM_on;
01058     }
01059   }
01060 
01061   if (am == EggRenderMode::AM_on) {
01062     // Alpha type "on" means to get the default transparency type.
01063     am = egg_alpha_mode;
01064   }
01065 
01066   switch (am) {
01067   case EggRenderMode::AM_on:
01068   case EggRenderMode::AM_blend:
01069     bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
01070     break;
01071 
01072   case EggRenderMode::AM_blend_no_occlude:
01073     bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
01074     bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
01075     break;
01076 
01077   case EggRenderMode::AM_ms:
01078     bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample));
01079     break;
01080 
01081   case EggRenderMode::AM_ms_mask:
01082     bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_multisample_mask));
01083     break;
01084 
01085   case EggRenderMode::AM_binary:
01086     bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_binary));
01087     break;
01088 
01089   case EggRenderMode::AM_dual:
01090     bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_dual));
01091     break;
01092 
01093   default:
01094     break;
01095   }
01096 
01097   switch (dwm) {
01098   case EggRenderMode::DWM_on:
01099     bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_on));
01100     break;
01101 
01102   case EggRenderMode::DWM_off:
01103     bucket.add_attrib(DepthWriteAttrib::make(DepthWriteAttrib::M_off));
01104     break;
01105 
01106   default:
01107     break;
01108   }
01109 
01110   switch (dtm) {
01111   case EggRenderMode::DTM_on:
01112     bucket.add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_less));
01113     break;
01114 
01115   case EggRenderMode::DTM_off:
01116     bucket.add_attrib(DepthTestAttrib::make(DepthTestAttrib::M_none));
01117     break;
01118 
01119   default:
01120     break;
01121   }
01122 
01123   if (has_bin) {
01124     bucket.add_attrib(CullBinAttrib::make(bin, draw_order));
01125 
01126   } else if (has_draw_order) {
01127     bucket.add_attrib(CullBinAttrib::make("fixed", draw_order));
01128   }
01129  
01130 
01131   if (egg_prim->get_bface_flag()) {
01132     // The primitive is marked with backface culling disabled--we want
01133     // to see both sides.
01134     bucket.add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
01135   }
01136 }
01137 
01138 
01139 
01140 ////////////////////////////////////////////////////////////////////
01141 //     Function: EggLoader::make_node
01142 //       Access: Private
01143 //  Description:
01144 ////////////////////////////////////////////////////////////////////
01145 PandaNode *EggLoader::
01146 make_node(EggNode *egg_node, PandaNode *parent) {
01147   if (egg_node->is_of_type(EggNurbsCurve::get_class_type())) {
01148     return make_node(DCAST(EggNurbsCurve, egg_node), parent);
01149   } else if (egg_node->is_of_type(EggPrimitive::get_class_type())) {
01150     return make_node(DCAST(EggPrimitive, egg_node), parent);
01151   } else if (egg_node->is_of_type(EggBin::get_class_type())) {
01152     return make_node(DCAST(EggBin, egg_node), parent);
01153   } else if (egg_node->is_of_type(EggGroup::get_class_type())) {
01154     return make_node(DCAST(EggGroup, egg_node), parent);
01155   } else if (egg_node->is_of_type(EggTable::get_class_type())) {
01156     return make_node(DCAST(EggTable, egg_node), parent);
01157   } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
01158     return make_node(DCAST(EggGroupNode, egg_node), parent);
01159   }
01160 
01161   return (PandaNode *)NULL;
01162 }
01163 
01164 ////////////////////////////////////////////////////////////////////
01165 //     Function: EggLoader::make_node (EggNurbsCurve)
01166 //       Access: Private
01167 //  Description:
01168 ////////////////////////////////////////////////////////////////////
01169 PandaNode *EggLoader::
01170 make_node(EggNurbsCurve *egg_curve, PandaNode *parent) {
01171   assert(parent != NULL);
01172   assert(!parent->is_geom_node());
01173 
01174   PT(ParametricCurve) curve;
01175 
01176   if (egg_load_classic_nurbs_curves) {
01177     curve = new ClassicNurbsCurve;
01178   } else {
01179     curve = new NurbsCurve;
01180   }
01181 
01182   NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
01183   nassertr(nurbs != (NurbsCurveInterface *)NULL, (PandaNode *)NULL);
01184 
01185   if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
01186     egg2pg_cat.error()
01187       << "Invalid NURBSCurve order for " << egg_curve->get_name() << ": "
01188       << egg_curve->get_order() << "\n";
01189     _error = true;
01190     return (PandaNode *)NULL;
01191   }
01192 
01193   nurbs->set_order(egg_curve->get_order());
01194 
01195   EggPrimitive::const_iterator pi;
01196   for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
01197     nurbs->append_cv(LCAST(float, (*pi)->get_pos4()));
01198   }
01199 
01200   int num_knots = egg_curve->get_num_knots();
01201   if (num_knots != nurbs->get_num_knots()) {
01202     egg2pg_cat.error()
01203       << "Invalid NURBSCurve number of knots for "
01204       << egg_curve->get_name() << ": got " << num_knots
01205       << " knots, expected " << nurbs->get_num_knots() << "\n";
01206     _error = true;
01207     return (PandaNode *)NULL;
01208   }
01209 
01210   for (int i = 0; i < num_knots; i++) {
01211     nurbs->set_knot(i, egg_curve->get_knot(i));
01212   }
01213 
01214   switch (egg_curve->get_curve_type()) {
01215   case EggCurve::CT_xyz:
01216     curve->set_curve_type(PCT_XYZ);
01217     break;
01218 
01219   case EggCurve::CT_hpr:
01220     curve->set_curve_type(PCT_HPR);
01221     break;
01222 
01223   case EggCurve::CT_t:
01224     curve->set_curve_type(PCT_T);
01225     break;
01226 
01227   default:
01228     break;
01229   }
01230   curve->set_name(egg_curve->get_name());
01231 
01232   if (!curve->recompute()) {
01233     egg2pg_cat.error()
01234       << "Invalid NURBSCurve " << egg_curve->get_name() << "\n";
01235     _error = true;
01236     return (PandaNode *)NULL;
01237   }
01238 
01239   parent->add_child(curve);
01240   return curve;
01241 }
01242 
01243 ////////////////////////////////////////////////////////////////////
01244 //     Function: EggLoader::make_node (EggPrimitive)
01245 //       Access: Private
01246 //  Description:
01247 ////////////////////////////////////////////////////////////////////
01248 PandaNode *EggLoader::
01249 make_node(EggPrimitive *egg_prim, PandaNode *parent) {
01250   assert(parent != NULL);
01251   assert(!parent->is_of_type(GeomNode::get_class_type()));
01252 
01253   if (egg_prim->cleanup()) {
01254     if (parent->is_of_type(SelectiveChildNode::get_class_type())) {
01255       // If we're putting a primitive under a SelectiveChildNode of
01256       // some kind, its exact position within the group is relevant,
01257       // so we need to create a placeholder now.
01258       PandaNode *group = new PandaNode(egg_prim->get_name());
01259       parent->add_child(group);
01260       make_nonindexed_primitive(egg_prim, group);
01261       return group;
01262     }
01263 
01264     // Otherwise, we don't really care what the position of this
01265     // primitive is within its parent's list of children, and in fact
01266     // we want to allow it to be combined with other polygons added to
01267     // the same parent.
01268     make_nonindexed_primitive(egg_prim, parent);
01269   }
01270   return (PandaNode *)NULL;
01271 }
01272 
01273 ////////////////////////////////////////////////////////////////////
01274 //     Function: EggLoader::make_node (EggBin)
01275 //       Access: Private
01276 //  Description:
01277 ////////////////////////////////////////////////////////////////////
01278 PandaNode *EggLoader::
01279 make_node(EggBin *egg_bin, PandaNode *parent) {
01280   // Presently, an EggBin can only mean an LOD node (i.e. a parent of
01281   // one or more EggGroups with LOD specifications).  Later it might
01282   // mean other things as well.
01283 
01284   nassertr((EggBinner::BinNumber)egg_bin->get_bin_number() == EggBinner::BN_lod, NULL);
01285   LODNode *lod_node = new LODNode(egg_bin->get_name());
01286 
01287   pvector<LODInstance> instances;
01288 
01289   EggGroup::const_iterator ci;
01290   for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
01291     LODInstance instance(*ci);
01292     instances.push_back(instance);
01293   }
01294 
01295   // Now that we've created all of our children, put them in the
01296   // proper order and tell the LOD node about them.
01297   sort(instances.begin(), instances.end());
01298 
01299   if (!instances.empty()) {
01300     // Set up the LOD node's center.  All of the children should have
01301     // the same center, because that's how we binned them.
01302     lod_node->set_center(LCAST(float, instances[0]._d->_center));
01303   }
01304 
01305   for (size_t i = 0; i < instances.size(); i++) {
01306     // Create the children in the proper order within the scene graph.
01307     const LODInstance &instance = instances[i];
01308     make_node(instance._egg_node, lod_node);
01309 
01310     // All of the children should have the same center, because that's
01311     // how we binned them.
01312     nassertr(lod_node->get_center().almost_equal
01313              (LCAST(float, instance._d->_center), 0.01), NULL);
01314 
01315     // Tell the LOD node about this child's switching distances.
01316     lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out);
01317   }
01318 
01319   return create_group_arc(egg_bin, parent, lod_node);
01320 }
01321 
01322 
01323 ////////////////////////////////////////////////////////////////////
01324 //     Function: EggLoader::make_node (EggGroup)
01325 //       Access: Private
01326 //  Description:
01327 ////////////////////////////////////////////////////////////////////
01328 PandaNode *EggLoader::
01329 make_node(EggGroup *egg_group, PandaNode *parent) {
01330   PT(PandaNode) node = NULL;
01331 
01332   if (egg_group->get_num_object_types() != 0) {
01333     pset<string> expanded;
01334     pvector<string> expanded_history;
01335     if (!expand_object_types(egg_group, expanded, expanded_history)) {
01336       return NULL;
01337     }
01338   }
01339 
01340   if (egg_group->get_dart_type() != EggGroup::DT_none) {
01341     // A group with the <Dart> flag set means to create a character.
01342     CharacterMaker char_maker(egg_group, *this);
01343     node = char_maker.make_node();
01344 
01345   } else if (egg_group->get_cs_type() != EggGroup::CST_none &&
01346              egg_group->get_cs_type() != EggGroup::CST_geode) {
01347     // A collision group: create collision geometry.
01348     node = new CollisionNode(egg_group->get_name());
01349 
01350     make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p());
01351     if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
01352       // If we also specified to keep the geometry, continue the
01353       // traversal.
01354       EggGroup::const_iterator ci;
01355       for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01356         make_node(*ci, parent);
01357       }
01358     }
01359 
01360     node = create_group_arc(egg_group, parent, node);
01361 
01362     if (!egg_show_collision_solids) {
01363       node->set_draw_mask(DrawMask::all_off());
01364     }
01365     return node;
01366 
01367   } else if (egg_group->get_switch_flag()) {
01368     if (egg_group->get_switch_fps() != 0.0) {
01369       // Create a sequence node.
01370       node = new SequenceNode(egg_group->get_switch_fps(), 
01371                               egg_group->get_name());
01372     } else {
01373       // Create a switch node.
01374       node = new SwitchNode(egg_group->get_name());
01375     }
01376       
01377     EggGroup::const_iterator ci;
01378     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01379       make_node(*ci, node);
01380     }
01381 
01382   } else if (egg_group->get_model_flag() || 
01383              egg_group->get_dcs_type() != EggGroup::DC_none) {
01384     // A model or DCS flag; create a model node.
01385     node = new ModelNode(egg_group->get_name());
01386     switch (egg_group->get_dcs_type()) {
01387     case EggGroup::DC_net:
01388       DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_net);
01389       break;
01390 
01391     case EggGroup::DC_local:
01392     case EggGroup::DC_default:
01393       DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_local);
01394       break;
01395 
01396     case EggGroup::DC_none:
01397       break;
01398     }
01399 
01400     EggGroup::const_iterator ci;
01401     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01402       make_node(*ci, node);
01403     }
01404 
01405   } else {
01406     // A normal group; just create a normal node, and traverse.
01407     node = new PandaNode(egg_group->get_name());
01408 
01409     EggGroup::const_iterator ci;
01410     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01411       make_node(*ci, node);
01412     }
01413   }
01414 
01415   if (node == (PandaNode *)NULL) {
01416     return NULL;
01417   }
01418   return create_group_arc(egg_group, parent, node);
01419 }
01420 
01421 ////////////////////////////////////////////////////////////////////
01422 //     Function: EggLoader::create_group_arc
01423 //       Access: Private
01424 //  Description: Creates the arc parenting a new group to the scene
01425 //               graph, and applies any relevant attribs to the
01426 //               arc according to the EggGroup node that inspired the
01427 //               group.
01428 ////////////////////////////////////////////////////////////////////
01429 PandaNode *EggLoader::
01430 create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
01431   parent->add_child(node);
01432 
01433   // If the group had a transform, apply it to the arc.
01434   if (egg_group->has_transform()) {
01435     node->set_transform(make_transform(egg_group));
01436   }
01437 
01438   // If the group has a billboard flag, apply that.
01439   switch (egg_group->get_billboard_type()) {
01440   case EggGroup::BT_point_camera_relative:
01441     node->set_effect(BillboardEffect::make_point_eye());
01442     break;
01443 
01444   case EggGroup::BT_point_world_relative:
01445     node->set_effect(BillboardEffect::make_point_world());
01446     break;
01447 
01448   case EggGroup::BT_axis:
01449     node->set_effect(BillboardEffect::make_axis());
01450     break;
01451 
01452   case EggGroup::BT_none:
01453     break;
01454   }
01455 
01456   if (egg_group->get_decal_flag()) {
01457     if (egg_ignore_decals) {
01458       egg2pg_cat.error()
01459         << "Ignoring decal flag on " << egg_group->get_name() << "\n";
01460       _error = true;
01461     }
01462 
01463     // If the group has the "decal" flag set, it means that all of the
01464     // descendant groups will be decaled onto the geometry within
01465     // this group.  This means we'll need to reparent things a bit
01466     // afterward.
01467     _decals.insert(node);
01468   }
01469 
01470   // If the group specified some property that should propagate down
01471   // to the leaves, we have to remember this node and apply the
01472   // property later, after we've created the actual geometry.
01473   DeferredNodeProperty def;
01474   if (egg_group->has_collide_mask()) {
01475     def._from_collide_mask = egg_group->get_collide_mask();
01476     def._into_collide_mask = egg_group->get_collide_mask();
01477     def._flags |=
01478       DeferredNodeProperty::F_has_from_collide_mask |
01479       DeferredNodeProperty::F_has_into_collide_mask;
01480   }
01481   if (egg_group->has_from_collide_mask()) {
01482     def._from_collide_mask = egg_group->get_from_collide_mask();
01483     def._flags |= DeferredNodeProperty::F_has_from_collide_mask;
01484   }
01485   if (egg_group->has_into_collide_mask()) {
01486     def._into_collide_mask = egg_group->get_into_collide_mask();
01487     def._flags |= DeferredNodeProperty::F_has_into_collide_mask;
01488   }
01489 
01490   if (def._flags != 0) {
01491     _deferred_nodes[node] = def;
01492   }
01493 
01494   return node;
01495 }
01496 
01497 ////////////////////////////////////////////////////////////////////
01498 //     Function: EggLoader::make_node (EggTable)
01499 //       Access: Private
01500 //  Description:
01501 ////////////////////////////////////////////////////////////////////
01502 PandaNode *EggLoader::
01503 make_node(EggTable *egg_table, PandaNode *parent) {
01504   if (egg_table->get_table_type() != EggTable::TT_bundle) {
01505     // We only do anything with bundles.  Isolated tables are treated
01506     // as ordinary groups.
01507     return make_node(DCAST(EggGroupNode, egg_table), parent);
01508   }
01509 
01510   // It's an actual bundle, so make an AnimBundle from it and its
01511   // descendants.
01512   AnimBundleMaker bundle_maker(egg_table);
01513   AnimBundleNode *node = bundle_maker.make_node();
01514   parent->add_child(node);
01515   return node;
01516 }
01517 
01518 
01519 ////////////////////////////////////////////////////////////////////
01520 //     Function: EggLoader::make_node (EggGroupNode)
01521 //       Access: Private
01522 //  Description:
01523 ////////////////////////////////////////////////////////////////////
01524 PandaNode *EggLoader::
01525 make_node(EggGroupNode *egg_group, PandaNode *parent) {
01526   PandaNode *node = new PandaNode(egg_group->get_name());
01527 
01528   EggGroupNode::const_iterator ci;
01529   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01530     make_node(*ci, node);
01531   }
01532 
01533   parent->add_child(node);
01534   return node;
01535 }
01536 
01537 ////////////////////////////////////////////////////////////////////
01538 //     Function: EggLoader::make_collision_solids
01539 //       Access: Private
01540 //  Description: Creates CollisionSolids corresponding to the
01541 //               collision geometry indicated at the given node and
01542 //               below.
01543 ////////////////////////////////////////////////////////////////////
01544 void EggLoader::
01545 make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
01546                       CollisionNode *cnode) {
01547   if (egg_group->get_cs_type() != EggGroup::CST_none) {
01548     start_group = egg_group;
01549   }
01550 
01551   switch (start_group->get_cs_type()) {
01552   case EggGroup::CST_none:
01553   case EggGroup::CST_geode:
01554     // No collision flags; do nothing.  Don't even traverse further.
01555     return;
01556 
01557   case EggGroup::CST_inverse_sphere:
01558     // These aren't presently supported.
01559     egg2pg_cat.error()
01560       << "Not presently supported: <Collide> { "
01561       << egg_group->get_cs_type() << " }\n";
01562     _error = true;
01563     break;
01564 
01565   case EggGroup::CST_plane:
01566     make_collision_plane(egg_group, cnode, start_group->get_collide_flags());
01567     break;
01568 
01569   case EggGroup::CST_polygon:
01570     make_collision_polygon(egg_group, cnode, start_group->get_collide_flags());
01571     break;
01572 
01573   case EggGroup::CST_polyset:
01574     make_collision_polyset(egg_group, cnode, start_group->get_collide_flags());
01575     break;
01576 
01577   case EggGroup::CST_sphere:
01578     make_collision_sphere(egg_group, cnode, start_group->get_collide_flags());
01579     break;
01580   }
01581 
01582   if ((start_group->get_collide_flags() & EggGroup::CF_descend) != 0) {
01583     // Now pick up everything below.
01584     EggGroup::const_iterator ci;
01585     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01586       if ((*ci)->is_of_type(EggGroup::get_class_type())) {
01587         make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode);
01588       }
01589     }
01590   }
01591 }
01592 
01593 ////////////////////////////////////////////////////////////////////
01594 //     Function: EggLoader::make_collision_plane
01595 //       Access: Private
01596 //  Description: Creates a single CollisionPlane corresponding
01597 //               to the first polygon associated with this group.
01598 ////////////////////////////////////////////////////////////////////
01599 void EggLoader::
01600 make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
01601                      EggGroup::CollideFlags flags) {
01602   EggGroup *geom_group = find_collision_geometry(egg_group);
01603   if (geom_group != (EggGroup *)NULL) {
01604     EggGroup::const_iterator ci;
01605     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
01606       if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
01607         CollisionPlane *csplane =
01608           create_collision_plane(DCAST(EggPolygon, *ci), egg_group);
01609         if (csplane != (CollisionPlane *)NULL) {
01610           apply_collision_flags(csplane, flags);
01611           cnode->add_solid(csplane);
01612           return;
01613         }
01614       }
01615     }
01616   }
01617 }
01618 
01619 ////////////////////////////////////////////////////////////////////
01620 //     Function: EggLoader::make_collision_polygon
01621 //       Access: Private
01622 //  Description: Creates a single CollisionPolygon corresponding
01623 //               to the first polygon associated with this group.
01624 ////////////////////////////////////////////////////////////////////
01625 void EggLoader::
01626 make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
01627                        EggGroup::CollideFlags flags) {
01628 
01629   EggGroup *geom_group = find_collision_geometry(egg_group);
01630   if (geom_group != (EggGroup *)NULL) {
01631     EggGroup::const_iterator ci;
01632     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
01633       if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
01634         create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
01635                                   egg_group, flags);
01636       }
01637     }
01638   }
01639 }
01640 
01641 ////////////////////////////////////////////////////////////////////
01642 //     Function: EggLoader::make_collision_polyset
01643 //       Access: Private
01644 //  Description: Creates a series of CollisionPolygons corresponding
01645 //               to the polygons associated with this group.
01646 ////////////////////////////////////////////////////////////////////
01647 void EggLoader::
01648 make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
01649                        EggGroup::CollideFlags flags) {
01650   EggGroup *geom_group = find_collision_geometry(egg_group);
01651   if (geom_group != (EggGroup *)NULL) {
01652     EggGroup::const_iterator ci;
01653     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
01654       if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
01655         create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
01656                                   egg_group, flags);
01657       }
01658     }
01659   }
01660 }
01661 
01662 ////////////////////////////////////////////////////////////////////
01663 //     Function: EggLoader::make_collision_sphere
01664 //       Access: Private
01665 //  Description: Creates a single CollisionSphere corresponding
01666 //               to the polygons associated with this group.
01667 ////////////////////////////////////////////////////////////////////
01668 void EggLoader::
01669 make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
01670                       EggGroup::CollideFlags flags) {
01671   EggGroup *geom_group = find_collision_geometry(egg_group);
01672   if (geom_group != (EggGroup *)NULL) {
01673     // Collect all of the vertices.
01674     pset<EggVertex *> vertices;
01675 
01676     EggGroup::const_iterator ci;
01677     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
01678       if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
01679         EggPrimitive *prim = DCAST(EggPrimitive, *ci);
01680         EggPrimitive::const_iterator pi;
01681         for (pi = prim->begin(); pi != prim->end(); ++pi) {
01682           vertices.insert(*pi);
01683         }
01684       }
01685     }
01686 
01687     // Now average together all of the vertices to get a center.
01688     int num_vertices = 0;
01689     LPoint3d center(0.0, 0.0, 0.0);
01690     pset<EggVertex *>::const_iterator vi;
01691 
01692     for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
01693       EggVertex *vtx = (*vi);
01694       if (vtx->get_num_dimensions() == 3) {
01695         center += vtx->get_pos3();
01696         num_vertices++;
01697 
01698       } else if (vtx->get_num_dimensions() == 4) {
01699         LPoint4d p4 = vtx->get_pos4();
01700         if (p4[3] != 0.0) {
01701           center += LPoint3d(p4[0], p4[1], p4[2]) / p4[3];
01702           num_vertices++;
01703         }
01704       }
01705     }
01706 
01707     if (num_vertices > 0) {
01708       center /= (double)num_vertices;
01709 
01710       LMatrix4d mat = egg_group->get_vertex_to_node();
01711       center = center * mat;
01712 
01713       // And the furthest vertex determines the radius.
01714       double radius2 = 0.0;
01715       for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
01716         EggVertex *vtx = (*vi);
01717         if (vtx->get_num_dimensions() == 3) {
01718           LPoint3d p3 = vtx->get_pos3();
01719           LVector3d v = p3 * mat - center;
01720           radius2 = max(radius2, v.length_squared());
01721 
01722         } else if (vtx->get_num_dimensions() == 4) {
01723           LPoint4d p4 = vtx->get_pos4();
01724           if (p4[3] != 0.0) {
01725             LPoint3d p3 = LPoint3d(p4[0], p4[1], p4[2]) / p4[3];
01726             LVector3d v = p3 * mat - center;
01727             radius2 = max(radius2, v.length_squared());
01728           }
01729         }
01730       }
01731 
01732       float radius = sqrtf(radius2);
01733       CollisionSphere *cssphere =
01734         new CollisionSphere(LCAST(float, center), radius);
01735       apply_collision_flags(cssphere, flags);
01736       cnode->add_solid(cssphere);
01737     }
01738   }
01739 }
01740 
01741 ////////////////////////////////////////////////////////////////////
01742 //     Function: EggLoader::apply_collision_flags
01743 //       Access: Private
01744 //  Description: Does funny stuff to the CollisionSolid as
01745 //               appropriate, based on the settings of the given
01746 //               CollideFlags.
01747 ////////////////////////////////////////////////////////////////////
01748 void EggLoader::
01749 apply_collision_flags(CollisionSolid *solid,
01750                       EggGroup::CollideFlags flags) {
01751   if ((flags & EggGroup::CF_intangible) != 0) {
01752     solid->set_tangible(false);
01753   }
01754 }
01755 
01756 ////////////////////////////////////////////////////////////////////
01757 //     Function: EggLoader::find_collision_geometry
01758 //       Access: Private
01759 //  Description: Looks for the node, at or below the indicated node,
01760 //               that contains the associated collision geometry.
01761 ////////////////////////////////////////////////////////////////////
01762 EggGroup *EggLoader::
01763 find_collision_geometry(EggGroup *egg_group) {
01764   if ((egg_group->get_collide_flags() & EggGroup::CF_descend) != 0) {
01765     // If we have the "descend" instruction, we'll get to it when we
01766     // get to it.  Don't worry about it now.
01767     return egg_group;
01768   }
01769 
01770   // Does this group have any polygons?
01771   EggGroup::const_iterator ci;
01772   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01773     if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
01774       // Yes!  Use this group.
01775       return egg_group;
01776     }
01777   }
01778 
01779   // Well, the group had no polygons; look for a child group that has
01780   // the same collision type.
01781   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01782     if ((*ci)->is_of_type(EggGroup::get_class_type())) {
01783       EggGroup *child_group = DCAST(EggGroup, *ci);
01784       if (child_group->get_cs_type() == egg_group->get_cs_type()) {
01785         return child_group;
01786       }
01787     }
01788   }
01789 
01790   // We got nothing.
01791   return NULL;
01792 }
01793 
01794 ////////////////////////////////////////////////////////////////////
01795 //     Function: EggLoader::create_collision_plane
01796 //       Access: Private
01797 //  Description: Creates a single CollisionPlane from the indicated
01798 //               EggPolygon.
01799 ////////////////////////////////////////////////////////////////////
01800 CollisionPlane *EggLoader::
01801 create_collision_plane(EggPolygon *egg_poly, EggGroup *parent_group) {
01802   if (!egg_poly->cleanup()) {
01803     egg2pg_cat.error()
01804       << "Degenerate collision plane in " << parent_group->get_name()
01805       << "\n";
01806     _error = true;
01807     return NULL;
01808   }
01809 
01810   LMatrix4d mat = egg_poly->get_vertex_to_node();
01811 
01812   pvector<Vertexf> vertices;
01813   if (!egg_poly->empty()) {
01814     EggPolygon::const_iterator vi;
01815     vi = egg_poly->begin();
01816 
01817     Vertexd vert = (*vi)->get_pos3() * mat;
01818     vertices.push_back(LCAST(float, vert));
01819 
01820     Vertexd last_vert = vert;
01821     ++vi;
01822     while (vi != egg_poly->end()) {
01823       vert = (*vi)->get_pos3() * mat;
01824       if (!vert.almost_equal(last_vert)) {
01825         vertices.push_back(LCAST(float, vert));
01826       }
01827 
01828       last_vert = vert;
01829       ++vi;
01830     }
01831   }
01832 
01833   if (vertices.size() < 3) {
01834     return NULL;
01835   }
01836   Planef plane(vertices[0], vertices[1], vertices[2]);
01837   return new CollisionPlane(plane);
01838 }
01839 
01840 ////////////////////////////////////////////////////////////////////
01841 //     Function: EggLoader::create_collision_polygons
01842 //       Access: Private
01843 //  Description: Creates one or more CollisionPolygons from the
01844 //               indicated EggPolygon, and adds them to the indicated
01845 //               CollisionNode.
01846 ////////////////////////////////////////////////////////////////////
01847 void EggLoader::
01848 create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
01849                           EggGroup *parent_group,
01850                           EggGroup::CollideFlags flags) {
01851   LMatrix4d mat = egg_poly->get_vertex_to_node();
01852 
01853   PT(EggGroup) group = new EggGroup;
01854 
01855   if (!egg_poly->triangulate_into(group, false)) {
01856     egg2pg_cat.error()
01857       << "Degenerate collision polygon in " << parent_group->get_name()
01858       << "\n";
01859     _error = true;
01860     return;
01861   }
01862 
01863   if (group->size() != 1) {
01864     egg2pg_cat.error()
01865       << "Concave collision polygon in " << parent_group->get_name()
01866       << "\n";
01867     _error = true;
01868   }
01869 
01870   EggGroup::iterator ci;
01871   for (ci = group->begin(); ci != group->end(); ++ci) {
01872     EggPolygon *poly = DCAST(EggPolygon, *ci);
01873 
01874     pvector<Vertexf> vertices;
01875     if (!poly->empty()) {
01876       EggPolygon::const_iterator vi;
01877       vi = poly->begin();
01878 
01879       Vertexd vert = (*vi)->get_pos3() * mat;
01880       vertices.push_back(LCAST(float, vert));
01881 
01882       Vertexd last_vert = vert;
01883       ++vi;
01884       while (vi != poly->end()) {
01885         vert = (*vi)->get_pos3() * mat;
01886         if (!vert.almost_equal(last_vert)) {
01887           vertices.push_back(LCAST(float, vert));
01888         }
01889 
01890         last_vert = vert;
01891         ++vi;
01892       }
01893     }
01894 
01895     if (vertices.size() >= 3) {
01896       const Vertexf *vertices_begin = &vertices[0];
01897       const Vertexf *vertices_end = vertices_begin + vertices.size();
01898       CollisionPolygon *cspoly =
01899         new CollisionPolygon(vertices_begin, vertices_end);
01900       apply_collision_flags(cspoly, flags);
01901       cnode->add_solid(cspoly);
01902     }
01903   }
01904 }
01905 
01906 
01907 ////////////////////////////////////////////////////////////////////
01908 //     Function: EggLoader::apply_deferred_nodes
01909 //       Access: Private
01910 //  Description: Walks back over the tree and applies the
01911 //               DeferredNodeProperties that were saved up along the
01912 //               way.
01913 ////////////////////////////////////////////////////////////////////
01914 void EggLoader::
01915 apply_deferred_nodes(PandaNode *node, const DeferredNodeProperty &prop) {
01916   DeferredNodeProperty next_prop(prop);
01917 
01918   // Do we have a DeferredNodeProperty associated with this node?
01919   DeferredNodes::const_iterator dni;
01920   dni = _deferred_nodes.find(node);
01921 
01922   if (dni != _deferred_nodes.end()) {
01923     const DeferredNodeProperty &def = (*dni).second;
01924     next_prop.compose(def);
01925   }
01926 
01927   // Now apply the accumulated state to the node.
01928   next_prop.apply_to_node(node);
01929 
01930   int num_children = node->get_num_children();
01931   for (int i = 0; i < num_children; i++) {
01932     apply_deferred_nodes(node->get_child(i), next_prop);
01933   }
01934 }
01935 
01936 ////////////////////////////////////////////////////////////////////
01937 //     Function: EggLoader::expand_object_types
01938 //       Access: Private
01939 //  Description: Recursively expands the group's ObjectType string(s).
01940 //               It's recursive because an ObjectType string might
01941 //               itself expand to another ObjectType string, which is
01942 //               allowed; but we don't want to get caught in a cycle.
01943 //
01944 //               The return value is true if the object type is
01945 //               expanded and the node is valid, or false if the node
01946 //               should be ignored (e.g. ObjectType "backstage").
01947 ////////////////////////////////////////////////////////////////////
01948 bool EggLoader::
01949 expand_object_types(EggGroup *egg_group, const pset<string> &expanded,
01950                     const pvector<string> &expanded_history) {
01951   int num_object_types = egg_group->get_num_object_types();
01952 
01953   // First, copy out the object types so we can recursively modify the
01954   // list.
01955   vector_string object_types;
01956   int i;
01957   for (i = 0; i < num_object_types; i++) {
01958     object_types.push_back(egg_group->get_object_type(i));
01959   }
01960   egg_group->clear_object_types();
01961 
01962   for (i = 0; i < num_object_types; i++) {
01963     string object_type = object_types[i];
01964     pset<string> new_expanded(expanded);
01965 
01966     // Check for a cycle.
01967     if (!new_expanded.insert(object_type).second) {
01968       egg2pg_cat.error()
01969         << "Cycle in ObjectType expansions:\n";
01970       pvector<string>::const_iterator pi;
01971       for (pi = expanded_history.begin();
01972            pi != expanded_history.end();
01973            ++pi) {
01974         egg2pg_cat.error(false) 
01975           << (*pi) << " -> ";
01976       }
01977       egg2pg_cat.error(false) << object_type << "\n";
01978       _error = true;
01979 
01980     } else {
01981       // No cycle; continue.
01982       pvector<string> new_expanded_history(expanded_history);
01983       new_expanded_history.push_back(object_type);
01984 
01985       if (!do_expand_object_type(egg_group, new_expanded, 
01986                                  new_expanded_history, object_type)) {
01987         // Ignorable group; stop here.
01988         return false;
01989       }
01990     }
01991   }
01992 
01993   return true;
01994 }
01995 
01996 ////////////////////////////////////////////////////////////////////
01997 //     Function: EggLoader::expand_object_type
01998 //       Access: Private
01999 //  Description: Further implementation of expand_object_types().
02000 ////////////////////////////////////////////////////////////////////
02001 bool EggLoader::
02002 do_expand_object_type(EggGroup *egg_group, const pset<string> &expanded,
02003                       const pvector<string> &expanded_history,
02004                       const string &object_type) {
02005   // Try to find the egg syntax that the given objecttype is
02006   // shorthand for.  First, look in the config file.
02007 
02008   string egg_syntax =
02009     config_egg2pg.GetString("egg-object-type-" + downcase(object_type), "none");
02010 
02011   if (egg_syntax == "none") {
02012     // It wasn't defined in a config file.  Maybe it's built in?
02013     
02014     if (cmp_nocase_uh(object_type, "barrier") == 0) {
02015       egg_syntax = "<Collide> { Polyset descend }";
02016       
02017     } else if (cmp_nocase_uh(object_type, "solidpoly") == 0) {
02018       egg_syntax = "<Collide> { Polyset descend solid }";
02019       
02020     } else if (cmp_nocase_uh(object_type, "turnstile") == 0) {
02021       egg_syntax = "<Collide> { Polyset descend turnstile }";
02022       
02023     } else if (cmp_nocase_uh(object_type, "sphere") == 0) {
02024       egg_syntax = "<Collide> { Sphere descend }";
02025       
02026     } else if (cmp_nocase_uh(object_type, "trigger") == 0) {
02027       egg_syntax = "<Collide> { Polyset descend intangible }";
02028       
02029     } else if (cmp_nocase_uh(object_type, "trigger_sphere") == 0) {
02030       egg_syntax = "<Collide> { Sphere descend intangible }";
02031       
02032     } else if (cmp_nocase_uh(object_type, "eye_trigger") == 0) {
02033       egg_syntax = "<Collide> { Polyset descend intangible center }";
02034       
02035     } else if (cmp_nocase_uh(object_type, "bubble") == 0) {
02036       egg_syntax = "<Collide> { Sphere keep descend }";
02037       
02038     } else if (cmp_nocase_uh(object_type, "ghost") == 0) {
02039       egg_syntax = "<Scalar> collide-mask { 0 }";
02040       
02041     } else if (cmp_nocase_uh(object_type, "dcs") == 0) {
02042       egg_syntax = "<DCS> { 1 }";
02043       
02044     } else if (cmp_nocase_uh(object_type, "model") == 0) {
02045       egg_syntax = "<Model> { 1 }";
02046       
02047     } else if (cmp_nocase_uh(object_type, "backstage") == 0) {
02048       // Ignore "backstage" geometry.
02049       return false;
02050       
02051     } else {
02052       egg2pg_cat.error()
02053         << "Unknown ObjectType " << object_type << "\n";
02054       _error = true;
02055       return true;
02056     }
02057   }
02058 
02059   if (!egg_syntax.empty()) {
02060     if (!egg_group->parse_egg(egg_syntax)) {
02061       egg2pg_cat.error()
02062         << "Error while parsing definition for ObjectType "
02063         << object_type << "\n";
02064       _error = true;
02065 
02066     } else {
02067       // Now we've parsed the object type syntax, which might have
02068       // added more object types.  Recurse if necessary.
02069       if (egg_group->get_num_object_types() != 0) {
02070         if (!expand_object_types(egg_group, expanded, expanded_history)) {
02071           return false;
02072         }
02073       }
02074     }
02075   }
02076 
02077   return true;
02078 }
02079 
02080 ////////////////////////////////////////////////////////////////////
02081 //     Function: EggLoader::make_transform
02082 //       Access: Private
02083 //  Description: Walks back over the tree and applies the
02084 //               DeferredNodeProperties that were saved up along the
02085 //               way.
02086 ////////////////////////////////////////////////////////////////////
02087 CPT(TransformState) EggLoader::
02088 make_transform(const EggTransform3d *egg_transform) {
02089   // We'll build up the transform componentwise, so we preserve any
02090   // componentwise properties of the egg transform.
02091 
02092   CPT(TransformState) ts = TransformState::make_identity();
02093   int num_components = egg_transform->get_num_components();
02094   for (int i = 0; i < num_components; i++) {
02095     switch (egg_transform->get_component_type(i)) {
02096     case EggTransform3d::CT_translate:
02097       {
02098         LVector3f trans(LCAST(float, egg_transform->get_component_vector(i)));
02099         ts = TransformState::make_pos(trans)->compose(ts);
02100       }
02101       break;
02102 
02103     case EggTransform3d::CT_rotx:
02104       {
02105         LRotationf rot(LVector3f(1.0f, 0.0f, 0.0f),
02106                        (float)egg_transform->get_component_number(i));
02107         ts = TransformState::make_quat(rot)->compose(ts);
02108       }
02109       break;
02110 
02111     case EggTransform3d::CT_roty:
02112       {
02113         LRotationf rot(LVector3f(0.0f, 1.0f, 0.0f),
02114                        (float)egg_transform->get_component_number(i));
02115         ts = TransformState::make_quat(rot)->compose(ts);
02116       }
02117       break;
02118 
02119     case EggTransform3d::CT_rotz:
02120       {
02121         LRotationf rot(LVector3f(0.0f, 0.0f, 1.0f),
02122                        (float)egg_transform->get_component_number(i));
02123         ts = TransformState::make_quat(rot)->compose(ts);
02124       }
02125       break;
02126 
02127     case EggTransform3d::CT_rotate:
02128       {
02129         LRotationf rot(LCAST(float, egg_transform->get_component_vector(i)),
02130                        (float)egg_transform->get_component_number(i));
02131         ts = TransformState::make_quat(rot)->compose(ts);
02132       }
02133       break;
02134 
02135     case EggTransform3d::CT_scale:
02136       {
02137         LVecBase3f scale(LCAST(float, egg_transform->get_component_vector(i)));
02138         ts = TransformState::make_scale(scale)->compose(ts);
02139       }
02140       break;
02141 
02142     case EggTransform3d::CT_uniform_scale:
02143       {
02144         float scale = (float)egg_transform->get_component_number(i);
02145         ts = TransformState::make_scale(scale)->compose(ts);
02146       }
02147       break;
02148 
02149     case EggTransform3d::CT_matrix:
02150       {
02151         LMatrix4f mat(LCAST(float, egg_transform->get_component_matrix(i)));
02152         ts = TransformState::make_mat(mat)->compose(ts);
02153       }
02154       break;
02155 
02156     case EggTransform3d::CT_invalid:
02157       nassertr(false, ts);
02158       break;
02159     }
02160   }
02161 
02162   return ts;
02163 }

Generated on Fri May 2 00:38:12 2003 for Panda by doxygen1.3