Panda3D

eggLoader.cxx

00001 // Filename: eggLoader.cxx
00002 // Created by:  drose (26Feb02)
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 "pandabase.h"
00016 
00017 #include "eggLoader.h"
00018 #include "eggRenderState.h"
00019 #include "egg_parametrics.h"
00020 #include "config_egg2pg.h"
00021 #include "config_egg.h"
00022 #include "nodePath.h"
00023 #include "renderState.h"
00024 #include "transformState.h"
00025 #include "texturePool.h"
00026 #include "billboardEffect.h"
00027 #include "decalEffect.h"
00028 #include "colorAttrib.h"
00029 #include "textureAttrib.h"
00030 #include "materialPool.h"
00031 #include "geomNode.h"
00032 #include "geomVertexFormat.h"
00033 #include "geomVertexArrayFormat.h"
00034 #include "geomVertexData.h"
00035 #include "geomVertexWriter.h"
00036 #include "geom.h"
00037 #include "geomTriangles.h"
00038 #include "geomTristrips.h"
00039 #include "geomTrifans.h"
00040 #include "geomLines.h"
00041 #include "geomLinestrips.h"
00042 #include "geomPoints.h"
00043 #include "sequenceNode.h"
00044 #include "switchNode.h"
00045 #include "portalNode.h"
00046 #include "polylightNode.h"
00047 #include "lodNode.h"
00048 #include "modelNode.h"
00049 #include "modelRoot.h"
00050 #include "string_utils.h"
00051 #include "eggPrimitive.h"
00052 #include "eggPoint.h"
00053 #include "eggLine.h"
00054 #include "eggTextureCollection.h"
00055 #include "eggNurbsCurve.h"
00056 #include "eggNurbsSurface.h"
00057 #include "eggGroupNode.h"
00058 #include "eggGroup.h"
00059 #include "eggPolygon.h"
00060 #include "eggTriangleStrip.h"
00061 #include "eggTriangleFan.h"
00062 #include "eggBin.h"
00063 #include "eggTable.h"
00064 #include "eggBinner.h"
00065 #include "eggVertexPool.h"
00066 #include "pt_EggTexture.h"
00067 #include "characterMaker.h"
00068 #include "character.h"
00069 #include "animBundleMaker.h"
00070 #include "animBundleNode.h"
00071 #include "selectiveChildNode.h"
00072 #include "collisionNode.h"
00073 #include "collisionSphere.h"
00074 #include "collisionInvSphere.h"
00075 #include "collisionTube.h"
00076 #include "collisionPlane.h"
00077 #include "collisionPolygon.h"
00078 #include "collisionFloorMesh.h"
00079 #include "parametricCurve.h"
00080 #include "nurbsCurve.h"
00081 #include "nurbsCurveInterface.h"
00082 #include "nurbsCurveEvaluator.h"
00083 #include "nurbsSurfaceEvaluator.h"
00084 #include "ropeNode.h"
00085 #include "sheetNode.h"
00086 #include "look_at.h"
00087 #include "configVariableString.h"
00088 #include "transformBlendTable.h"
00089 #include "transformBlend.h"
00090 #include "sparseArray.h"
00091 #include "bitArray.h"
00092 #include "thread.h"
00093 #include "uvScrollNode.h"
00094 #include "textureStagePool.h"
00095 
00096 #include <ctype.h>
00097 #include <algorithm>
00098 
00099 // This class is used in make_node(EggBin *) to sort LOD instances in
00100 // order by switching distance.
00101 class LODInstance {
00102 public:
00103   LODInstance(EggNode *egg_node);
00104   bool operator < (const LODInstance &other) const {
00105     return _d->_switch_in < other._d->_switch_in;
00106   }
00107 
00108   EggNode *_egg_node;
00109   const EggSwitchConditionDistance *_d;
00110 };
00111 
00112 LODInstance::
00113 LODInstance(EggNode *egg_node) {
00114   nassertv(egg_node != NULL);
00115   _egg_node = egg_node;
00116 
00117   // We expect this egg node to be an EggGroup with an LOD
00118   // specification.  That's what the EggBinner collected together,
00119   // after all.
00120   EggGroup *egg_group = DCAST(EggGroup, egg_node);
00121   nassertv(egg_group->has_lod());
00122   const EggSwitchCondition &sw = egg_group->get_lod();
00123 
00124   // For now, this is the only kind of switch condition there is.
00125   _d = DCAST(EggSwitchConditionDistance, &sw);
00126 }
00127 
00128 
00129 ////////////////////////////////////////////////////////////////////
00130 //     Function: EggLoader::Constructor
00131 //       Access: Public
00132 //  Description:
00133 ////////////////////////////////////////////////////////////////////
00134 EggLoader::
00135 EggLoader() {
00136   // We need to enforce whatever coordinate system the user asked for.
00137   _data = new EggData;
00138   _data->set_coordinate_system(egg_coordinate_system);
00139   _error = false;
00140   _dynamic_override = false;
00141   _dynamic_override_char_maker = NULL;
00142 }
00143 
00144 ////////////////////////////////////////////////////////////////////
00145 //     Function: EggLoader::Constructor
00146 //       Access: Public
00147 //  Description: The EggLoader constructor makes a copy of the EggData
00148 //               passed in.
00149 ////////////////////////////////////////////////////////////////////
00150 EggLoader::
00151 EggLoader(const EggData *data) :
00152   _data(new EggData(*data))
00153 {
00154   _error = false;
00155   _dynamic_override = false;
00156   _dynamic_override_char_maker = NULL;
00157 }
00158 
00159 
00160 ////////////////////////////////////////////////////////////////////
00161 //     Function: EggLoader::build_graph
00162 //       Access: Public
00163 //  Description:
00164 ////////////////////////////////////////////////////////////////////
00165 void EggLoader::
00166 build_graph() {
00167   _deferred_nodes.clear();
00168 
00169   // Expand all of the ObjectType flags before we do anything else;
00170   // that might prune out large portions of the scene.
00171   if (!expand_all_object_types(_data)) {
00172     return;
00173   }
00174 
00175   // Now, load up all of the textures.
00176   load_textures();
00177 
00178   // Clean up the vertices.
00179   _data->clear_connected_shading();
00180   _data->remove_unused_vertices(true);
00181   _data->get_connected_shading();
00182   _data->unify_attributes(true, egg_flat_shading, true);
00183 
00184   // Now we need to get the connected shading again, since in unifying
00185   // the attributes we may have made vertices suddenly become
00186   // identical to each other, thereby connecting more primitives than
00187   // before.
00188   _data->clear_connected_shading();
00189   _data->remove_unused_vertices(true);
00190   _data->get_connected_shading();
00191 
00192   // Sequences and switches have special needs.  Make sure that
00193   // primitives parented directly to a sequence or switch are sorted
00194   // into sub-groups first, to prevent them being unified into a
00195   // single polyset.
00196   separate_switches(_data);
00197 
00198   if (egg_emulate_bface) {
00199     emulate_bface(_data);
00200   }
00201 
00202   // Then bin up the polysets and LOD nodes.
00203   _data->remove_invalid_primitives(true);
00204   EggBinner binner(*this);
00205   binner.make_bins(_data);
00206 
00207   //  ((EggGroupNode *)_data)->write(cerr, 0);
00208 
00209   // Now build up the scene graph.
00210   _root = new ModelRoot(_data->get_egg_filename().get_basename());
00211   make_node(_data, _root);
00212 
00213   reparent_decals();
00214   start_sequences();
00215 
00216   apply_deferred_nodes(_root, DeferredNodeProperty());
00217 }
00218 
00219 ////////////////////////////////////////////////////////////////////
00220 //     Function: EggLoader::reparent_decals
00221 //       Access: Public
00222 //  Description: For each node representing a decal base geometry
00223 //               (i.e. a node corresponding to an EggGroup with the
00224 //               decal flag set), move all of its nested geometry
00225 //               directly below the GeomNode representing the group.
00226 ////////////////////////////////////////////////////////////////////
00227 void EggLoader::
00228 reparent_decals() {
00229   ExtraNodes::const_iterator di;
00230   for (di = _decals.begin(); di != _decals.end(); ++di) {
00231     PandaNode *node = (*di);
00232     nassertv(node != (PandaNode *)NULL);
00233 
00234     // The NodePath interface is best for this.
00235     NodePath parent(node);
00236 
00237     // First, search for the GeomNode.
00238     NodePath geom_parent;
00239     int num_children = parent.get_num_children();
00240     for (int i = 0; i < num_children; i++) {
00241       NodePath child = parent.get_child(i);
00242 
00243       if (child.node()->is_of_type(GeomNode::get_class_type())) {
00244         if (!geom_parent.is_empty()) {
00245           // Oops, too many GeomNodes.
00246           egg2pg_cat.error()
00247             << "Decal onto " << parent.node()->get_name()
00248             << " uses base geometry with multiple GeomNodes.\n";
00249           _error = true;
00250         }
00251         geom_parent = child;
00252       }
00253     }
00254 
00255     if (geom_parent.is_empty()) {
00256       // No children were GeomNodes.
00257       egg2pg_cat.error()
00258         << "Ignoring decal onto " << parent.node()->get_name()
00259         << "; no geometry within group.\n";
00260       _error = true;
00261     } else {
00262       // Now reparent all of the non-GeomNodes to this node.  We have
00263       // to be careful so we don't get lost as we self-modify this
00264       // list.
00265       int i = 0;
00266       while (i < num_children) {
00267         NodePath child = parent.get_child(i);
00268 
00269         if (child.node()->is_of_type(GeomNode::get_class_type())) {
00270           i++;
00271         } else {
00272           child.reparent_to(geom_parent);
00273           num_children--;
00274         }
00275       }
00276 
00277       // Finally, set the DecalEffect on the base geometry.
00278       geom_parent.node()->set_effect(DecalEffect::make());
00279     }
00280   }
00281 }
00282 
00283 ////////////////////////////////////////////////////////////////////
00284 //     Function: EggLoader::start_sequences
00285 //       Access: Public
00286 //  Description: Starts all of the SequenceNodes we created looping.
00287 //               We have to wait until the entire graph is built up to
00288 //               do this, because the SequenceNode needs its full set
00289 //               of children before it can know how many frames to
00290 //               loop.
00291 ////////////////////////////////////////////////////////////////////
00292 void EggLoader::
00293 start_sequences() {
00294   ExtraNodes::const_iterator ni;
00295   for (ni = _sequences.begin(); ni != _sequences.end(); ++ni) {
00296     SequenceNode *node = DCAST(SequenceNode, (*ni));
00297     node->loop(true);
00298   }
00299 }
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: EggLoader::make_polyset
00303 //       Access: Public
00304 //  Description: Creates a polyset--that is, a Geom--from the
00305 //               primitives that have already been grouped into a bin.
00306 //               If transform is non-NULL, it represents the transform
00307 //               to apply to the vertices (instead of the default
00308 //               transform based on the bin's position within the
00309 //               hierarchy).
00310 ////////////////////////////////////////////////////////////////////
00311 void EggLoader::
00312 make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform,
00313              bool is_dynamic, CharacterMaker *character_maker) {
00314   if (egg_bin->empty()) {
00315     // If there are no children--no primitives--never mind.
00316     return;
00317   }
00318 
00319   // We know that all of the primitives in the bin have the same
00320   // render state, so we can get that information from the first
00321   // primitive.
00322   EggGroupNode::const_iterator ci = egg_bin->begin();
00323   nassertv(ci != egg_bin->end());
00324   CPT(EggPrimitive) first_prim = DCAST(EggPrimitive, (*ci));
00325   nassertv(first_prim != (EggPrimitive *)NULL);
00326   const EggRenderState *render_state;
00327   DCAST_INTO_V(render_state, first_prim->get_user_data(EggRenderState::get_class_type()));
00328 
00329   if (render_state->_hidden && egg_suppress_hidden) {
00330     // Eat this polyset.
00331     return;
00332   }
00333 
00334   // Generate an optimal vertex pool (or multiple vertex pools, if we
00335   // have a lot of vertex) for the polygons within just the bin.  Each
00336   // EggVertexPool translates directly to an optimal GeomVertexData
00337   // structure.
00338   EggVertexPools vertex_pools;
00339   egg_bin->rebuild_vertex_pools(vertex_pools, (unsigned int)egg_max_vertices, 
00340                                 false);
00341 
00342   if (egg_mesh) {
00343     // If we're using the mesher, mesh now.
00344     egg_bin->mesh_triangles(render_state->_flat_shaded ? EggGroupNode::T_flat_shaded : 0);
00345 
00346   } else {
00347     // If we're not using the mesher, at least triangulate any
00348     // higher-order polygons we might have.
00349     egg_bin->triangulate_polygons(EggGroupNode::T_polygon | EggGroupNode::T_convex);
00350   }
00351 
00352   // Now that we've meshed, apply the per-prim attributes onto the
00353   // vertices, so we can copy them to the GeomVertexData.
00354   egg_bin->apply_first_attribute(false);
00355   egg_bin->post_apply_flat_attribute(false);
00356 
00357   //  egg_bin->write(cerr, 0);
00358 
00359   PT(GeomNode) geom_node;
00360 
00361   // Now iterate through each EggVertexPool.  Normally, there's only
00362   // one, but if we have a really big mesh, it might have been split
00363   // into multiple vertex pools (to keep each one within the
00364   // egg_max_vertices constraint).
00365   EggVertexPools::iterator vpi;
00366   for (vpi = vertex_pools.begin(); vpi != vertex_pools.end(); ++vpi) {
00367     EggVertexPool *vertex_pool = (*vpi);
00368     vertex_pool->remove_unused_vertices();
00369     //  vertex_pool->write(cerr, 0);
00370 
00371     bool has_overall_color;
00372     Colorf overall_color;
00373     vertex_pool->check_overall_color(has_overall_color, overall_color);
00374     if (!egg_flat_colors) {
00375       // If flat colors aren't allowed, then we don't care whether
00376       // there is an overall color.  In that case, treat all vertex
00377       // pools as if they contain a combination of multiple colors.
00378       has_overall_color = false;
00379     }
00380     
00381     // Create a handful of GeomPrimitives corresponding to the various
00382     // types of primitives that reference this vertex pool.
00383     UniquePrimitives unique_primitives;
00384     Primitives primitives;
00385     for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
00386       EggPrimitive *egg_prim;
00387       DCAST_INTO_V(egg_prim, (*ci));
00388       if (egg_prim->get_pool() == vertex_pool) {
00389         make_primitive(render_state, egg_prim, unique_primitives, primitives,
00390                        has_overall_color, overall_color);
00391       }
00392     }
00393 
00394     if (!primitives.empty()) {
00395       LMatrix4d mat;
00396       if (transform != NULL) {
00397         mat = (*transform);
00398       } else {
00399         mat = egg_bin->get_vertex_to_node();
00400       }
00401       
00402       // Now convert this vertex pool to a GeomVertexData.
00403       PT(GeomVertexData) vertex_data = 
00404         make_vertex_data(render_state, vertex_pool, egg_bin, mat,
00405                          is_dynamic, character_maker, has_overall_color);
00406       nassertv(vertex_data != (GeomVertexData *)NULL);
00407 
00408       // And create a Geom to hold the primitives.
00409       PT(Geom) geom = new Geom(vertex_data);
00410       
00411       // Add each new primitive to the Geom.
00412       Primitives::const_iterator pi;
00413       for (pi = primitives.begin(); pi != primitives.end(); ++pi) {
00414         GeomPrimitive *primitive = (*pi);
00415         geom->add_primitive(primitive);
00416       }
00417       
00418         //    vertex_data->write(cerr);
00419         //    geom->write(cerr);
00420         //    render_state->_state->write(cerr, 0);
00421 
00422       // Create a new GeomNode if we haven't already.
00423       if (geom_node == (GeomNode *)NULL) {
00424         // Now, is our parent node a GeomNode, or just an ordinary
00425         // PandaNode?  If it's a GeomNode, we can add the new Geom directly
00426         // to our parent; otherwise, we need to create a new node.
00427         if (parent->is_geom_node() && !render_state->_hidden) {
00428           geom_node = DCAST(GeomNode, parent);
00429           
00430         } else {
00431           geom_node = new GeomNode(egg_bin->get_name());
00432           if (render_state->_hidden) {
00433             parent->add_stashed(geom_node);
00434           } else {
00435             parent->add_child(geom_node);
00436           }
00437         }
00438       }
00439 
00440       CPT(RenderState) geom_state = render_state->_state;
00441       if (has_overall_color) {
00442         if (!overall_color.almost_equal(Colorf(1.0f, 1.0f, 1.0f, 1.0f))) {
00443           geom_state = geom_state->add_attrib(ColorAttrib::make_flat(overall_color), -1);
00444         }
00445       } else {
00446         geom_state = geom_state->add_attrib(ColorAttrib::make_vertex(), -1);
00447       }
00448 
00449       geom_node->add_geom(geom, geom_state);
00450     }
00451   }
00452    
00453   if (geom_node != (GeomNode *)NULL && egg_show_normals) {
00454     // Create some more geometry to visualize each normal.
00455     for (vpi = vertex_pools.begin(); vpi != vertex_pools.end(); ++vpi) {
00456       EggVertexPool *vertex_pool = (*vpi);
00457       show_normals(vertex_pool, geom_node);
00458     }
00459   }
00460 }
00461 
00462 ////////////////////////////////////////////////////////////////////
00463 //     Function: EggLoader::make_transform
00464 //       Access: Public
00465 //  Description: Creates a TransformState object corresponding to the
00466 //               indicated EggTransform.
00467 ////////////////////////////////////////////////////////////////////
00468 CPT(TransformState) EggLoader::
00469 make_transform(const EggTransform *egg_transform) {
00470   // We'll build up the transform componentwise, so we preserve any
00471   // componentwise properties of the egg transform.
00472 
00473   CPT(TransformState) ts = TransformState::make_identity();
00474   int num_components = egg_transform->get_num_components();
00475   for (int i = 0; i < num_components; i++) {
00476     switch (egg_transform->get_component_type(i)) {
00477     case EggTransform::CT_translate2d:
00478       {
00479         LVecBase2f trans2d(LCAST(float, egg_transform->get_component_vec2(i)));
00480         LVecBase3f trans3d(trans2d[0], trans2d[1], 0.0f);
00481         ts = TransformState::make_pos(trans3d)->compose(ts);
00482       }
00483       break;
00484 
00485     case EggTransform::CT_translate3d:
00486       {
00487         LVecBase3f trans3d(LCAST(float, egg_transform->get_component_vec3(i)));
00488         ts = TransformState::make_pos(trans3d)->compose(ts);
00489       }
00490       break;
00491 
00492     case EggTransform::CT_rotate2d:
00493       {
00494         LRotationf rot(LVector3f(0.0f, 0.0f, 1.0f),
00495                        (float)egg_transform->get_component_number(i));
00496         ts = TransformState::make_quat(rot)->compose(ts);
00497       }
00498       break;
00499 
00500     case EggTransform::CT_rotx:
00501       {
00502         LRotationf rot(LVector3f(1.0f, 0.0f, 0.0f),
00503                        (float)egg_transform->get_component_number(i));
00504         ts = TransformState::make_quat(rot)->compose(ts);
00505       }
00506       break;
00507 
00508     case EggTransform::CT_roty:
00509       {
00510         LRotationf rot(LVector3f(0.0f, 1.0f, 0.0f),
00511                        (float)egg_transform->get_component_number(i));
00512         ts = TransformState::make_quat(rot)->compose(ts);
00513       }
00514       break;
00515 
00516     case EggTransform::CT_rotz:
00517       {
00518         LRotationf rot(LVector3f(0.0f, 0.0f, 1.0f),
00519                        (float)egg_transform->get_component_number(i));
00520         ts = TransformState::make_quat(rot)->compose(ts);
00521       }
00522       break;
00523 
00524     case EggTransform::CT_rotate3d:
00525       {
00526         LRotationf rot(LCAST(float, egg_transform->get_component_vec3(i)),
00527                        (float)egg_transform->get_component_number(i));
00528         ts = TransformState::make_quat(rot)->compose(ts);
00529       }
00530       break;
00531 
00532     case EggTransform::CT_scale2d:
00533       {
00534         LVecBase2f scale2d(LCAST(float, egg_transform->get_component_vec2(i)));
00535         LVecBase3f scale3d(scale2d[0], scale2d[1], 1.0f);
00536         ts = TransformState::make_scale(scale3d)->compose(ts);
00537       }
00538       break;
00539 
00540     case EggTransform::CT_scale3d:
00541       {
00542         LVecBase3f scale3d(LCAST(float, egg_transform->get_component_vec3(i)));
00543         ts = TransformState::make_scale(scale3d)->compose(ts);
00544       }
00545       break;
00546 
00547     case EggTransform::CT_uniform_scale:
00548       {
00549         float scale = (float)egg_transform->get_component_number(i);
00550         ts = TransformState::make_scale(scale)->compose(ts);
00551       }
00552       break;
00553 
00554     case EggTransform::CT_matrix3:
00555       {
00556         LMatrix3f m(LCAST(float, egg_transform->get_component_mat3(i)));
00557         LMatrix4f mat4(m(0, 0), m(0, 1), 0.0, m(0, 2),
00558                        m(1, 0), m(1, 1), 0.0, m(1, 2),
00559                        0.0, 0.0, 1.0, 0.0,
00560                        m(2, 0), m(2, 1), 0.0, m(2, 2));
00561 
00562         ts = TransformState::make_mat(mat4)->compose(ts);
00563       }
00564       break;
00565 
00566     case EggTransform::CT_matrix4:
00567       {
00568         LMatrix4f mat4(LCAST(float, egg_transform->get_component_mat4(i)));
00569         ts = TransformState::make_mat(mat4)->compose(ts);
00570       }
00571       break;
00572 
00573     case EggTransform::CT_invalid:
00574       nassertr(false, ts);
00575       break;
00576     }
00577   }
00578 
00579   if (ts->components_given()) {
00580     return ts;
00581   }
00582 
00583   // Finally, we uniquify all the matrix-based TransformStates we
00584   // create by complete matrix value.  The TransformState class
00585   // doesn't normally go this far, because of the cost of this
00586   // additional uniquification step, but this is the egg loader so we
00587   // don't mind spending a little bit of extra time here to get a more
00588   // optimal result.
00589   TransformStates::iterator tsi = _transform_states.insert(TransformStates::value_type(ts->get_mat(), ts)).first;
00590   
00591   return (*tsi).second;
00592 }
00593 
00594 ////////////////////////////////////////////////////////////////////
00595 //     Function: EggLoader::show_normals
00596 //       Access: Private
00597 //  Description: In the presence of egg-show-normals, generate some
00598 //               additional geometry to represent the normals,
00599 //               tangents, and binormals of each vertex.
00600 ////////////////////////////////////////////////////////////////////
00601 void EggLoader::
00602 show_normals(EggVertexPool *vertex_pool, GeomNode *geom_node) {
00603   PT(GeomPrimitive) primitive = new GeomLines(Geom::UH_static);
00604   CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
00605   PT(GeomVertexData) vertex_data =
00606     new GeomVertexData(vertex_pool->get_name(), format, Geom::UH_static);
00607 
00608   GeomVertexWriter vertex(vertex_data, InternalName::get_vertex());
00609   GeomVertexWriter color(vertex_data, InternalName::get_color());
00610 
00611   EggVertexPool::const_iterator vi;
00612   for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
00613     EggVertex *vert = (*vi);
00614     LPoint3d pos = vert->get_pos3();
00615 
00616     if (vert->has_normal()) {
00617       vertex.add_data3f(LCAST(float, pos));
00618       vertex.add_data3f(LCAST(float, pos + vert->get_normal() * egg_normal_scale));
00619       color.add_data4f(1.0f, 0.0f, 0.0f, 1.0f);
00620       color.add_data4f(1.0f, 0.0f, 0.0f, 1.0f);
00621       primitive->add_next_vertices(2);
00622       primitive->close_primitive();
00623     }
00624 
00625     // Also look for tangents and binormals in each texture coordinate
00626     // set.
00627     EggVertex::const_uv_iterator uvi;
00628     for (uvi = vert->uv_begin(); uvi != vert->uv_end(); ++uvi) {
00629       EggVertexUV *uv_obj = (*uvi);
00630       if (uv_obj->has_tangent()) {
00631         vertex.add_data3f(LCAST(float, pos));
00632         vertex.add_data3f(LCAST(float, pos + uv_obj->get_tangent() * egg_normal_scale));
00633         color.add_data4f(0.0f, 1.0f, 0.0f, 1.0f);
00634         color.add_data4f(0.0f, 1.0f, 0.0f, 1.0f);
00635         primitive->add_next_vertices(2);
00636         primitive->close_primitive();
00637       }
00638       if (uv_obj->has_binormal()) {
00639         vertex.add_data3f(LCAST(float, pos));
00640         vertex.add_data3f(LCAST(float, pos + uv_obj->get_binormal() * egg_normal_scale));
00641         color.add_data4f(0.0f, 0.0f, 1.0f, 1.0f);
00642         color.add_data4f(0.0f, 0.0f, 1.0f, 1.0f);
00643         primitive->add_next_vertices(2);
00644         primitive->close_primitive();
00645       }
00646     }
00647   }
00648 
00649   PT(Geom) geom = new Geom(vertex_data);
00650   geom->add_primitive(primitive);
00651   geom_node->add_geom(geom);
00652 }
00653 
00654 ////////////////////////////////////////////////////////////////////
00655 //     Function: EggLoader::make_nurbs_curve
00656 //       Access: Private
00657 //  Description:
00658 ////////////////////////////////////////////////////////////////////
00659 void EggLoader::
00660 make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent, 
00661                  const LMatrix4d &mat) {
00662   if (egg_load_old_curves) {
00663     // Make a NurbsCurve instead of a RopeNode (old interface).
00664     make_old_nurbs_curve(egg_curve, parent, mat);
00665     return;
00666   }
00667 
00668   assert(parent != NULL);
00669   assert(!parent->is_geom_node());
00670 
00671   PT(NurbsCurveEvaluator) nurbs = ::make_nurbs_curve(egg_curve, mat);
00672   if (nurbs == (NurbsCurveEvaluator *)NULL) {
00673     _error = true;
00674     return;
00675   }
00676 
00677   /*
00678   switch (egg_curve->get_curve_type()) {
00679   case EggCurve::CT_xyz:
00680     curve->set_curve_type(PCT_XYZ);
00681     break;
00682 
00683   case EggCurve::CT_hpr:
00684     curve->set_curve_type(PCT_HPR);
00685     break;
00686 
00687   case EggCurve::CT_t:
00688     curve->set_curve_type(PCT_T);
00689     break;
00690 
00691   default:
00692     break;
00693   }
00694   */
00695 
00696   PT(RopeNode) rope = new RopeNode(egg_curve->get_name());
00697   rope->set_curve(nurbs);
00698 
00699   // Respect the subdivision values in the egg file, if any.
00700   if (egg_curve->get_subdiv() != 0) {
00701     int subdiv_per_segment = 
00702       (int)((egg_curve->get_subdiv() + 0.5) / nurbs->get_num_segments());
00703     rope->set_num_subdiv(max(subdiv_per_segment, 1));
00704   }
00705 
00706   const EggRenderState *render_state;
00707   DCAST_INTO_V(render_state, egg_curve->get_user_data(EggRenderState::get_class_type()));
00708   if (render_state->_hidden && egg_suppress_hidden) {
00709     // Eat this primitive.
00710     return;
00711   }
00712 
00713   rope->set_state(render_state->_state);
00714   rope->set_uv_mode(RopeNode::UV_parametric);
00715 
00716   if (egg_curve->has_vertex_color()) {
00717     // If the curve had individual vertex color, enable it.
00718     rope->set_use_vertex_color(true);
00719   } else if (egg_curve->has_color()) {
00720     // Otherwise, if the curve has overall color, apply it.
00721     rope->set_attrib(ColorAttrib::make_flat(egg_curve->get_color()));
00722   }
00723 
00724   parent->add_child(rope);
00725 }
00726 
00727 ////////////////////////////////////////////////////////////////////
00728 //     Function: EggLoader::make_old_nurbs_curve
00729 //       Access: Private
00730 //  Description: This deprecated interface creates a NurbsCurve object
00731 //               for the EggNurbsCurve entry.  It will eventually be
00732 //               removed in favor of the above, which creates a
00733 //               RopeNode.
00734 ////////////////////////////////////////////////////////////////////
00735 void EggLoader::
00736 make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
00737                      const LMatrix4d &mat) {
00738   assert(parent != NULL);
00739   assert(!parent->is_geom_node());
00740 
00741   PT(ParametricCurve) curve;
00742   curve = new NurbsCurve;
00743 
00744   NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
00745   nassertv(nurbs != (NurbsCurveInterface *)NULL);
00746 
00747   if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
00748     egg2pg_cat.error()
00749       << "Invalid NURBSCurve order for " << egg_curve->get_name() << ": "
00750       << egg_curve->get_order() << "\n";
00751     _error = true;
00752     return;
00753   }
00754 
00755   nurbs->set_order(egg_curve->get_order());
00756 
00757   EggPrimitive::const_iterator pi;
00758   for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
00759     nurbs->append_cv(LCAST(float, (*pi)->get_pos4() * mat));
00760   }
00761 
00762   int num_knots = egg_curve->get_num_knots();
00763   if (num_knots != nurbs->get_num_knots()) {
00764     egg2pg_cat.error()
00765       << "Invalid NURBSCurve number of knots for "
00766       << egg_curve->get_name() << ": got " << num_knots
00767       << " knots, expected " << nurbs->get_num_knots() << "\n";
00768     _error = true;
00769     return;
00770   }
00771 
00772   for (int i = 0; i < num_knots; i++) {
00773     nurbs->set_knot(i, egg_curve->get_knot(i));
00774   }
00775 
00776   switch (egg_curve->get_curve_type()) {
00777   case EggCurve::CT_xyz:
00778     curve->set_curve_type(PCT_XYZ);
00779     break;
00780 
00781   case EggCurve::CT_hpr:
00782     curve->set_curve_type(PCT_HPR);
00783     break;
00784 
00785   case EggCurve::CT_t:
00786     curve->set_curve_type(PCT_T);
00787     break;
00788 
00789   default:
00790     break;
00791   }
00792   curve->set_name(egg_curve->get_name());
00793 
00794   if (!curve->recompute()) {
00795     egg2pg_cat.error()
00796       << "Invalid NURBSCurve " << egg_curve->get_name() << "\n";
00797     _error = true;
00798     return;
00799   }
00800 
00801   parent->add_child(curve);
00802 }
00803 
00804 ////////////////////////////////////////////////////////////////////
00805 //     Function: EggLoader::make_nurbs_surface
00806 //       Access: Private
00807 //  Description:
00808 ////////////////////////////////////////////////////////////////////
00809 void EggLoader::
00810 make_nurbs_surface(EggNurbsSurface *egg_surface, PandaNode *parent,
00811                    const LMatrix4d &mat) {
00812   assert(parent != NULL);
00813   assert(!parent->is_geom_node());
00814 
00815   PT(NurbsSurfaceEvaluator) nurbs = ::make_nurbs_surface(egg_surface, mat);
00816   if (nurbs == (NurbsSurfaceEvaluator *)NULL) {
00817     _error = true;
00818     return;
00819   }
00820 
00821   PT(SheetNode) sheet = new SheetNode(egg_surface->get_name());
00822   sheet->set_surface(nurbs);
00823 
00824   // Respect the subdivision values in the egg file, if any.
00825   if (egg_surface->get_u_subdiv() != 0) {
00826     int u_subdiv_per_segment = 
00827       (int)((egg_surface->get_u_subdiv() + 0.5) / nurbs->get_num_u_segments());
00828     sheet->set_num_u_subdiv(max(u_subdiv_per_segment, 1));
00829   }
00830   if (egg_surface->get_v_subdiv() != 0) {
00831     int v_subdiv_per_segment = 
00832       (int)((egg_surface->get_v_subdiv() + 0.5) / nurbs->get_num_v_segments());
00833     sheet->set_num_v_subdiv(max(v_subdiv_per_segment, 1));
00834   }
00835 
00836   const EggRenderState *render_state;
00837   DCAST_INTO_V(render_state, egg_surface->get_user_data(EggRenderState::get_class_type()));
00838   if (render_state->_hidden && egg_suppress_hidden) {
00839     // Eat this primitive.
00840     return;
00841   }
00842 
00843   sheet->set_state(render_state->_state);
00844 
00845   if (egg_surface->has_vertex_color()) {
00846     // If the surface had individual vertex color, enable it.
00847     sheet->set_use_vertex_color(true);
00848   } else if (egg_surface->has_color()) {
00849     // Otherwise, if the surface has overall color, apply it.
00850     sheet->set_attrib(ColorAttrib::make_flat(egg_surface->get_color()));
00851   }
00852 
00853   parent->add_child(sheet);
00854 }
00855 
00856 ////////////////////////////////////////////////////////////////////
00857 //     Function: EggLoader::load_textures
00858 //       Access: Private
00859 //  Description:
00860 ////////////////////////////////////////////////////////////////////
00861 void EggLoader::
00862 load_textures() {
00863   // First, collect all the textures that are referenced.
00864   EggTextureCollection tc;
00865   tc.find_used_textures(_data);
00866 
00867   EggTextureCollection::iterator ti;
00868   for (ti = tc.begin(); ti != tc.end(); ++ti) {
00869     PT_EggTexture egg_tex = (*ti);
00870 
00871     TextureDef def;
00872     if (load_texture(def, egg_tex)) {
00873       // Now associate the pointers, so we'll be able to look up the
00874       // Texture pointer given an EggTexture pointer, later.
00875       _textures[egg_tex] = def;
00876     }
00877   }
00878 }
00879 
00880 
00881 ////////////////////////////////////////////////////////////////////
00882 //     Function: EggLoader::load_texture
00883 //       Access: Private
00884 //  Description:
00885 ////////////////////////////////////////////////////////////////////
00886 bool EggLoader::
00887 load_texture(TextureDef &def, EggTexture *egg_tex) {
00888   // Check to see if we should reduce the number of channels in
00889   // the texture.
00890   int wanted_channels = 0;
00891   bool wanted_alpha = false;
00892   switch (egg_tex->get_format()) {
00893   case EggTexture::F_red:
00894   case EggTexture::F_green:
00895   case EggTexture::F_blue:
00896   case EggTexture::F_alpha:
00897   case EggTexture::F_luminance:
00898     wanted_channels = 1;
00899     wanted_alpha = false;
00900     break;
00901 
00902   case EggTexture::F_luminance_alpha:
00903   case EggTexture::F_luminance_alphamask:
00904     wanted_channels = 2;
00905     wanted_alpha = true;
00906     break;
00907 
00908   case EggTexture::F_rgb:
00909   case EggTexture::F_rgb12:
00910   case EggTexture::F_rgb8:
00911   case EggTexture::F_rgb5:
00912   case EggTexture::F_rgb332:
00913     wanted_channels = 3;
00914     wanted_alpha = false;
00915     break;
00916 
00917   case EggTexture::F_rgba:
00918   case EggTexture::F_rgbm:
00919   case EggTexture::F_rgba12:
00920   case EggTexture::F_rgba8:
00921   case EggTexture::F_rgba4:
00922   case EggTexture::F_rgba5:
00923     wanted_channels = 4;
00924     wanted_alpha = true;
00925     break;
00926 
00927   case EggTexture::F_unspecified:
00928     wanted_alpha = egg_tex->has_alpha_filename();
00929   }
00930 
00931   // Since some properties of the textures are inferred from the
00932   // texture files themselves (if the properties are not explicitly
00933   // specified in the egg file), then we add the textures as
00934   // dependents for the egg file.
00935   if (_record != (BamCacheRecord *)NULL) {
00936     _record->add_dependent_file(egg_tex->get_fullpath());
00937     if (egg_tex->has_alpha_filename() && wanted_alpha) {
00938       _record->add_dependent_file(egg_tex->get_alpha_fullpath());
00939     }
00940   }
00941 
00942   // By convention, the egg loader will preload the simple texture images.
00943   LoaderOptions options;
00944   if (egg_preload_simple_textures) {
00945     options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_preload_simple);
00946   }
00947 
00948   if (!egg_ignore_filters && !egg_ignore_mipmaps) {
00949     switch (egg_tex->get_minfilter()) {
00950     case EggTexture::FT_nearest:
00951     case EggTexture::FT_linear:
00952     case EggTexture::FT_unspecified:
00953       break;
00954       
00955     case EggTexture::FT_nearest_mipmap_nearest:
00956     case EggTexture::FT_linear_mipmap_nearest:
00957     case EggTexture::FT_nearest_mipmap_linear:
00958     case EggTexture::FT_linear_mipmap_linear:
00959       options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps);
00960     }
00961   }
00962 
00963   PT(Texture) tex;
00964   switch (egg_tex->get_texture_type()) {
00965   case EggTexture::TT_unspecified:
00966   case EggTexture::TT_1d_texture:
00967     options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_allow_1d);
00968     // Fall through.
00969 
00970   case EggTexture::TT_2d_texture:
00971     if (egg_tex->has_alpha_filename() && wanted_alpha) {
00972       tex = TexturePool::load_texture(egg_tex->get_fullpath(),
00973                                       egg_tex->get_alpha_fullpath(),
00974                                       wanted_channels,
00975                                       egg_tex->get_alpha_file_channel(),
00976                                       egg_tex->get_read_mipmaps(), options);
00977     } else {
00978       tex = TexturePool::load_texture(egg_tex->get_fullpath(),
00979                                       wanted_channels,
00980                                       egg_tex->get_read_mipmaps(), options);
00981     }
00982     break;
00983 
00984   case EggTexture::TT_3d_texture:
00985     tex = TexturePool::load_3d_texture(egg_tex->get_fullpath(),
00986                                        egg_tex->get_read_mipmaps(), options);
00987     break;
00988 
00989   case EggTexture::TT_cube_map:
00990     tex = TexturePool::load_cube_map(egg_tex->get_fullpath(),
00991                                      egg_tex->get_read_mipmaps(), options);
00992     break;
00993   }
00994 
00995   if (tex == (Texture *)NULL) {
00996     return false;
00997   }
00998 
00999   // Record the original filenames in the textures (as loaded from the
01000   // egg file).  These filenames will be written back to the bam file
01001   // if the bam file is written out.
01002   tex->set_filename(egg_tex->get_filename());
01003   if (egg_tex->has_alpha_filename() && wanted_alpha) {
01004     tex->set_alpha_filename(egg_tex->get_alpha_filename());
01005   }
01006 
01007   // See if there is some egg data hanging on the texture.  In
01008   // particular, the TxaFileFilter might have left that here for us.
01009   TypedReferenceCount *aux = tex->get_aux_data("egg");
01010   if (aux != (TypedReferenceCount *)NULL &&
01011       aux->is_of_type(EggTexture::get_class_type())) {
01012     EggTexture *aux_egg_tex = DCAST(EggTexture, aux);
01013 
01014     if (aux_egg_tex->get_alpha_mode() != EggTexture::AM_unspecified) {
01015       egg_tex->set_alpha_mode(aux_egg_tex->get_alpha_mode());
01016     }
01017     if (aux_egg_tex->get_format() != EggTexture::F_unspecified) {
01018       egg_tex->set_format(aux_egg_tex->get_format());
01019     }
01020     if (aux_egg_tex->get_minfilter() != EggTexture::FT_unspecified) {
01021       egg_tex->set_minfilter(aux_egg_tex->get_minfilter());
01022     }
01023     if (aux_egg_tex->get_magfilter() != EggTexture::FT_unspecified) {
01024       egg_tex->set_magfilter(aux_egg_tex->get_magfilter());
01025     }
01026     if (aux_egg_tex->has_anisotropic_degree()) {
01027       egg_tex->set_anisotropic_degree(aux_egg_tex->get_anisotropic_degree());
01028     }
01029   }
01030 
01031   apply_texture_attributes(tex, egg_tex);
01032 
01033   // Make a texture stage for the texture.
01034   PT(TextureStage) stage = make_texture_stage(egg_tex);
01035   def._texture = DCAST(TextureAttrib, TextureAttrib::make())->add_on_stage(stage, tex);
01036   def._stage = stage;
01037   def._egg_tex = egg_tex;
01038 
01039   return true;
01040 }
01041 
01042 
01043 ////////////////////////////////////////////////////////////////////
01044 //     Function: EggLoader::apply_texture_attributes
01045 //       Access: Private
01046 //  Description:
01047 ////////////////////////////////////////////////////////////////////
01048 void EggLoader::
01049 apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
01050   if (egg_tex->get_compression_mode() != EggTexture::CM_default) {
01051     tex->set_compression(convert_compression_mode(egg_tex->get_compression_mode()));
01052   }
01053 
01054   EggTexture::WrapMode wrap_u = egg_tex->determine_wrap_u();
01055   EggTexture::WrapMode wrap_v = egg_tex->determine_wrap_v();
01056   EggTexture::WrapMode wrap_w = egg_tex->determine_wrap_w();
01057 
01058   if (wrap_u != EggTexture::WM_unspecified) {
01059     tex->set_wrap_u(convert_wrap_mode(wrap_u));
01060   }
01061   if (wrap_v != EggTexture::WM_unspecified) {
01062     tex->set_wrap_v(convert_wrap_mode(wrap_v));
01063   }
01064   if (wrap_w != EggTexture::WM_unspecified) {
01065     tex->set_wrap_w(convert_wrap_mode(wrap_w));
01066   }
01067 
01068   if (egg_tex->has_border_color()) {
01069     tex->set_border_color(egg_tex->get_border_color());
01070   }
01071 
01072   switch (egg_tex->get_minfilter()) {
01073   case EggTexture::FT_nearest:
01074     tex->set_minfilter(Texture::FT_nearest);
01075     break;
01076 
01077   case EggTexture::FT_linear:
01078     if (egg_ignore_filters) {
01079       egg2pg_cat.warning()
01080         << "Ignoring minfilter request\n";
01081       tex->set_minfilter(Texture::FT_nearest);
01082     } else {
01083       tex->set_minfilter(Texture::FT_linear);
01084     }
01085     break;
01086 
01087   case EggTexture::FT_nearest_mipmap_nearest:
01088     if (egg_ignore_filters) {
01089       egg2pg_cat.warning()
01090         << "Ignoring minfilter request\n";
01091       tex->set_minfilter(Texture::FT_nearest);
01092     } else if (egg_ignore_mipmaps) {
01093       egg2pg_cat.warning()
01094         << "Ignoring mipmap request\n";
01095       tex->set_minfilter(Texture::FT_nearest);
01096     } else {
01097       tex->set_minfilter(Texture::FT_nearest_mipmap_nearest);
01098     }
01099     break;
01100 
01101   case EggTexture::FT_linear_mipmap_nearest:
01102     if (egg_ignore_filters) {
01103       egg2pg_cat.warning()
01104         << "Ignoring minfilter request\n";
01105       tex->set_minfilter(Texture::FT_nearest);
01106     } else if (egg_ignore_mipmaps) {
01107       egg2pg_cat.warning()
01108         << "Ignoring mipmap request\n";
01109       tex->set_minfilter(Texture::FT_linear);
01110     } else {
01111       tex->set_minfilter(Texture::FT_linear_mipmap_nearest);
01112     }
01113     break;
01114 
01115   case EggTexture::FT_nearest_mipmap_linear:
01116     if (egg_ignore_filters) {
01117       egg2pg_cat.warning()
01118         << "Ignoring minfilter request\n";
01119       tex->set_minfilter(Texture::FT_nearest);
01120     } else if (egg_ignore_mipmaps) {
01121       egg2pg_cat.warning()
01122         << "Ignoring mipmap request\n";
01123       tex->set_minfilter(Texture::FT_nearest);
01124     } else {
01125       tex->set_minfilter(Texture::FT_nearest_mipmap_linear);
01126     }
01127     break;
01128 
01129   case EggTexture::FT_linear_mipmap_linear:
01130     if (egg_ignore_filters) {
01131       egg2pg_cat.warning()
01132         << "Ignoring minfilter request\n";
01133       tex->set_minfilter(Texture::FT_nearest);
01134     } else if (egg_ignore_mipmaps) {
01135       egg2pg_cat.warning()
01136         << "Ignoring mipmap request\n";
01137       tex->set_minfilter(Texture::FT_linear);
01138     } else {
01139       tex->set_minfilter(Texture::FT_linear_mipmap_linear);
01140     }
01141     break;
01142 
01143   case EggTexture::FT_unspecified:
01144     break;
01145   }
01146 
01147   switch (egg_tex->get_magfilter()) {
01148   case EggTexture::FT_nearest:
01149   case EggTexture::FT_nearest_mipmap_nearest:
01150   case EggTexture::FT_nearest_mipmap_linear:
01151     tex->set_magfilter(Texture::FT_nearest);
01152     break;
01153 
01154   case EggTexture::FT_linear:
01155   case EggTexture::FT_linear_mipmap_nearest:
01156   case EggTexture::FT_linear_mipmap_linear:
01157     if (egg_ignore_filters) {
01158       egg2pg_cat.warning()
01159         << "Ignoring magfilter request\n";
01160       tex->set_magfilter(Texture::FT_nearest);
01161     } else {
01162       tex->set_magfilter(Texture::FT_linear);
01163     }
01164     break;
01165 
01166   case EggTexture::FT_unspecified:
01167     break;
01168   }
01169 
01170   if (egg_tex->has_anisotropic_degree()) {
01171     tex->set_anisotropic_degree(egg_tex->get_anisotropic_degree());
01172   }
01173 
01174   if (tex->get_num_components() == 1) {
01175     switch (egg_tex->get_format()) {
01176     case EggTexture::F_red:
01177       tex->set_format(Texture::F_red);
01178       break;
01179     case EggTexture::F_green:
01180       tex->set_format(Texture::F_green);
01181       break;
01182     case EggTexture::F_blue:
01183       tex->set_format(Texture::F_blue);
01184       break;
01185     case EggTexture::F_alpha:
01186       tex->set_format(Texture::F_alpha);
01187       break;
01188     case EggTexture::F_luminance:
01189       tex->set_format(Texture::F_luminance);
01190       break;
01191 
01192     case EggTexture::F_unspecified:
01193       break;
01194 
01195     default:
01196       egg2pg_cat.warning()
01197         << "Ignoring inappropriate format " << egg_tex->get_format()
01198         << " for 1-component texture " << egg_tex->get_name() << "\n";
01199     }
01200 
01201   } else if (tex->get_num_components() == 2) {
01202     switch (egg_tex->get_format()) {
01203     case EggTexture::F_luminance_alpha:
01204       tex->set_format(Texture::F_luminance_alpha);
01205       break;
01206 
01207     case EggTexture::F_luminance_alphamask:
01208       tex->set_format(Texture::F_luminance_alphamask);
01209       break;
01210 
01211     case EggTexture::F_unspecified:
01212       break;
01213 
01214     default:
01215       egg2pg_cat.warning()
01216         << "Ignoring inappropriate format " << egg_tex->get_format()
01217         << " for 2-component texture " << egg_tex->get_name() << "\n";
01218     }
01219 
01220   } else if (tex->get_num_components() == 3) {
01221     switch (egg_tex->get_format()) {
01222     case EggTexture::F_rgb:
01223       tex->set_format(Texture::F_rgb);
01224       break;
01225     case EggTexture::F_rgb12:
01226       if (tex->get_component_width() >= 2) {
01227         // Only do this if the component width supports it.
01228         tex->set_format(Texture::F_rgb12);
01229       } else {
01230         egg2pg_cat.warning()
01231           << "Ignoring inappropriate format " << egg_tex->get_format()
01232           << " for 8-bit texture " << egg_tex->get_name() << "\n";
01233       }
01234       break;
01235     case EggTexture::F_rgb8:
01236     case EggTexture::F_rgba8:
01237       // We'll quietly accept RGBA8 for a 3-component texture, since
01238       // flt2egg generates these for 3-component as well as for
01239       // 4-component textures.
01240       tex->set_format(Texture::F_rgb8);
01241       break;
01242     case EggTexture::F_rgb5:
01243       tex->set_format(Texture::F_rgb5);
01244       break;
01245     case EggTexture::F_rgb332:
01246       tex->set_format(Texture::F_rgb332);
01247       break;
01248 
01249     case EggTexture::F_unspecified:
01250       break;
01251 
01252     default:
01253       egg2pg_cat.warning()
01254         << "Ignoring inappropriate format " << egg_tex->get_format()
01255         << " for 3-component texture " << egg_tex->get_name() << "\n";
01256     }
01257 
01258   } else if (tex->get_num_components() == 4) {
01259     switch (egg_tex->get_format()) {
01260     case EggTexture::F_rgba:
01261       tex->set_format(Texture::F_rgba);
01262       break;
01263     case EggTexture::F_rgbm:
01264       tex->set_format(Texture::F_rgbm);
01265       break;
01266     case EggTexture::F_rgba12:
01267       if (tex->get_component_width() >= 2) {
01268         // Only do this if the component width supports it.
01269         tex->set_format(Texture::F_rgba12);
01270       } else {
01271         egg2pg_cat.warning()
01272           << "Ignoring inappropriate format " << egg_tex->get_format()
01273           << " for 8-bit texture " << egg_tex->get_name() << "\n";
01274       }
01275       break;
01276     case EggTexture::F_rgba8:
01277       tex->set_format(Texture::F_rgba8);
01278       break;
01279     case EggTexture::F_rgba4:
01280       tex->set_format(Texture::F_rgba4);
01281       break;
01282     case EggTexture::F_rgba5:
01283       tex->set_format(Texture::F_rgba5);
01284       break;
01285 
01286     case EggTexture::F_unspecified:
01287       break;
01288 
01289     default:
01290       egg2pg_cat.warning()
01291         << "Ignoring inappropriate format " << egg_tex->get_format()
01292         << " for 4-component texture " << egg_tex->get_name() << "\n";
01293     }
01294   }
01295 
01296   switch (egg_tex->get_quality_level()) {
01297   case EggTexture::QL_unspecified:
01298   case EggTexture::QL_default:
01299     tex->set_quality_level(Texture::QL_default);
01300     break;
01301 
01302   case EggTexture::QL_fastest:
01303     tex->set_quality_level(Texture::QL_fastest);
01304     break;
01305 
01306   case EggTexture::QL_normal:
01307     tex->set_quality_level(Texture::QL_normal);
01308     break;
01309 
01310   case EggTexture::QL_best:
01311     tex->set_quality_level(Texture::QL_best);
01312     break;
01313   }
01314 }
01315 
01316 ////////////////////////////////////////////////////////////////////
01317 //     Function: EggLoader::convert_compression_mode
01318 //       Access: Private
01319 //  Description: Returns the Texture::CompressionMode enum
01320 //               corresponding to the EggTexture::CompressionMode.
01321 //               Returns CM_default if the compression mode is
01322 //               unspecified.
01323 ////////////////////////////////////////////////////////////////////
01324 Texture::CompressionMode EggLoader::
01325 convert_compression_mode(EggTexture::CompressionMode compression_mode) const {
01326   switch (compression_mode) {
01327   case EggTexture::CM_off:
01328     return Texture::CM_off;
01329 
01330   case EggTexture::CM_on:
01331     return Texture::CM_on;
01332 
01333   case EggTexture::CM_fxt1:
01334     return Texture::CM_fxt1;
01335 
01336   case EggTexture::CM_dxt1:
01337     return Texture::CM_dxt1;
01338 
01339   case EggTexture::CM_dxt2:
01340     return Texture::CM_dxt2;
01341 
01342   case EggTexture::CM_dxt3:
01343     return Texture::CM_dxt3;
01344 
01345   case EggTexture::CM_dxt4:
01346     return Texture::CM_dxt4;
01347 
01348   case EggTexture::CM_dxt5:
01349     return Texture::CM_dxt5;
01350 
01351   case EggTexture::CM_default:
01352     return Texture::CM_default;
01353   }
01354 
01355   egg2pg_cat.warning()
01356     << "Unexpected texture compression flag: " << (int)compression_mode << "\n";
01357   return Texture::CM_default;
01358 }
01359 
01360 ////////////////////////////////////////////////////////////////////
01361 //     Function: EggLoader::convert_wrap_mode
01362 //       Access: Private
01363 //  Description: Returns the Texture::WrapMode enum corresponding to
01364 //               the EggTexture::WrapMode.  Returns WM_repeat if the
01365 //               wrap mode is unspecified.
01366 ////////////////////////////////////////////////////////////////////
01367 Texture::WrapMode EggLoader::
01368 convert_wrap_mode(EggTexture::WrapMode wrap_mode) const {
01369   switch (wrap_mode) {
01370   case EggTexture::WM_clamp:
01371     return Texture::WM_clamp;
01372 
01373   case EggTexture::WM_repeat:
01374     return Texture::WM_repeat;
01375 
01376   case EggTexture::WM_mirror:
01377     return Texture::WM_mirror;
01378 
01379   case EggTexture::WM_mirror_once:
01380     return Texture::WM_mirror_once;
01381 
01382   case EggTexture::WM_border_color:
01383     return Texture::WM_border_color;
01384 
01385   case EggTexture::WM_unspecified:
01386     return Texture::WM_repeat;
01387   }
01388 
01389   egg2pg_cat.warning()
01390     << "Unexpected texture wrap flag: " << (int)wrap_mode << "\n";
01391   return Texture::WM_repeat;
01392 }
01393 
01394 ////////////////////////////////////////////////////////////////////
01395 //     Function: EggLoader::make_texture_stage
01396 //       Access: Private
01397 //  Description: Creates a TextureStage object suitable for rendering
01398 //               the indicated texture.
01399 ////////////////////////////////////////////////////////////////////
01400 PT(TextureStage) EggLoader::
01401 make_texture_stage(const EggTexture *egg_tex) {
01402   // If the egg texture specifies any relevant TextureStage
01403   // properties, or if it is multitextured on top of anything else, it
01404   // gets its own texture stage; otherwise, it gets the default
01405   // texture stage.
01406   if (!egg_tex->has_stage_name() &&
01407       !egg_tex->has_uv_name() &&
01408       !egg_tex->has_color() && 
01409       (egg_tex->get_env_type() == EggTexture::ET_unspecified ||
01410        egg_tex->get_env_type() == EggTexture::ET_modulate) &&
01411       egg_tex->get_combine_mode(EggTexture::CC_rgb) == EggTexture::CM_unspecified &&
01412       egg_tex->get_combine_mode(EggTexture::CC_alpha) == EggTexture::CM_unspecified &&
01413 
01414       !egg_tex->has_priority() &&
01415       egg_tex->get_multitexture_sort() == 0 &&
01416       !egg_tex->get_saved_result()) {
01417     return TextureStage::get_default();
01418   }
01419 
01420   PT(TextureStage) stage = new TextureStage(egg_tex->get_stage_name());
01421 
01422   switch (egg_tex->get_env_type()) {
01423   case EggTexture::ET_modulate:
01424     stage->set_mode(TextureStage::M_modulate);
01425     break;
01426     
01427   case EggTexture::ET_decal:
01428     stage->set_mode(TextureStage::M_decal);
01429     break;
01430     
01431   case EggTexture::ET_blend:
01432     stage->set_mode(TextureStage::M_blend);
01433     break;
01434     
01435   case EggTexture::ET_replace:
01436     stage->set_mode(TextureStage::M_replace);
01437     break;
01438     
01439   case EggTexture::ET_add:
01440     stage->set_mode(TextureStage::M_add);
01441     break;
01442     
01443   case EggTexture::ET_blend_color_scale:
01444     stage->set_mode(TextureStage::M_blend_color_scale);
01445     break;
01446 
01447   case EggTexture::ET_modulate_glow:
01448     stage->set_mode(TextureStage::M_modulate_glow);
01449     break;
01450 
01451   case EggTexture::ET_modulate_gloss:
01452     stage->set_mode(TextureStage::M_modulate_gloss);
01453     break;
01454 
01455   case EggTexture::ET_normal:
01456     stage->set_mode(TextureStage::M_normal);
01457     break;
01458 
01459   case EggTexture::ET_normal_height:
01460     stage->set_mode(TextureStage::M_normal_height);
01461     break;
01462 
01463   case EggTexture::ET_glow:
01464     stage->set_mode(TextureStage::M_glow);
01465     break;
01466 
01467   case EggTexture::ET_gloss:
01468     stage->set_mode(TextureStage::M_gloss);
01469     break;
01470 
01471   case EggTexture::ET_height:
01472     stage->set_mode(TextureStage::M_height);
01473     break;
01474 
01475   case EggTexture::ET_selector:
01476     stage->set_mode(TextureStage::M_selector);
01477     break;
01478 
01479   case EggTexture::ET_unspecified:
01480     break;
01481   }
01482 
01483   switch (egg_tex->get_combine_mode(EggTexture::CC_rgb)) {
01484   case EggTexture::CM_replace:
01485     stage->set_combine_rgb(get_combine_mode(egg_tex, EggTexture::CC_rgb),
01486                            get_combine_source(egg_tex, EggTexture::CC_rgb, 0),
01487                            get_combine_operand(egg_tex, EggTexture::CC_rgb, 0));
01488     break;
01489 
01490   case EggTexture::CM_modulate:
01491   case EggTexture::CM_add:
01492   case EggTexture::CM_add_signed:
01493   case EggTexture::CM_subtract:
01494   case EggTexture::CM_dot3_rgb:
01495   case EggTexture::CM_dot3_rgba:
01496     stage->set_combine_rgb(get_combine_mode(egg_tex, EggTexture::CC_rgb),
01497                            get_combine_source(egg_tex, EggTexture::CC_rgb, 0),
01498                            get_combine_operand(egg_tex, EggTexture::CC_rgb, 0),
01499                            get_combine_source(egg_tex, EggTexture::CC_rgb, 1),
01500                            get_combine_operand(egg_tex, EggTexture::CC_rgb, 1));
01501     break;
01502 
01503   case EggTexture::CM_interpolate:
01504     stage->set_combine_rgb(get_combine_mode(egg_tex, EggTexture::CC_rgb),
01505                            get_combine_source(egg_tex, EggTexture::CC_rgb, 0),
01506                            get_combine_operand(egg_tex, EggTexture::CC_rgb, 0),
01507                            get_combine_source(egg_tex, EggTexture::CC_rgb, 1),
01508                            get_combine_operand(egg_tex, EggTexture::CC_rgb, 1),
01509                            get_combine_source(egg_tex, EggTexture::CC_rgb, 2),
01510                            get_combine_operand(egg_tex, EggTexture::CC_rgb, 2));
01511     break;
01512 
01513   case EggTexture::CM_unspecified:
01514     break;
01515   }
01516 
01517   switch (egg_tex->get_combine_mode(EggTexture::CC_alpha)) {
01518   case EggTexture::CM_replace:
01519     stage->set_combine_alpha(get_combine_mode(egg_tex, EggTexture::CC_alpha),
01520                              get_combine_source(egg_tex, EggTexture::CC_alpha, 0),
01521                              get_combine_operand(egg_tex, EggTexture::CC_alpha, 0));
01522     break;
01523 
01524   case EggTexture::CM_modulate:
01525   case EggTexture::CM_add:
01526   case EggTexture::CM_add_signed:
01527   case EggTexture::CM_subtract:
01528     stage->set_combine_alpha(get_combine_mode(egg_tex, EggTexture::CC_alpha),
01529                              get_combine_source(egg_tex, EggTexture::CC_alpha, 0),
01530                              get_combine_operand(egg_tex, EggTexture::CC_alpha, 0),
01531                              get_combine_source(egg_tex, EggTexture::CC_alpha, 1),
01532                              get_combine_operand(egg_tex, EggTexture::CC_alpha, 1));
01533     break;
01534     
01535   case EggTexture::CM_interpolate:
01536     stage->set_combine_alpha(get_combine_mode(egg_tex, EggTexture::CC_alpha),
01537                              get_combine_source(egg_tex, EggTexture::CC_alpha, 0),
01538                              get_combine_operand(egg_tex, EggTexture::CC_alpha, 0),
01539                              get_combine_source(egg_tex, EggTexture::CC_alpha, 1),
01540                              get_combine_operand(egg_tex, EggTexture::CC_alpha, 1),
01541                              get_combine_source(egg_tex, EggTexture::CC_alpha, 2),
01542                              get_combine_operand(egg_tex, EggTexture::CC_alpha, 2));
01543     break;
01544 
01545   case EggTexture::CM_unspecified:
01546   case EggTexture::CM_dot3_rgb:
01547   case EggTexture::CM_dot3_rgba:
01548     break;
01549   }
01550 
01551 
01552   if (egg_tex->has_uv_name()) {
01553     PT(InternalName) name = 
01554       InternalName::get_texcoord_name(egg_tex->get_uv_name());
01555     stage->set_texcoord_name(name);
01556   }
01557 
01558   if (egg_tex->has_rgb_scale()) {
01559     stage->set_rgb_scale(egg_tex->get_rgb_scale());
01560   }
01561 
01562   if (egg_tex->has_alpha_scale()) {
01563     stage->set_alpha_scale(egg_tex->get_alpha_scale());
01564   }
01565 
01566   stage->set_saved_result(egg_tex->get_saved_result());
01567 
01568   stage->set_sort(egg_tex->get_multitexture_sort() * 10);
01569 
01570   if (egg_tex->has_priority()) {
01571     stage->set_sort(egg_tex->get_priority());
01572   }
01573 
01574   if (egg_tex->has_color()) {
01575     stage->set_color(egg_tex->get_color());
01576   }
01577 
01578   return TextureStagePool::get_stage(stage);
01579 }
01580 
01581 ////////////////////////////////////////////////////////////////////
01582 //     Function: EggLoader::separate_switches
01583 //       Access: Private
01584 //  Description: Walks the tree recursively, looking for EggPrimitives
01585 //               that are children of sequence or switch nodes.  If
01586 //               any are found, they are moved within their own group
01587 //               to protect them from being flattened with their
01588 //               neighbors.
01589 ////////////////////////////////////////////////////////////////////
01590 void EggLoader::
01591 separate_switches(EggNode *egg_node) {
01592   bool parent_has_switch = false;
01593   if (egg_node->is_of_type(EggGroup::get_class_type())) {
01594     EggGroup *egg_group = DCAST(EggGroup, egg_node);
01595     parent_has_switch = egg_group->get_switch_flag();
01596   }
01597 
01598   if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
01599     EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
01600 
01601     EggGroupNode::iterator ci;
01602     ci = egg_group->begin();
01603     while (ci != egg_group->end()) {
01604       EggGroupNode::iterator cnext;
01605       cnext = ci;
01606       ++cnext;
01607 
01608       PT(EggNode) child = (*ci);
01609       if (parent_has_switch && 
01610           child->is_of_type(EggPrimitive::get_class_type())) {
01611         // Move this child under a new node.
01612         PT(EggGroup) new_group = new EggGroup(child->get_name());
01613         egg_group->replace(ci, new_group.p());
01614         new_group->add_child(child);
01615       }
01616 
01617       separate_switches(child);
01618 
01619       ci = cnext;
01620     }
01621   }
01622 }
01623 
01624 ////////////////////////////////////////////////////////////////////
01625 //     Function: EggLoader::emulate_bface
01626 //       Access: Private
01627 //  Description: Looks for EggPolygons with a bface flag applied to
01628 //               them.  Any such polygons are duplicated into a pair
01629 //               of back-to-back polygons, and the bface flag is
01630 //               removed.
01631 ////////////////////////////////////////////////////////////////////
01632 void EggLoader::
01633 emulate_bface(EggNode *egg_node) {
01634   if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
01635     EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
01636     PT(EggGroupNode) dup_prims = new EggGroupNode;
01637 
01638     EggGroupNode::iterator ci;
01639     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01640       PT(EggNode) child = (*ci);
01641       if (child->is_of_type(EggPolygon::get_class_type())) {
01642         EggPolygon *poly = DCAST(EggPolygon, child);
01643         if (poly->get_bface_flag()) {
01644           poly->set_bface_flag(false);
01645 
01646           PT(EggPolygon) dup_poly = new EggPolygon(*poly);
01647           dup_poly->reverse_vertex_ordering();
01648           if (dup_poly->has_normal()) {
01649             dup_poly->set_normal(-dup_poly->get_normal());
01650           }
01651 
01652           // Also reverse the normal on any vertices.
01653           EggPolygon::iterator vi;
01654           for (vi = dup_poly->begin(); vi != dup_poly->end(); ++vi) {
01655             EggVertex *vertex = (*vi);
01656             if (vertex->has_normal()) {
01657               EggVertex dup_vertex(*vertex);
01658               dup_vertex.set_normal(-dup_vertex.get_normal());
01659               EggVertex *new_vertex = vertex->get_pool()->create_unique_vertex(dup_vertex);
01660               if (new_vertex != vertex) {
01661                 new_vertex->copy_grefs_from(*vertex);
01662                 dup_poly->replace(vi, new_vertex);
01663               }
01664             }
01665           }
01666           dup_prims->add_child(dup_poly);
01667         }
01668       }
01669 
01670       emulate_bface(child);
01671     }
01672 
01673     // Now that we've iterated through all the children, add in any
01674     // duplicated polygons we generated.
01675     egg_group->steal_children(*dup_prims);
01676   }
01677 }
01678 
01679 ////////////////////////////////////////////////////////////////////
01680 //     Function: EggLoader::make_node
01681 //       Access: Private
01682 //  Description:
01683 ////////////////////////////////////////////////////////////////////
01684 PandaNode *EggLoader::
01685 make_node(EggNode *egg_node, PandaNode *parent) {
01686   if (egg_node->is_of_type(EggBin::get_class_type())) {
01687     return make_node(DCAST(EggBin, egg_node), parent);
01688   } else if (egg_node->is_of_type(EggGroup::get_class_type())) {
01689     return make_node(DCAST(EggGroup, egg_node), parent);
01690   } else if (egg_node->is_of_type(EggTable::get_class_type())) {
01691     return make_node(DCAST(EggTable, egg_node), parent);
01692   } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
01693     return make_node(DCAST(EggGroupNode, egg_node), parent);
01694   }
01695 
01696   return (PandaNode *)NULL;
01697 }
01698 
01699 ////////////////////////////////////////////////////////////////////
01700 //     Function: EggLoader::make_node (EggBin)
01701 //       Access: Private
01702 //  Description:
01703 ////////////////////////////////////////////////////////////////////
01704 PandaNode *EggLoader::
01705 make_node(EggBin *egg_bin, PandaNode *parent) {
01706   // An EggBin might mean an LOD node (i.e. a parent of one or more
01707   // EggGroups with LOD specifications), or it might mean a polyset
01708   // node (a parent of one or more similar EggPrimitives).
01709   switch (egg_bin->get_bin_number()) {
01710   case EggBinner::BN_polyset:
01711     make_polyset(egg_bin, parent, NULL, _dynamic_override, _dynamic_override_char_maker);
01712     return NULL;
01713 
01714   case EggBinner::BN_lod:
01715     return make_lod(egg_bin, parent);
01716 
01717   case EggBinner::BN_nurbs_surface:
01718     {
01719       nassertr(!egg_bin->empty(), NULL);
01720       EggNode *child = egg_bin->get_first_child();
01721       EggNurbsSurface *egg_nurbs;
01722       DCAST_INTO_R(egg_nurbs, child, NULL);
01723       const LMatrix4d &mat = egg_nurbs->get_vertex_to_node();
01724       make_nurbs_surface(egg_nurbs, parent, mat);
01725     }
01726     return NULL;
01727 
01728   case EggBinner::BN_nurbs_curve:
01729     {
01730       nassertr(!egg_bin->empty(), NULL);
01731       EggNode *child = egg_bin->get_first_child();
01732       EggNurbsCurve *egg_nurbs;
01733       DCAST_INTO_R(egg_nurbs, child, NULL);
01734       const LMatrix4d &mat = egg_nurbs->get_vertex_to_node();
01735       make_nurbs_curve(egg_nurbs, parent, mat);
01736     }
01737     return NULL;
01738 
01739   case EggBinner::BN_none:
01740     break;
01741   }
01742 
01743   // Shouldn't get here.
01744   return (PandaNode *)NULL;
01745 }
01746 
01747 ////////////////////////////////////////////////////////////////////
01748 //     Function: EggLoader::make_lod
01749 //       Access: Private
01750 //  Description:
01751 ////////////////////////////////////////////////////////////////////
01752 PandaNode *EggLoader::
01753 make_lod(EggBin *egg_bin, PandaNode *parent) {
01754   PT(LODNode) lod_node = LODNode::make_default_lod(egg_bin->get_name());
01755 
01756   pvector<LODInstance> instances;
01757   
01758   EggGroup::const_iterator ci;
01759   for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
01760     LODInstance instance(*ci);
01761     instances.push_back(instance);
01762   }
01763   
01764   // Now that we've created all of our children, put them in the
01765   // proper order and tell the LOD node about them.
01766   sort(instances.begin(), instances.end());
01767   
01768   if (!instances.empty()) {
01769     // Set up the LOD node's center.  All of the children should have
01770     // the same center, because that's how we binned them.
01771     lod_node->set_center(LCAST(float, instances[0]._d->_center));
01772   }
01773   
01774   for (size_t i = 0; i < instances.size(); i++) {
01775     // Create the children in the proper order within the scene graph.
01776     const LODInstance &instance = instances[i];
01777     make_node(instance._egg_node, lod_node);
01778     
01779     // All of the children should have the same center, because that's
01780     // how we binned them.
01781     nassertr(lod_node->get_center().almost_equal
01782              (LCAST(float, instance._d->_center), 0.01), NULL);
01783     
01784     // Tell the LOD node about this child's switching distances.
01785     lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out);
01786   }
01787 
01788   _groups[egg_bin] = lod_node;
01789   return create_group_arc(egg_bin, parent, lod_node);
01790 }
01791 
01792 ////////////////////////////////////////////////////////////////////
01793 //     Function: EggLoader::make_node (EggGroup)
01794 //       Access: Private
01795 //  Description:
01796 ////////////////////////////////////////////////////////////////////
01797 PandaNode *EggLoader::
01798 make_node(EggGroup *egg_group, PandaNode *parent) {
01799   PT(PandaNode) node = NULL;
01800 
01801   if (egg_group->get_dart_type() != EggGroup::DT_none) {
01802     // A group with the <Dart> flag set means to create a character.
01803     bool structured = (egg_group->get_dart_type() == EggGroup::DT_structured);
01804    
01805     CharacterMaker char_maker(egg_group, *this, structured);  
01806 
01807     node = char_maker.make_node();
01808     if(structured) {
01809       //we're going to generate the rest of the children normally
01810       //except we'll be making dynamic geometry
01811       _dynamic_override = true;
01812       _dynamic_override_char_maker = &char_maker;
01813       EggGroup::const_iterator ci;
01814       for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01815         make_node(*ci, node);
01816       }
01817       _dynamic_override_char_maker = NULL;
01818       _dynamic_override = false;
01819     }
01820 
01821   } else if (egg_group->get_cs_type() != EggGroup::CST_none) {
01822     // A collision group: create collision geometry.
01823     node = new CollisionNode(egg_group->get_name());
01824 
01825     make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p());
01826     if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
01827       // If we also specified to keep the geometry, continue the
01828       // traversal.  In this case, we create a new PandaNode to be the
01829       // parent of the visible geometry and the collision geometry.
01830       PandaNode *combined = new PandaNode("");
01831       parent->add_child(combined);
01832       combined->add_child(node);
01833       node = combined;
01834       
01835       EggGroup::const_iterator ci;
01836       for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01837         make_node(*ci, combined);
01838       }
01839     }
01840 
01841     node = create_group_arc(egg_group, parent, node);
01842     return node;
01843 
01844   } else if (egg_group->get_portal_flag()) {
01845     // Create a portal instead of a regular polyset.  Scan the
01846     // children of this node looking for a polygon, similar to the
01847     // collision polygon case, above.
01848     PortalNode *pnode = new PortalNode(egg_group->get_name());
01849     node = pnode;
01850 
01851     set_portal_polygon(egg_group, pnode);
01852     if (pnode->get_num_vertices() == 0) {
01853       egg2pg_cat.warning()
01854         << "Portal " << egg_group->get_name() << " has no vertices!\n";
01855     }
01856     
01857   } else if (egg_group->get_polylight_flag()) {
01858     // Create a polylight instead of a regular polyset.
01859     // use make_sphere to get the center, radius and color
01860     //egg2pg_cat.debug() << "polylight node\n";
01861     LPoint3f center;
01862     Colorf color;
01863     float radius;
01864     
01865     if (!make_sphere(egg_group, EggGroup::CF_none, center, radius, color)) {
01866       egg2pg_cat.warning()
01867         << "Polylight " << egg_group->get_name() << " make_sphere failed!\n";
01868     }
01869     PolylightNode *pnode = new PolylightNode(egg_group->get_name());
01870     pnode->set_pos(center);
01871     pnode->set_color(color);
01872     pnode->set_radius(radius);
01873     node = pnode;
01874     
01875   } else if (egg_group->get_switch_flag()) {
01876     if (egg_group->get_switch_fps() != 0.0) {
01877       // Create a sequence node.
01878       node = new SequenceNode(egg_group->get_name());
01879       ((SequenceNode *)node.p())->set_frame_rate(egg_group->get_switch_fps());
01880       _sequences.insert(node);
01881     } else {
01882       // Create a switch node.
01883       node = new SwitchNode(egg_group->get_name());
01884     }
01885       
01886     EggGroup::const_iterator ci;
01887     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01888       make_node(*ci, node);
01889     }
01890   } else if (egg_group->has_scrolling_uvs()) {
01891     node = new UvScrollNode(egg_group->get_name(), egg_group->get_scroll_u(), egg_group->get_scroll_v(), egg_group->get_scroll_r());
01892     
01893     EggGroup::const_iterator ci;
01894     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01895       make_node(*ci, node);
01896     }
01897     
01898   } else if (egg_group->get_model_flag() || egg_group->has_dcs_type()) {
01899     // A model or DCS flag; create a model node.
01900     node = new ModelNode(egg_group->get_name());
01901     switch (egg_group->get_dcs_type()) {
01902     case EggGroup::DC_net:
01903       DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_net);
01904       break;
01905 
01906     case EggGroup::DC_no_touch:
01907       DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_no_touch);
01908       break;
01909 
01910     case EggGroup::DC_local:
01911     case EggGroup::DC_default:
01912       DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_local);
01913       break;
01914 
01915     case EggGroup::DC_none:
01916     case EggGroup::DC_unspecified:
01917       break;
01918     }
01919 
01920     EggGroup::const_iterator ci;
01921     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01922       make_node(*ci, node);
01923     }
01924 
01925   } else {
01926     // A normal group; just create a normal node, and traverse.  But
01927     // if all of the children of this group are polysets, anticipate
01928     // this for the benefit of smaller grouping, and create a single
01929     // GeomNode for all of the children.
01930     bool all_polysets = false;
01931     bool any_hidden = false;
01932 
01933     // We don't want to ever create a GeomNode under a "decal" flag,
01934     // since that can confuse the decal reparenting.
01935     if (!egg_group->determine_decal()) {
01936       check_for_polysets(egg_group, all_polysets, any_hidden);
01937     }
01938 
01939     if (all_polysets && !any_hidden) {
01940       node = new GeomNode(egg_group->get_name());
01941     } else {
01942       node = new PandaNode(egg_group->get_name());
01943     }
01944 
01945     EggGroup::const_iterator ci;
01946     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
01947       make_node(*ci, node);
01948     }
01949   }
01950 
01951   if (node == (PandaNode *)NULL) {
01952     return NULL;
01953   }
01954 
01955   // Associate any instances with this node.
01956   int num_group_refs = egg_group->get_num_group_refs();
01957   for (int gri = 0; gri < num_group_refs; ++gri) {
01958     EggGroup *group_ref = egg_group->get_group_ref(gri);
01959     Groups::const_iterator gi = _groups.find(group_ref);
01960     if (gi != _groups.end()) {
01961       PandaNode *node_ref = (*gi).second;
01962       node->add_child(node_ref);
01963     }
01964   }
01965 
01966   _groups[egg_group] = node;
01967   return create_group_arc(egg_group, parent, node);
01968 }
01969 
01970 ////////////////////////////////////////////////////////////////////
01971 //     Function: EggLoader::create_group_arc
01972 //       Access: Private
01973 //  Description: Creates the arc parenting a new group to the scene
01974 //               graph, and applies any relevant attribs to the
01975 //               arc according to the EggGroup node that inspired the
01976 //               group.
01977 ////////////////////////////////////////////////////////////////////
01978 PandaNode *EggLoader::
01979 create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
01980   parent->add_child(node);
01981 
01982   // If the group had a transform, apply it to the node.
01983   if (egg_group->has_transform()) {
01984     CPT(TransformState) transform = make_transform(egg_group);
01985     node->set_transform(transform);
01986     node->set_prev_transform(transform);
01987   }
01988 
01989   // If the group has a billboard flag, apply that.
01990   switch (egg_group->get_billboard_type()) {
01991   case EggGroup::BT_point_camera_relative:
01992     node->set_effect(BillboardEffect::make_point_eye());
01993     break;
01994 
01995   case EggGroup::BT_point_world_relative:
01996     node->set_effect(BillboardEffect::make_point_world());
01997     break;
01998 
01999   case EggGroup::BT_axis:
02000     node->set_effect(BillboardEffect::make_axis());
02001     break;
02002 
02003   case EggGroup::BT_none:
02004     break;
02005   }
02006 
02007   if (egg_group->get_decal_flag()) {
02008     if (egg_ignore_decals) {
02009       egg2pg_cat.error()
02010         << "Ignoring decal flag on " << egg_group->get_name() << "\n";
02011       _error = true;
02012     }
02013 
02014     // If the group has the "decal" flag set, it means that all of the
02015     // descendant groups will be decaled onto the geometry within
02016     // this group.  This means we'll need to reparent things a bit
02017     // afterward.
02018     _decals.insert(node);
02019   }
02020 
02021   // Copy all the tags from the group onto the node.
02022   EggGroup::TagData::const_iterator ti;
02023   for (ti = egg_group->tag_begin(); ti != egg_group->tag_end(); ++ti) {
02024     node->set_tag((*ti).first, (*ti).second);
02025   }
02026 
02027   if (egg_group->get_blend_mode() != EggGroup::BM_unspecified &&
02028       egg_group->get_blend_mode() != EggGroup::BM_none) {
02029     // Apply a ColorBlendAttrib to the group.
02030     ColorBlendAttrib::Mode mode = get_color_blend_mode(egg_group->get_blend_mode());
02031     ColorBlendAttrib::Operand a = get_color_blend_operand(egg_group->get_blend_operand_a());
02032     ColorBlendAttrib::Operand b = get_color_blend_operand(egg_group->get_blend_operand_b());
02033     Colorf color = egg_group->get_blend_color();
02034     node->set_attrib(ColorBlendAttrib::make(mode, a, b, color));
02035   }
02036 
02037   // If the group specified some property that should propagate down
02038   // to the leaves, we have to remember this node and apply the
02039   // property later, after we've created the actual geometry.
02040   DeferredNodeProperty def;
02041   if (egg_group->has_collide_mask()) {
02042     def._from_collide_mask = egg_group->get_collide_mask();
02043     def._into_collide_mask = egg_group->get_collide_mask();
02044     def._flags |=
02045       DeferredNodeProperty::F_has_from_collide_mask |
02046       DeferredNodeProperty::F_has_into_collide_mask;
02047   }
02048   if (egg_group->has_from_collide_mask()) {
02049     def._from_collide_mask = egg_group->get_from_collide_mask();
02050     def._flags |= DeferredNodeProperty::F_has_from_collide_mask;
02051   }
02052   if (egg_group->has_into_collide_mask()) {
02053     def._into_collide_mask = egg_group->get_into_collide_mask();
02054     def._flags |= DeferredNodeProperty::F_has_into_collide_mask;
02055   }
02056 
02057   if (def._flags != 0) {
02058     _deferred_nodes[node] = def;
02059   }
02060 
02061   return node;
02062 }
02063 
02064 ////////////////////////////////////////////////////////////////////
02065 //     Function: EggLoader::make_node (EggTable)
02066 //       Access: Private
02067 //  Description:
02068 ////////////////////////////////////////////////////////////////////
02069 PandaNode *EggLoader::
02070 make_node(EggTable *egg_table, PandaNode *parent) {
02071   if (egg_table->get_table_type() != EggTable::TT_bundle) {
02072     // We only do anything with bundles.  Isolated tables are treated
02073     // as ordinary groups.
02074     return make_node(DCAST(EggGroupNode, egg_table), parent);
02075   }
02076 
02077   // It's an actual bundle, so make an AnimBundle from it and its
02078   // descendants.
02079   AnimBundleMaker bundle_maker(egg_table);
02080   AnimBundleNode *node = bundle_maker.make_node();
02081   parent->add_child(node);
02082   return node;
02083 }
02084 
02085 
02086 ////////////////////////////////////////////////////////////////////
02087 //     Function: EggLoader::make_node (EggGroupNode)
02088 //       Access: Private
02089 //  Description:
02090 ////////////////////////////////////////////////////////////////////
02091 PandaNode *EggLoader::
02092 make_node(EggGroupNode *egg_group, PandaNode *parent) {
02093   PandaNode *node = new PandaNode(egg_group->get_name());
02094 
02095   EggGroupNode::const_iterator ci;
02096   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
02097     make_node(*ci, node);
02098   }
02099 
02100   parent->add_child(node);
02101   return node;
02102 }
02103 
02104 ////////////////////////////////////////////////////////////////////
02105 //     Function: EggLoader::check_for_polysets
02106 //       Access: Private
02107 //  Description: Sets all_polysets true if all of the children of this
02108 //               node represent a polyset.  Sets any_hidden true if
02109 //               any of those polysets are flagged hidden.
02110 ////////////////////////////////////////////////////////////////////
02111 void EggLoader::
02112 check_for_polysets(EggGroup *egg_group, bool &all_polysets, bool &any_hidden) {
02113   all_polysets = (!egg_group->empty());
02114   any_hidden = false;
02115 
02116   EggGroup::const_iterator ci;
02117   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
02118     if ((*ci)->is_of_type(EggBin::get_class_type())) {
02119       EggBin *egg_bin = DCAST(EggBin, (*ci));
02120       if (egg_bin->get_bin_number() == EggBinner::BN_polyset) {
02121         // We know that all of the primitives in the bin have the same
02122         // render state, so we can get that information from the first
02123         // primitive.
02124         EggGroup::const_iterator bci = egg_bin->begin();
02125         nassertv(bci != egg_bin->end());
02126         const EggPrimitive *first_prim;
02127         DCAST_INTO_V(first_prim, (*bci));
02128         const EggRenderState *render_state;
02129         DCAST_INTO_V(render_state, first_prim->get_user_data(EggRenderState::get_class_type()));
02130 
02131         if (render_state->_hidden) {
02132           any_hidden = true;
02133         }
02134       } else {
02135         all_polysets = false;
02136         return;
02137       }
02138     } else if ((*ci)->is_of_type(EggGroup::get_class_type())) {
02139       // Other kinds of children, like vertex pools, comments,
02140       // textures, etc., are ignored; but groups indicate more nodes,
02141       // so if we find a nested group it means we're not all polysets.
02142       all_polysets = false;
02143       return;
02144     }
02145   }
02146 }
02147 
02148 ////////////////////////////////////////////////////////////////////
02149 //     Function: EggLoader::make_vertex_data
02150 //       Access: Private
02151 //  Description: Creates a GeomVertexData structure from the vertex
02152 //               pool, for the indicated transform space.  If a
02153 //               GeomVertexData has already been created for this
02154 //               transform, just returns it.
02155 ////////////////////////////////////////////////////////////////////
02156 PT(GeomVertexData) EggLoader::
02157 make_vertex_data(const EggRenderState *render_state, 
02158                  EggVertexPool *vertex_pool, EggNode *primitive_home,
02159                  const LMatrix4d &transform,
02160                  bool is_dynamic, CharacterMaker *character_maker,
02161                  bool ignore_color) {
02162   VertexPoolTransform vpt;
02163   vpt._vertex_pool = vertex_pool;
02164   vpt._bake_in_uvs = render_state->_bake_in_uvs;
02165   vpt._transform = transform;
02166 
02167   VertexPoolData::iterator di;
02168   di = _vertex_pool_data.find(vpt);
02169   if (di != _vertex_pool_data.end()) {
02170     return (*di).second;
02171   }
02172 
02173   // Decide on the format for the vertices.
02174   PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat;
02175   array_format->add_column
02176     (InternalName::get_vertex(), vertex_pool->get_num_dimensions(),
02177      Geom::NT_float32, Geom::C_point);
02178 
02179   if (vertex_pool->has_normals()) {
02180     array_format->add_column
02181       (InternalName::get_normal(), 3, 
02182        Geom::NT_float32, Geom::C_vector);
02183   }
02184 
02185   if (!ignore_color) {
02186     array_format->add_column
02187       (InternalName::get_color(), 1, 
02188        Geom::NT_packed_dabc, Geom::C_color);
02189   }
02190 
02191   vector_string uv_names, uvw_names, tbn_names;
02192   vertex_pool->get_uv_names(uv_names, uvw_names, tbn_names);
02193   vector_string::const_iterator ni;
02194   for (ni = uv_names.begin(); ni != uv_names.end(); ++ni) {
02195     string name = (*ni);
02196 
02197     PT(InternalName) iname = InternalName::get_texcoord_name(name);
02198 
02199     if (find(uvw_names.begin(), uvw_names.end(), name) != uvw_names.end()) {
02200       // This one actually represents 3-d texture coordinates.
02201       array_format->add_column
02202         (iname, 3, Geom::NT_float32, Geom::C_texcoord);
02203     } else {
02204       array_format->add_column
02205         (iname, 2, Geom::NT_float32, Geom::C_texcoord);
02206     }
02207   }
02208   for (ni = tbn_names.begin(); ni != tbn_names.end(); ++ni) {
02209     string name = (*ni);
02210 
02211     PT(InternalName) iname = InternalName::get_tangent_name(name);
02212     array_format->add_column
02213       (iname, 3, Geom::NT_float32, Geom::C_vector);
02214     iname = InternalName::get_binormal_name(name);
02215     array_format->add_column
02216       (iname, 3, Geom::NT_float32, Geom::C_vector);
02217   }
02218 
02219   PT(GeomVertexFormat) temp_format = new GeomVertexFormat(array_format);
02220 
02221   PT(TransformBlendTable) blend_table;
02222   PT(SliderTable) slider_table;
02223   string name = _data->get_egg_filename().get_basename_wo_extension();
02224 
02225   if (is_dynamic) {
02226     // If it's a dynamic object, we need a TransformBlendTable and
02227     // maybe a SliderTable, and additional columns in the vertex data:
02228     // one that indexes into the blend table per vertex, and also
02229     // one for each different type of morph delta.
02230 
02231     // Tell the format that we're setting it up for Panda-based
02232     // animation.
02233     GeomVertexAnimationSpec animation;
02234     animation.set_panda();
02235     temp_format->set_animation(animation);
02236 
02237     blend_table = new TransformBlendTable;
02238     blend_table->set_rows(SparseArray::lower_on(vertex_pool->size()));
02239 
02240     PT(GeomVertexArrayFormat) anim_array_format = new GeomVertexArrayFormat;
02241     anim_array_format->add_column
02242       (InternalName::get_transform_blend(), 1, 
02243        Geom::NT_uint16, Geom::C_index);
02244     temp_format->add_array(anim_array_format);
02245 
02246     pmap<string, BitArray> slider_names;
02247     EggVertexPool::const_iterator vi;
02248     for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
02249       EggVertex *vertex = (*vi);
02250       
02251       EggMorphVertexList::const_iterator mvi;
02252       for (mvi = vertex->_dxyzs.begin(); mvi != vertex->_dxyzs.end(); ++mvi) {
02253         slider_names[(*mvi).get_name()].set_bit(vertex->get_index());
02254         record_morph(anim_array_format, character_maker, (*mvi).get_name(),
02255                      InternalName::get_vertex(), 3);
02256       }
02257       if (vertex->has_normal()) {
02258         EggMorphNormalList::const_iterator mni;
02259         for (mni = vertex->_dnormals.begin(); mni != vertex->_dnormals.end(); ++mni) {
02260           slider_names[(*mni).get_name()].set_bit(vertex->get_index());
02261           record_morph(anim_array_format, character_maker, (*mni).get_name(),
02262                        InternalName::get_normal(), 3);
02263         }
02264       }
02265       if (!ignore_color && vertex->has_color()) {
02266         EggMorphColorList::const_iterator mci;
02267         for (mci = vertex->_drgbas.begin(); mci != vertex->_drgbas.end(); ++mci) {
02268           slider_names[(*mci).get_name()].set_bit(vertex->get_index());
02269           record_morph(anim_array_format, character_maker, (*mci).get_name(),
02270                        InternalName::get_color(), 4);
02271         }
02272       }
02273       EggVertex::const_uv_iterator uvi;
02274       for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
02275         EggVertexUV *egg_uv = (*uvi);
02276         string name = egg_uv->get_name();
02277         bool has_w = (find(uvw_names.begin(), uvw_names.end(), name) != uvw_names.end());
02278         PT(InternalName) iname = InternalName::get_texcoord_name(name);
02279 
02280         EggMorphTexCoordList::const_iterator mti;
02281         for (mti = egg_uv->_duvs.begin(); mti != egg_uv->_duvs.end(); ++mti) {
02282           slider_names[(*mti).get_name()].set_bit(vertex->get_index());
02283           record_morph(anim_array_format, character_maker, (*mti).get_name(),
02284                        iname, has_w ? 3 : 2);
02285         }
02286       }
02287     }
02288 
02289     if (!slider_names.empty()) {
02290       // If we have any sliders at all, create a table for them.
02291 
02292       slider_table = new SliderTable;
02293       
02294       pmap<string, BitArray>::iterator si;
02295       for (si = slider_names.begin(); si != slider_names.end(); ++si) {
02296         PT(VertexSlider) slider = character_maker->egg_to_slider((*si).first);
02297         slider_table->add_slider(slider, (*si).second);
02298       }
02299     }
02300 
02301     // We'll also assign the character name to the vertex data, so it
02302     // will show up in PStats.
02303     name = character_maker->get_name();
02304   }
02305 
02306   CPT(GeomVertexFormat) format =
02307     GeomVertexFormat::register_format(temp_format);
02308 
02309   // Now create a new GeomVertexData using the indicated format.  It
02310   // is actually correct to create it with UH_static even it
02311   // represents a dynamic object, because the vertex data itself won't
02312   // be changing--just the result of applying the animation is
02313   // dynamic.
02314   PT(GeomVertexData) vertex_data =
02315     new GeomVertexData(name, format, Geom::UH_static);
02316 
02317   vertex_data->set_transform_blend_table(blend_table);
02318   if (slider_table != (SliderTable *)NULL) {
02319     vertex_data->set_slider_table(SliderTable::register_table(slider_table));
02320   }
02321 
02322   // And fill the data from the vertex pool.
02323   EggVertexPool::const_iterator vi;
02324   for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
02325     GeomVertexWriter gvw(vertex_data);
02326     EggVertex *vertex = (*vi);
02327     gvw.set_row(vertex->get_index());
02328 
02329     gvw.set_column(InternalName::get_vertex());
02330     gvw.add_data4f(LCAST(float, vertex->get_pos4() * transform));
02331 
02332     if (is_dynamic) {
02333       EggMorphVertexList::const_iterator mvi;
02334       for (mvi = vertex->_dxyzs.begin(); mvi != vertex->_dxyzs.end(); ++mvi) {
02335         const EggMorphVertex &morph = (*mvi);
02336         CPT(InternalName) delta_name = 
02337           InternalName::get_morph(InternalName::get_vertex(), morph.get_name());
02338         gvw.set_column(delta_name);
02339         gvw.add_data3f(LCAST(float, morph.get_offset() * transform));
02340       }
02341     }
02342 
02343     if (vertex->has_normal()) {
02344       gvw.set_column(InternalName::get_normal());
02345       Normald orig_normal = vertex->get_normal();
02346       Normald transformed_normal = normalize(orig_normal * transform);
02347       gvw.add_data3f(LCAST(float, transformed_normal));
02348 
02349       if (is_dynamic) {
02350         EggMorphNormalList::const_iterator mni;
02351         for (mni = vertex->_dnormals.begin(); mni != vertex->_dnormals.end(); ++mni) {
02352           const EggMorphNormal &morph = (*mni);
02353           CPT(InternalName) delta_name = 
02354             InternalName::get_morph(InternalName::get_normal(), morph.get_name());
02355           gvw.set_column(delta_name);
02356           Normald morphed_normal = orig_normal + morph.get_offset();
02357           Normald transformed_morphed_normal = normalize(morphed_normal * transform);
02358           LVector3d delta = transformed_morphed_normal - transformed_normal;
02359           gvw.add_data3f(LCAST(float, delta));
02360         }
02361       }
02362     }
02363 
02364     if (!ignore_color && vertex->has_color()) {
02365       gvw.set_column(InternalName::get_color());
02366       gvw.add_data4f(vertex->get_color());
02367 
02368       if (is_dynamic) {
02369         EggMorphColorList::const_iterator mci;
02370         for (mci = vertex->_drgbas.begin(); mci != vertex->_drgbas.end(); ++mci) {
02371           const EggMorphColor &morph = (*mci);
02372           CPT(InternalName) delta_name = 
02373             InternalName::get_morph(InternalName::get_color(), morph.get_name());
02374           gvw.set_column(delta_name);
02375           gvw.add_data4f(morph.get_offset());
02376         }
02377       }
02378     }
02379 
02380     EggVertex::const_uv_iterator uvi;
02381     for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
02382       EggVertexUV *egg_uv = (*uvi);
02383       TexCoord3d orig_uvw = egg_uv->get_uvw();
02384       TexCoord3d uvw = egg_uv->get_uvw();
02385 
02386       string name = egg_uv->get_name();
02387       PT(InternalName) iname = InternalName::get_texcoord_name(name);
02388       gvw.set_column(iname);
02389 
02390       BakeInUVs::const_iterator buv = render_state->_bake_in_uvs.find(iname);
02391       if (buv != render_state->_bake_in_uvs.end()) {
02392         // If we are to bake in a texture matrix, do so now.
02393         uvw = uvw * (*buv).second->get_transform3d();
02394       }
02395 
02396       gvw.set_data3f(LCAST(float, uvw));
02397 
02398       if (is_dynamic) {
02399         EggMorphTexCoordList::const_iterator mti;
02400         for (mti = egg_uv->_duvs.begin(); mti != egg_uv->_duvs.end(); ++mti) {
02401           const EggMorphTexCoord &morph = (*mti);
02402           CPT(InternalName) delta_name = 
02403             InternalName::get_morph(iname, morph.get_name());
02404           gvw.set_column(delta_name);
02405           TexCoord3d duvw = morph.get_offset();
02406           if (buv != render_state->_bake_in_uvs.end()) {
02407             TexCoord3d new_uvw = orig_uvw + duvw;
02408             duvw = (new_uvw * (*buv).second->get_transform3d()) - uvw;
02409           }
02410           
02411           gvw.add_data3f(LCAST(float, duvw));
02412         }
02413       }
02414 
02415       // Also add the tangent and binormal, if present.
02416       if (egg_uv->has_tangent() && egg_uv->has_binormal()) {
02417         PT(InternalName) iname = InternalName::get_tangent_name(name);
02418         gvw.set_column(iname);
02419         if (gvw.has_column()) {
02420           LVector3d tangent = egg_uv->get_tangent();
02421           LVector3d binormal = egg_uv->get_binormal();
02422           gvw.add_data3f(LCAST(float, tangent));
02423           gvw.set_column(InternalName::get_binormal_name(name));
02424           gvw.add_data3f(LCAST(float, binormal));
02425         }          
02426       }
02427     }
02428 
02429     if (is_dynamic) {
02430       // Figure out the transforms affecting this particular vertex.
02431       TransformBlend blend;
02432       if (vertex->gref_size() == 0) {
02433         // If the vertex has no explicit membership, it belongs right
02434         // where it is.
02435         PT(VertexTransform) vt = character_maker->egg_to_transform(primitive_home);
02436         nassertr(vt != (VertexTransform *)NULL, vertex_data);
02437         blend.add_transform(vt, 1.0f);
02438       } else {
02439         // If the vertex does have an explicit membership, ignore its
02440         // parentage and assign it where it wants to be.
02441         EggVertex::GroupRef::const_iterator gri;
02442         for (gri = vertex->gref_begin(); gri != vertex->gref_end(); ++gri) {
02443           EggGroup *egg_joint = (*gri);
02444           double membership = egg_joint->get_vertex_membership(vertex);
02445           
02446           PT(VertexTransform) vt = character_maker->egg_to_transform(egg_joint);
02447           nassertr(vt != (VertexTransform *)NULL, vertex_data);
02448           blend.add_transform(vt, membership);
02449         }
02450       }
02451       blend.normalize_weights();
02452 
02453       int table_index = blend_table->add_blend(blend);
02454       gvw.set_column(InternalName::get_transform_blend());
02455       gvw.set_data1i(table_index);
02456     }
02457   }
02458 
02459   bool inserted = _vertex_pool_data.insert
02460     (VertexPoolData::value_type(vpt, vertex_data)).second;
02461   nassertr(inserted, vertex_data);
02462 
02463   Thread::consider_yield();
02464   return vertex_data;
02465 }
02466 
02467 ////////////////////////////////////////////////////////////////////
02468 //     Function: EggLoader::record_morph
02469 //       Access: Private
02470 //  Description: 
02471 ////////////////////////////////////////////////////////////////////
02472 void EggLoader::
02473 record_morph(GeomVertexArrayFormat *array_format,
02474              CharacterMaker *character_maker,
02475              const string &morph_name, InternalName *column_name,
02476              int num_components) {
02477   PT(InternalName) delta_name = 
02478     InternalName::get_morph(column_name, morph_name);
02479   if (!array_format->has_column(delta_name)) {
02480     array_format->add_column
02481       (delta_name, num_components,
02482        Geom::NT_float32, Geom::C_morph_delta);
02483   }
02484 }
02485 
02486 ////////////////////////////////////////////////////////////////////
02487 //     Function: EggLoader::make_primitive
02488 //       Access: Private
02489 //  Description: Creates a GeomPrimitive corresponding to the
02490 //               indicated EggPrimitive, and adds it to the set.
02491 ////////////////////////////////////////////////////////////////////
02492 void EggLoader::
02493 make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim, 
02494                EggLoader::UniquePrimitives &unique_primitives,
02495                EggLoader::Primitives &primitives,
02496                bool has_overall_color, const Colorf &overall_color) {
02497   PT(GeomPrimitive) primitive;
02498   if (egg_prim->is_of_type(EggPolygon::get_class_type())) {
02499     if (egg_prim->size() == 3) {
02500       primitive = new GeomTriangles(Geom::UH_static);
02501     }
02502 
02503   } else if (egg_prim->is_of_type(EggTriangleStrip::get_class_type())) {
02504     primitive = new GeomTristrips(Geom::UH_static);
02505 
02506   } else if (egg_prim->is_of_type(EggTriangleFan::get_class_type())) {
02507     primitive = new GeomTrifans(Geom::UH_static);
02508 
02509   } else if (egg_prim->is_of_type(EggLine::get_class_type())) {
02510     if (egg_prim->size() == 2) {
02511       primitive = new GeomLines(Geom::UH_static);
02512     } else {
02513       primitive = new GeomLinestrips(Geom::UH_static);
02514     }
02515 
02516   } else if (egg_prim->is_of_type(EggPoint::get_class_type())) {
02517     primitive = new GeomPoints(Geom::UH_static);
02518   }
02519 
02520   if (primitive == (GeomPrimitive *)NULL) {
02521     // Don't know how to make this kind of primitive.
02522     egg2pg_cat.warning()
02523       << "Ignoring " << egg_prim->get_type() << "\n";
02524     return;
02525   }
02526 
02527   if (render_state->_flat_shaded) {
02528     primitive->set_shade_model(GeomPrimitive::SM_flat_first_vertex);
02529 
02530   } else if (egg_prim->get_shading() == EggPrimitive::S_overall) {
02531     primitive->set_shade_model(GeomPrimitive::SM_uniform);
02532 
02533   } else {
02534     primitive->set_shade_model(GeomPrimitive::SM_smooth);
02535   }
02536 
02537   // Insert the primitive into the set, but if we already have a
02538   // primitive of that type, reset the pointer to that one instead.
02539   PrimitiveUnifier pu(primitive);
02540   pair<UniquePrimitives::iterator, bool> result =
02541     unique_primitives.insert(UniquePrimitives::value_type(pu, primitive));
02542 
02543   if (result.second) {
02544     // This was the first primitive of this type.  Store it.
02545     primitives.push_back(primitive);
02546   }
02547 
02548   GeomPrimitive *orig_prim = (*result.first).second;
02549 
02550   // Make sure we don't try to put more than egg_max_indices into any
02551   // one GeomPrimitive.
02552   if (orig_prim->get_num_vertices() + egg_prim->size() <= (unsigned int)egg_max_indices) {
02553     primitive = orig_prim;
02554 
02555   } else if (orig_prim != primitive) {
02556     // If the old primitive is full, keep the new primitive from now
02557     // on.
02558     (*result.first).second = primitive;
02559     primitives.push_back(primitive);
02560   }
02561 
02562   // Now add the vertices.
02563   EggPrimitive::const_iterator vi;
02564   for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
02565     primitive->add_vertex((*vi)->get_index());
02566   }
02567   primitive->close_primitive();
02568 }
02569 
02570 ////////////////////////////////////////////////////////////////////
02571 //     Function: EggLoader::set_portal_polygon
02572 //       Access: Private
02573 //  Description: Defines the PortalNode from the first polygon found
02574 //               within this group.
02575 ////////////////////////////////////////////////////////////////////
02576 void EggLoader::
02577 set_portal_polygon(EggGroup *egg_group, PortalNode *pnode) {
02578   pnode->clear_vertices();
02579 
02580   PT(EggPolygon) poly = find_first_polygon(egg_group);
02581   if (poly != (EggPolygon *)NULL) {
02582     LMatrix4d mat = poly->get_vertex_to_node();
02583 
02584     EggPolygon::const_iterator vi;
02585     for (vi = poly->begin(); vi != poly->end(); ++vi) {
02586       Vertexd vert = (*vi)->get_pos3() * mat;
02587       pnode->add_vertex(LCAST(float, vert));
02588     }
02589   }
02590 }
02591 
02592 ////////////////////////////////////////////////////////////////////
02593 //     Function: EggLoader::find_first_polygon
02594 //       Access: Private
02595 //  Description: Returns the first EggPolygon found at or below the
02596 //               indicated node.
02597 ////////////////////////////////////////////////////////////////////
02598 PT(EggPolygon) EggLoader::
02599 find_first_polygon(EggGroup *egg_group) {
02600   // Does this group have any polygons?
02601   EggGroup::const_iterator ci;
02602   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
02603     if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
02604       // Yes!  Return the polygon.
02605       return DCAST(EggPolygon, (*ci));
02606     }
02607   }
02608 
02609   // Well, the group had no polygons; look for a child group that
02610   // does.
02611   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
02612     if ((*ci)->is_of_type(EggGroup::get_class_type())) {
02613       EggGroup *child_group = DCAST(EggGroup, *ci);
02614       PT(EggPolygon) found = find_first_polygon(child_group);
02615       if (found != (EggPolygon *)NULL) {
02616         return found;
02617       }
02618     }
02619   }
02620 
02621   // We got nothing.
02622   return NULL;
02623 }
02624 
02625 ////////////////////////////////////////////////////////////////////
02626 //     Function: EggLoader::make_sphere
02627 //       Access: Private
02628 //  Description: Creates a single generic Sphere corresponding
02629 //               to the polygons associated with this group.
02630 //               This sphere is used by make_collision_sphere and
02631 //               Polylight sphere. It could be used for other spheres.
02632 ////////////////////////////////////////////////////////////////////
02633 bool EggLoader::
02634 make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags, 
02635             LPoint3f &center, float &radius, Colorf &color) {
02636   bool success=false;
02637   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
02638   if (geom_group != (EggGroup *)NULL) {
02639     // Collect all of the vertices.
02640     pset<EggVertex *> vertices;
02641 
02642     EggGroup::const_iterator ci;
02643     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
02644       if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
02645         EggPrimitive *prim = DCAST(EggPrimitive, *ci);
02646         EggPrimitive::const_iterator pi;
02647         for (pi = prim->begin(); pi != prim->end(); ++pi) {
02648           vertices.insert(*pi);
02649         }
02650       }
02651     }
02652 
02653     // Now average together all of the vertices to get a center.
02654     int num_vertices = 0;
02655     LPoint3d d_center(0.0, 0.0, 0.0);
02656     pset<EggVertex *>::const_iterator vi;
02657 
02658     for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
02659       EggVertex *vtx = (*vi);
02660       d_center += vtx->get_pos3();
02661       num_vertices++;
02662     }
02663 
02664     if (num_vertices > 0) {
02665       d_center /= (double)num_vertices;
02666       //egg2pg_cat.debug() << "make_sphere d_center: " << d_center << "\n";
02667 
02668       LMatrix4d mat = egg_group->get_vertex_to_node();
02669       d_center = d_center * mat;
02670 
02671       // And the furthest vertex determines the radius.
02672       double radius2 = 0.0;
02673       for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
02674         EggVertex *vtx = (*vi);
02675         LPoint3d p3 = vtx->get_pos3();
02676                 LVector3d v = p3 * mat - d_center;
02677         radius2 = max(radius2, v.length_squared());
02678       }
02679 
02680       center = LCAST(float,d_center);
02681       radius = sqrtf(radius2);
02682 
02683       //egg2pg_cat.debug() << "make_sphere radius: " << radius << "\n";
02684       vi = vertices.begin();
02685       EggVertex *clr_vtx = (*vi);
02686       color = clr_vtx->get_color();
02687       success = true;
02688     }
02689   }
02690   return success;
02691 }
02692 
02693 ////////////////////////////////////////////////////////////////////
02694 //     Function: EggLoader::make_collision_solids
02695 //       Access: Private
02696 //  Description: Creates CollisionSolids corresponding to the
02697 //               collision geometry indicated at the given node and
02698 //               below.
02699 ////////////////////////////////////////////////////////////////////
02700 void EggLoader::
02701 make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
02702                       CollisionNode *cnode) {
02703   if (egg_group->get_cs_type() != EggGroup::CST_none) {
02704     start_group = egg_group;
02705   }
02706 
02707   switch (start_group->get_cs_type()) {
02708   case EggGroup::CST_none:
02709     // No collision flags; do nothing.  Don't even traverse further.
02710     return;
02711 
02712   case EggGroup::CST_plane:
02713     make_collision_plane(egg_group, cnode, start_group->get_collide_flags());
02714     break;
02715 
02716   case EggGroup::CST_polygon:
02717     make_collision_polygon(egg_group, cnode, start_group->get_collide_flags());
02718     break;
02719 
02720   case EggGroup::CST_polyset:
02721     make_collision_polyset(egg_group, cnode, start_group->get_collide_flags());
02722     break;
02723 
02724   case EggGroup::CST_sphere:
02725     make_collision_sphere(egg_group, cnode, start_group->get_collide_flags());
02726     break;
02727 
02728   case EggGroup::CST_inv_sphere:
02729     make_collision_inv_sphere(egg_group, cnode, start_group->get_collide_flags());
02730     break;
02731 
02732   case EggGroup::CST_tube:
02733     make_collision_tube(egg_group, cnode, start_group->get_collide_flags());
02734     break;
02735 
02736   case EggGroup::CST_floor_mesh:
02737     make_collision_floor_mesh(egg_group, cnode, start_group->get_collide_flags());
02738     break;
02739   }
02740 
02741   if ((start_group->get_collide_flags() & EggGroup::CF_descend) != 0) {
02742     // Now pick up everything below.
02743     EggGroup::const_iterator ci;
02744     for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
02745       if ((*ci)->is_of_type(EggGroup::get_class_type())) {
02746         make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode);
02747       }
02748     }
02749   }
02750 }
02751 
02752 ////////////////////////////////////////////////////////////////////
02753 //     Function: EggLoader::make_collision_plane
02754 //       Access: Private
02755 //  Description: Creates a single CollisionPlane corresponding
02756 //               to the first polygon associated with this group.
02757 ////////////////////////////////////////////////////////////////////
02758 void EggLoader::
02759 make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
02760                      EggGroup::CollideFlags flags) {
02761   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
02762   if (geom_group != (EggGroup *)NULL) {
02763     EggGroup::const_iterator ci;
02764     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
02765       if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
02766         CollisionPlane *csplane =
02767           create_collision_plane(DCAST(EggPolygon, *ci), egg_group);
02768         if (csplane != (CollisionPlane *)NULL) {
02769           apply_collision_flags(csplane, flags);
02770           cnode->add_solid(csplane);
02771           return;
02772         }
02773       } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
02774         EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
02775         PT(EggGroup) temp_group = new EggGroup;
02776         if (comp->triangulate_into(temp_group)) {
02777           make_collision_plane(temp_group, cnode, flags);
02778           return;
02779         }
02780       }
02781     }
02782   }
02783 }
02784 
02785 
02786 
02787 ////////////////////////////////////////////////////////////////////
02788 //     Function: EggLoader::make_collision_floor_mesh
02789 //       Access: Private
02790 //  Description: Creates a single CollisionPolygon corresponding
02791 //               to the first polygon associated with this group.
02792 ////////////////////////////////////////////////////////////////////
02793 void EggLoader::
02794 make_collision_floor_mesh(EggGroup *egg_group, CollisionNode *cnode,
02795                        EggGroup::CollideFlags flags) {
02796 
02797   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
02798   
02799 
02800   if (geom_group != (EggGroup *)NULL) {
02801     create_collision_floor_mesh(cnode, geom_group,flags);
02802   }
02803 }
02804 
02805 ////////////////////////////////////////////////////////////////////
02806 //     Function: EggLoader::make_collision_polygon
02807 //       Access: Private
02808 //  Description: Creates a single CollisionPolygon corresponding
02809 //               to the first polygon associated with this group.
02810 ////////////////////////////////////////////////////////////////////
02811 void EggLoader::
02812 make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
02813                        EggGroup::CollideFlags flags) {
02814 
02815   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
02816   if (geom_group != (EggGroup *)NULL) {
02817     EggGroup::const_iterator ci;
02818     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
02819       if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
02820         create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
02821                                   egg_group, flags);
02822       } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
02823         EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
02824         PT(EggGroup) temp_group = new EggGroup;
02825         if (comp->triangulate_into(temp_group)) {
02826           make_collision_polygon(temp_group, cnode, flags);
02827           return;
02828         }
02829       }
02830     }
02831   }
02832 }
02833 
02834 
02835 ////////////////////////////////////////////////////////////////////
02836 //     Function: EggLoader::make_collision_polyset
02837 //       Access: Private
02838 //  Description: Creates a series of CollisionPolygons corresponding
02839 //               to the polygons associated with this group.
02840 ////////////////////////////////////////////////////////////////////
02841 void EggLoader::
02842 make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
02843                        EggGroup::CollideFlags flags) {
02844   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
02845   if (geom_group != (EggGroup *)NULL) {
02846     EggGroup::const_iterator ci;
02847     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
02848       if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
02849         create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
02850                                   egg_group, flags);
02851       } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
02852         EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
02853         PT(EggGroup) temp_group = new EggGroup;
02854         if (comp->triangulate_into(temp_group)) {
02855           make_collision_polyset(temp_group, cnode, flags);
02856         }
02857       }
02858     }
02859   }
02860 }
02861 
02862 ////////////////////////////////////////////////////////////////////
02863 //     Function: EggLoader::make_collision_sphere
02864 //       Access: Private
02865 //  Description: Creates a single CollisionSphere corresponding
02866 //               to the polygons associated with this group.
02867 ////////////////////////////////////////////////////////////////////
02868 void EggLoader::
02869 make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
02870                       EggGroup::CollideFlags flags) {
02871   LPoint3f center;
02872   float radius;
02873   Colorf dummycolor;
02874   if (make_sphere(egg_group, flags, center, radius, dummycolor)) {
02875     CollisionSphere *cssphere =
02876       new CollisionSphere(center, radius);
02877     apply_collision_flags(cssphere, flags);
02878     cnode->add_solid(cssphere);
02879   }
02880 }
02881 
02882 ////////////////////////////////////////////////////////////////////
02883 //     Function: EggLoader::make_collision_inv_sphere
02884 //       Access: Private
02885 //  Description: Creates a single CollisionInvSphere corresponding
02886 //               to the polygons associated with this group.
02887 ////////////////////////////////////////////////////////////////////
02888 void EggLoader::
02889 make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode,
02890                           EggGroup::CollideFlags flags) {
02891   LPoint3f center;
02892   float radius;
02893   Colorf dummycolor;
02894   if (make_sphere(egg_group, flags, center, radius, dummycolor)) {
02895     CollisionInvSphere *cssphere =
02896       new CollisionInvSphere(center, radius);
02897     apply_collision_flags(cssphere, flags);
02898     cnode->add_solid(cssphere);
02899   }
02900 }
02901 
02902 ////////////////////////////////////////////////////////////////////
02903 //     Function: EggLoader::make_collision_tube
02904 //       Access: Private
02905 //  Description: Creates a single CollisionTube corresponding
02906 //               to the polygons associated with this group.
02907 ////////////////////////////////////////////////////////////////////
02908 void EggLoader::
02909 make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
02910                     EggGroup::CollideFlags flags) {
02911   EggGroup *geom_group = find_collision_geometry(egg_group, flags);
02912   if (geom_group != (EggGroup *)NULL) {
02913     // Collect all of the vertices.
02914     pset<EggVertex *> vertices;
02915 
02916     EggGroup::const_iterator ci;
02917     for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
02918       if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
02919         EggPrimitive *prim = DCAST(EggPrimitive, *ci);
02920         EggPrimitive::const_iterator pi;
02921         for (pi = prim->begin(); pi != prim->end(); ++pi) {
02922           vertices.insert(*pi);
02923         }
02924       }
02925     }
02926 
02927     // Now store the 3-d values in a vector for convenient access (and
02928     // also determine the centroid).  We compute this in node space.
02929     size_t num_vertices = vertices.size();
02930     if (num_vertices != 0) {
02931       LMatrix4d mat = egg_group->get_vertex_to_node();
02932       pvector<LPoint3d> vpos;
02933       vpos.reserve(num_vertices);
02934       
02935       LPoint3d center(0.0, 0.0, 0.0);
02936       pset<EggVertex *>::const_iterator vi;
02937       for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
02938         EggVertex *vtx = (*vi);
02939         LPoint3d pos = vtx->get_pos3() * mat;
02940         vpos.push_back(pos);
02941         center += pos;
02942       }
02943       center /= (double)num_vertices;
02944 
02945       // Now that we have the centroid, we have to try to figure out
02946       // the cylinder's major axis.  Start by finding a point farthest
02947       // from the centroid.
02948       size_t i;
02949       double radius2 = 0.0;
02950       LPoint3d far_a = center;
02951       for (i = 0; i < num_vertices; i++) {
02952         double dist2 = (vpos[i] - center).length_squared();
02953         if (dist2 > radius2) {
02954           radius2 = dist2;
02955           far_a = vpos[i];
02956         }
02957       }
02958 
02959       // The point we have found above, far_a, must be one one of the
02960       // endcaps.  Now find another point, far_b, that is the farthest
02961       // from far_a.  This will be a point on the other endcap.
02962       radius2 = 0.0;
02963       LPoint3d far_b = center;
02964       for (i = 0; i < num_vertices; i++) {
02965         double dist2 = (vpos[i] - far_a).length_squared();
02966         if (dist2 > radius2) {
02967           radius2 = dist2;
02968           far_b = vpos[i];
02969         }
02970       }
02971 
02972       // Now we have far_a and far_b, one point on each endcap.
02973       // However, these points are not necessarily centered on the
02974       // endcaps, so we haven't figured out the cylinder's axis yet
02975       // (the line between far_a and far_b will probably pass through
02976       // the cylinder at an angle).
02977 
02978       // So we still need to determine the full set of points in each
02979       // endcap.  To do this, we pass back through the set of points,
02980       // categorizing each point into either "endcap a" or "endcap b".
02981       // We also leave a hefty chunk of points in the middle
02982       // uncategorized; this helps prevent us from getting a little
02983       // bit lopsided with points near the middle that may appear to
02984       // be closer to the wrong endcap.
02985       LPoint3d cap_a_center(0.0, 0.0, 0.0);
02986       LPoint3d cap_b_center(0.0, 0.0, 0.0);
02987       int num_a = 0;
02988       int num_b = 0;
02989 
02990       // This is the threshold length; points farther away from the
02991       // center than this are deemed to be in one endcap or the other.
02992       double center_length = (far_a - far_b).length() / 4.0;
02993       double center_length2 = center_length * center_length;
02994 
02995       for (i = 0; i < num_vertices; i++) {
02996         double dist2 = (vpos[i] - center).length_squared();
02997         if (dist2 > center_length2) {
02998           // This point is farther away from the center than
02999           // center_length; therefore it belongs in an endcap.
03000           double dist_a2 = (vpos[i] - far_a).length_squared();
03001           double dist_b2 = (vpos[i] - far_b).length_squared();
03002           if (dist_a2 < dist_b2) {
03003             // It's in endcap a.
03004             cap_a_center += vpos[i];
03005             num_a++;
03006           } else {
03007             // It's in endcap b.
03008             cap_b_center += vpos[i];
03009             num_b++;
03010           }
03011         }
03012       }
03013 
03014       if (num_a > 0 && num_b > 0) {
03015         cap_a_center /= (double)num_a;
03016         cap_b_center /= (double)num_b;
03017 
03018 
03019         // Now we finally have the major axis of the cylinder.
03020         LVector3d axis = cap_b_center - cap_a_center;
03021         axis.normalize();
03022 
03023         // If the axis is *almost* parallel with a major axis, assume
03024         // it is meant to be exactly parallel.
03025         if (IS_THRESHOLD_ZERO(axis[0], 0.01)) {
03026           axis[0] = 0.0;
03027         }
03028         if (IS_THRESHOLD_ZERO(axis[1], 0.01)) {
03029           axis[1] = 0.0;
03030         }
03031         if (IS_THRESHOLD_ZERO(axis[2], 0.01)) {
03032           axis[2] = 0.0;
03033         }
03034         axis.normalize();
03035 
03036         // Transform all of the points so that the major axis is along
03037         // the Y axis, and the origin is the center.  This is very
03038         // similar to the CollisionTube's idea of its canonical
03039         // orientation (although not exactly the same, since it is
03040         // centered on the origin instead of having point_a on the
03041         // origin).  It makes it easier to determine the length and
03042         // radius of the cylinder.
03043         LMatrix4d mat;
03044         look_at(mat, axis, LVector3d(0.0, 0.0, 1.0), CS_zup_right);
03045         mat.set_row(3, center);
03046         LMatrix4d inv_mat;
03047         inv_mat.invert_from(mat);
03048 
03049         for (i = 0; i < num_vertices; i++) {
03050           vpos[i] = vpos[i] * inv_mat;
03051         }
03052 
03053         double max_radius2 = 0.0;
03054 
03055         // Now determine the radius.
03056         for (i = 0; i < num_vertices; i++) {
03057           LVector2d v(vpos[i][0], vpos[i][2]);
03058           double radius2 = v.length_squared();
03059           if (radius2 > max_radius2) {
03060             max_radius2 = radius2;
03061           }
03062         }
03063 
03064         // And with the radius, we can determine the length.  We need
03065         // to know the radius first because we want the round endcaps
03066         // to enclose all points.
03067         double min_y = 0.0;
03068         double max_y = 0.0;
03069 
03070         for (i = 0; i < num_vertices; i++) {
03071           LVector2d v(vpos[i][0], vpos[i][2]);
03072           double radius2 = v.length_squared();
03073 
03074           if (vpos[i][1] < min_y) {
03075             // Adjust the Y pos to account for the point's distance
03076             // from the axis.
03077             double factor = sqrt(max_radius2 - radius2);
03078             min_y = min(min_y, vpos[i][1] + factor);
03079 
03080           } else if (vpos[i][1] > max_y) {
03081             double factor = sqrt(max_radius2 - radius2);
03082             max_y = max(max_y, vpos[i][1] - factor);
03083           }
03084         }
03085 
03086         double length = max_y - min_y;
03087         double radius = sqrt(max_radius2);
03088 
03089         // Finally, we have everything we need to define the cylinder.
03090         LVector3d half = axis * (length / 2.0);
03091         LPoint3d point_a = center - half;
03092         LPoint3d point_b = center + half;
03093 
03094         CollisionTube *cstube =
03095           new CollisionTube(LCAST(float, point_a), LCAST(float, point_b),
03096                             radius);
03097         apply_collision_flags(cstube, flags);
03098         cnode->add_solid(cstube);
03099       }
03100     }
03101   }
03102 }
03103 
03104 ////////////////////////////////////////////////////////////////////
03105 //     Function: EggLoader::apply_collision_flags
03106 //       Access: Private
03107 //  Description: Does funny stuff to the CollisionSolid as
03108 //               appropriate, based on the settings of the given
03109 //               CollideFlags.
03110 ////////////////////////////////////////////////////////////////////
03111 void EggLoader::
03112 apply_collision_flags(CollisionSolid *solid, EggGroup::CollideFlags flags) {
03113   if ((flags & EggGroup::CF_intangible) != 0) {
03114     solid->set_tangible(false);
03115   }
03116   if ((flags & EggGroup::CF_level) != 0) {
03117     solid->set_effective_normal(LVector3f::up());
03118   }
03119 }
03120 
03121 ////////////////////////////////////////////////////////////////////
03122 //     Function: EggLoader::find_collision_geometry
03123 //       Access: Private
03124 //  Description: Looks for the node, at or below the indicated node,
03125 //               that contains the associated collision geometry.
03126 ////////////////////////////////////////////////////////////////////
03127 EggGroup *EggLoader::
03128 find_collision_geometry(EggGroup *egg_group, EggGroup::CollideFlags flags) {
03129   if ((flags & EggGroup::CF_descend) != 0) {
03130     // If we have the "descend" instruction, we'll get to it when we
03131     // get to it.  Don't worry about it now.
03132     return egg_group;
03133   }
03134 
03135   // Does this group have any polygons?
03136   EggGroup::const_iterator ci;
03137   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
03138     if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
03139       // Yes!  Use this group.
03140       return egg_group;
03141     }
03142   }
03143 
03144   // Well, the group had no polygons; look for a child group that has
03145   // the same collision type.
03146   for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
03147     if ((*ci)->is_of_type(EggGroup::get_class_type())) {
03148       EggGroup *child_group = DCAST(EggGroup, *ci);
03149       if (child_group->get_cs_type() == egg_group->get_cs_type()) {
03150         return child_group;
03151       }
03152     }
03153   }
03154 
03155   // We got nothing.
03156   return NULL;
03157 }
03158 
03159 ////////////////////////////////////////////////////////////////////
03160 //     Function: EggLoader::create_collision_plane
03161 //       Access: Private
03162 //  Description: Creates a single CollisionPlane from the indicated
03163 //               EggPolygon.
03164 ////////////////////////////////////////////////////////////////////
03165 CollisionPlane *EggLoader::
03166 create_collision_plane(EggPolygon *egg_poly, EggGroup *parent_group) {
03167   if (!egg_poly->cleanup()) {
03168     egg2pg_cat.info()
03169       << "Ignoring degenerate collision plane in " << parent_group->get_name()
03170       << "\n";
03171     return NULL;
03172   }
03173 
03174   if (!egg_poly->is_planar()) {
03175     egg2pg_cat.warning()
03176       << "Non-planar polygon defining collision plane in "
03177       << parent_group->get_name()
03178       << "\n";
03179   }
03180 
03181   LMatrix4d mat = egg_poly->get_vertex_to_node();
03182 
03183   pvector<Vertexf> vertices;
03184   if (!egg_poly->empty()) {
03185     EggPolygon::const_iterator vi;
03186     vi = egg_poly->begin();
03187 
03188     Vertexd vert = (*vi)->get_pos3() * mat;
03189     vertices.push_back(LCAST(float, vert));
03190 
03191     Vertexd last_vert = vert;
03192     ++vi;
03193     while (vi != egg_poly->end()) {
03194       vert = (*vi)->get_pos3() * mat;
03195       if (!vert.almost_equal(last_vert)) {
03196         vertices.push_back(LCAST(float, vert));
03197       }
03198 
03199       last_vert = vert;
03200       ++vi;
03201     }
03202   }
03203 
03204   if (vertices.size() < 3) {
03205     return NULL;
03206   }
03207   Planef plane(vertices[0], vertices[1], vertices[2]);
03208   return new CollisionPlane(plane);
03209 }
03210 
03211 ////////////////////////////////////////////////////////////////////
03212 //     Function: EggLoader::create_collision_polygons
03213 //       Access: Private
03214 //  Description: Creates one or more CollisionPolygons from the
03215 //               indicated EggPolygon, and adds them to the indicated
03216 //               CollisionNode.
03217 ////////////////////////////////////////////////////////////////////
03218 void EggLoader::
03219 create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
03220                           EggGroup *parent_group,
03221                           EggGroup::CollideFlags flags) {
03222   LMatrix4d mat = egg_poly->get_vertex_to_node();
03223 
03224   PT(EggGroup) group = new EggGroup;
03225 
03226   if (!egg_poly->triangulate_into(group, false)) {
03227     egg2pg_cat.info()
03228       << "Ignoring degenerate collision polygon in "
03229       << parent_group->get_name()
03230       << "\n";
03231     return;
03232   }
03233 
03234   if (group->size() != 1) {
03235     egg2pg_cat.info()
03236       << "Triangulating concave or non-planar collision polygon in "
03237       << parent_group->get_name()
03238       << "\n";
03239   }
03240 
03241   EggGroup::iterator ci;
03242   for (ci = group->begin(); ci != group->end(); ++ci) {
03243     EggPolygon *poly = DCAST(EggPolygon, *ci);
03244 
03245     pvector<Vertexf> vertices;
03246     if (!poly->empty()) {
03247       EggPolygon::const_iterator vi;
03248       vi = poly->begin();
03249 
03250       Vertexd vert = (*vi)->get_pos3() * mat;
03251       vertices.push_back(LCAST(float, vert));
03252 
03253       Vertexd last_vert = vert;
03254       ++vi;
03255       while (vi != poly->end()) {
03256         vert = (*vi)->get_pos3() * mat;
03257         if (!vert.almost_equal(last_vert)) {
03258           vertices.push_back(LCAST(float, vert));
03259         }
03260 
03261         last_vert = vert;
03262         ++vi;
03263       }
03264     }
03265 
03266     if (vertices.size() >= 3) {
03267       const Vertexf *vertices_begin = &vertices[0];
03268       const Vertexf *vertices_end = vertices_begin + vertices.size();
03269       PT(CollisionPolygon) cspoly =
03270         new CollisionPolygon(vertices_begin, vertices_end);
03271       if (cspoly->is_valid()) {
03272         apply_collision_flags(cspoly, flags);
03273         cnode->add_solid(cspoly);
03274       }        
03275     }
03276   }
03277 }
03278 
03279 
03280 ////////////////////////////////////////////////////////////////////
03281 //     Function: EggLoader::create_collision_floor_mesh
03282 //       Access: Private
03283 //  Description: Creates a CollisionFloorMesh from the
03284 //               indicated EggPolygons, and adds it to the indicated
03285 //               CollisionNode.
03286 ////////////////////////////////////////////////////////////////////
03287 void EggLoader::
03288 create_collision_floor_mesh(CollisionNode *cnode, 
03289                           EggGroup *parent_group,
03290                           EggGroup::CollideFlags flags) {
03291 
03292   PT(EggGroup) group = new EggGroup;
03293   EggVertexPool pool("floorMesh");
03294   pool.local_object();
03295   EggGroup::const_iterator egi;
03296   for (egi = parent_group->begin(); egi != parent_group->end(); ++egi) {
03297     if ((*egi)->is_of_type(EggPolygon::get_class_type())) {
03298       EggPolygon * poly = DCAST(EggPolygon, *egi);
03299       if (!poly->triangulate_into(group, false)) {
03300         egg2pg_cat.info()
03301           << "Ignoring degenerate collision polygon in "
03302           << parent_group->get_name()
03303           << "\n";
03304         return;
03305       }
03306       
03307     } 
03308   }
03309   if(group->size() == 0) {
03310     egg2pg_cat.info()
03311       << "empty collision solid\n";
03312     return;
03313   }
03314   PT(CollisionFloorMesh) cm = new CollisionFloorMesh;
03315   pvector<CollisionFloorMesh::TriangleIndices> triangles;
03316 
03317   EggGroup::iterator ci;
03318   for (ci = group->begin(); ci != group->end(); ++ci) {
03319     EggPolygon *poly = DCAST(EggPolygon, *ci);
03320     if (poly->get_num_vertices() == 3) {
03321       CollisionFloorMesh::TriangleIndices tri;
03322       
03323       //generate a shared vertex triangle from the vertex pool
03324       tri.p1=pool.create_unique_vertex(*poly->get_vertex(0))->get_index();
03325       tri.p2=pool.create_unique_vertex(*poly->get_vertex(1))->get_index();
03326       tri.p3=pool.create_unique_vertex(*poly->get_vertex(2))->get_index();
03327       
03328       triangles.push_back(tri);
03329     } else if (poly->get_num_vertices() == 4) {
03330       //this is a case that really shouldn't happen, but appears to be required
03331       //-split up the quad int 2 tris. 
03332       CollisionFloorMesh::TriangleIndices tri;
03333       CollisionFloorMesh::TriangleIndices tri2;
03334       
03335       //generate a shared vertex triangle from the vertex pool
03336       tri.p1=pool.create_unique_vertex(*poly->get_vertex(0))->get_index();
03337       tri.p2=pool.create_unique_vertex(*poly->get_vertex(1))->get_index();
03338       tri.p3=pool.create_unique_vertex(*poly->get_vertex(2))->get_index();
03339       
03340       triangles.push_back(tri);
03341 
03342       //generate a shared vertex triangle from the vertex pool
03343       tri2.p1=tri.p1;
03344       tri2.p2=tri.p3;      
03345       tri2.p3=pool.create_unique_vertex(*poly->get_vertex(3))->get_index();
03346 
03347       
03348       triangles.push_back(tri2);
03349     }  
03350   }
03351   
03352   //Now we have a set of triangles, and a pool
03353   PT(CollisionFloorMesh) csfloor = new CollisionFloorMesh;
03354 
03355 
03356   EggVertexPool::const_iterator vi;
03357   for (vi = pool.begin(); vi != pool.end(); vi++) {
03358     csfloor->add_vertex(LCAST(float,(*vi)->get_pos3()));
03359   }
03360 
03361   pvector<CollisionFloorMesh::TriangleIndices>::iterator ti;
03362 
03363   for (ti = triangles.begin(); ti != triangles.end(); ti++) {
03364     CollisionFloorMesh::TriangleIndices triangle = *ti;
03365     csfloor->add_triangle(triangle.p1, triangle.p2, triangle.p3);
03366   }
03367   cnode->add_solid(csfloor);
03368 }
03369 
03370 
03371 ////////////////////////////////////////////////////////////////////
03372 //     Function: EggLoader::apply_deferred_nodes
03373 //       Access: Private
03374 //  Description: Walks back over the tree and applies the
03375 //               DeferredNodeProperties that were saved up along the
03376 //               way.
03377 ////////////////////////////////////////////////////////////////////
03378 void EggLoader::
03379 apply_deferred_nodes(PandaNode *node, const DeferredNodeProperty &prop) {
03380   DeferredNodeProperty next_prop(prop);
03381 
03382   // Do we have a DeferredNodeProperty associated with this node?
03383   DeferredNodes::const_iterator dni;
03384   dni = _deferred_nodes.find(node);
03385 
03386   if (dni != _deferred_nodes.end()) {
03387     const DeferredNodeProperty &def = (*dni).second;
03388     next_prop.compose(def);
03389   }
03390 
03391   // Now apply the accumulated state to the node.
03392   next_prop.apply_to_node(node);
03393 
03394   int num_children = node->get_num_children();
03395   for (int i = 0; i < num_children; i++) {
03396     apply_deferred_nodes(node->get_child(i), next_prop);
03397   }
03398 }
03399 
03400 ////////////////////////////////////////////////////////////////////
03401 //     Function: EggLoader::expand_all_object_types
03402 //       Access: Private
03403 //  Description: Walks the hierarchy and calls expand_object_types()
03404 //               on each node, to expand all of the ObjectType
03405 //               definitions in the file at once.  Also prunes any
03406 //               nodes that are flagged "backstage".
03407 //
03408 //               The return value is true if this node should be kept,
03409 //               false if it should be pruned.
03410 ////////////////////////////////////////////////////////////////////
03411 bool EggLoader::
03412 expand_all_object_types(EggNode *egg_node) {
03413   if (egg_node->is_of_type(EggGroup::get_class_type())) {
03414     EggGroup *egg_group = DCAST(EggGroup, egg_node);
03415 
03416     if (egg_group->get_num_object_types() != 0) {
03417       pset<string> expanded;
03418       pvector<string> expanded_history;
03419       if (!expand_object_types(egg_group, expanded, expanded_history)) {
03420         return false;
03421       }
03422     }
03423   }
03424 
03425   // Now recurse on children, and we might prune children from this
03426   // list as we go.
03427   if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
03428     EggGroupNode *egg_group_node = DCAST(EggGroupNode, egg_node);
03429     EggGroupNode::const_iterator ci;
03430     ci = egg_group_node->begin();
03431     while (ci != egg_group_node->end()) {
03432       EggGroupNode::const_iterator cnext = ci;
03433       ++cnext;
03434 
03435       if (!expand_all_object_types(*ci)) {
03436         // Prune this child.
03437         egg_group_node->erase(ci);
03438       }
03439       ci = cnext;
03440     }
03441   }
03442 
03443   return true;
03444 }
03445 
03446 ////////////////////////////////////////////////////////////////////
03447 //     Function: EggLoader::expand_object_types
03448 //       Access: Private
03449 //  Description: Recursively expands the group's ObjectType string(s).
03450 //               It's recursive because an ObjectType string might
03451 //               itself expand to another ObjectType string, which is
03452 //               allowed; but we don't want to get caught in a cycle.
03453 //
03454 //               The return value is true if the object type is
03455 //               expanded and the node is valid, or false if the node
03456 //               should be ignored (e.g. ObjectType "backstage").
03457 ////////////////////////////////////////////////////////////////////
03458 bool EggLoader::
03459 expand_object_types(EggGroup *egg_group, const pset<string> &expanded,
03460                     const pvector<string> &expanded_history) {
03461   int num_object_types = egg_group->get_num_object_types();
03462 
03463   // First, copy out the object types so we can recursively modify the
03464   // list.
03465   vector_string object_types;
03466   int i;
03467   for (i = 0; i < num_object_types; i++) {
03468     object_types.push_back(egg_group->get_object_type(i));
03469   }
03470   egg_group->clear_object_types();
03471 
03472   for (i = 0; i < num_object_types; i++) {
03473     string object_type = object_types[i];
03474     pset<string> new_expanded(expanded);
03475 
03476     // Check for a cycle.
03477     if (!new_expanded.insert(object_type).second) {
03478       egg2pg_cat.error()
03479         << "Cycle in ObjectType expansions:\n";
03480       pvector<string>::const_iterator pi;
03481       for (pi = expanded_history.begin();
03482            pi != expanded_history.end();
03483            ++pi) {
03484         egg2pg_cat.error(false) 
03485           << (*pi) << " -> ";
03486       }
03487       egg2pg_cat.error(false) << object_type << "\n";
03488       _error = true;
03489 
03490     } else {
03491       // No cycle; continue.
03492       pvector<string> new_expanded_history(expanded_history);
03493       new_expanded_history.push_back(object_type);
03494 
03495       if (!do_expand_object_type(egg_group, new_expanded, 
03496                                  new_expanded_history, object_type)) {
03497         // Ignorable group; stop here.
03498         return false;
03499       }
03500     }
03501   }
03502 
03503   return true;
03504 }
03505 
03506 ////////////////////////////////////////////////////////////////////
03507 //     Function: EggLoader::do_expand_object_types
03508 //       Access: Private
03509 //  Description: Further implementation of expand_object_types().
03510 ////////////////////////////////////////////////////////////////////
03511 bool EggLoader::
03512 do_expand_object_type(EggGroup *egg_group, const pset<string> &expanded,
03513                       const pvector<string> &expanded_history,
03514                       const string &object_type) {
03515   // Try to find the egg syntax that the given objecttype is
03516   // shorthand for.  First, look in the config file.
03517 
03518   ConfigVariableString egg_object_type
03519     ("egg-object-type-" + downcase(object_type), "");
03520   string egg_syntax = egg_object_type;
03521 
03522   if (!egg_object_type.has_value()) {
03523     // It wasn't defined in a config file.  Maybe it's built in?
03524     
03525     if (cmp_nocase_uh(object_type, "barrier") == 0) {
03526       egg_syntax = "<Collide> { Polyset descend }";
03527       
03528     } else if (cmp_nocase_uh(object_type, "solidpoly") == 0) {
03529       egg_syntax = "<Collide> { Polyset descend solid }";
03530       
03531     } else if (cmp_nocase_uh(object_type, "turnstile") == 0) {
03532       egg_syntax = "<Collide> { Polyset descend turnstile }";
03533       
03534     } else if (cmp_nocase_uh(object_type, "sphere") == 0) {
03535       egg_syntax = "<Collide> { Sphere descend }";
03536 
03537     } else if (cmp_nocase_uh(object_type, "tube") == 0) {
03538       egg_syntax = "<Collide> { Tube descend }";
03539       
03540     } else if (cmp_nocase_uh(object_type, "trigger") == 0) {
03541       egg_syntax = "<Collide> { Polyset descend intangible }";
03542       
03543     } else if (cmp_nocase_uh(object_type, "trigger_sphere") == 0) {
03544       egg_syntax = "<Collide> { Sphere descend intangible }";
03545       
03546     } else if (cmp_nocase_uh(object_type, "eye_trigger") == 0) {
03547       egg_syntax = "<Collide> { Polyset descend intangible center }";
03548       
03549     } else if (cmp_nocase_uh(object_type, "bubble") == 0) {
03550       egg_syntax = "<Collide> { Sphere keep descend }";
03551       
03552     } else if (cmp_nocase_uh(object_type, "ghost") == 0) {
03553       egg_syntax = "<Scalar> collide-mask { 0 }";
03554       
03555     } else if (cmp_nocase_uh(object_type, "dcs") == 0) {
03556       egg_syntax = "<DCS> { 1 }";
03557       
03558     } else if (cmp_nocase_uh(object_type, "model") == 0) {
03559       egg_syntax = "<Model> { 1 }";
03560       
03561     } else if (cmp_nocase_uh(object_type, "none") == 0) {
03562       // ObjectType "none" is a special case, meaning nothing in particular.
03563       return true;
03564       
03565     } else if (cmp_nocase_uh(object_type, "backstage") == 0) {
03566       // Ignore "backstage" geometry.
03567       return false;
03568       
03569     } else {
03570       egg2pg_cat.error()
03571         << "Unknown ObjectType " << object_type << "\n";
03572       _error = true;
03573       egg2pg_cat.debug() << "returning true\n";
03574       return true;
03575     }
03576   }
03577 
03578   if (!egg_syntax.empty()) {
03579     if (!egg_group->parse_egg(egg_syntax)) {
03580       egg2pg_cat.error()
03581         << "Error while parsing definition for ObjectType "
03582         << object_type << "\n";
03583       _error = true;
03584 
03585     } else {
03586       // Now we've parsed the object type syntax, which might have
03587       // added more object types.  Recurse if necessary.
03588       if (egg_group->get_num_object_types() != 0) {
03589         if (!expand_object_types(egg_group, expanded, expanded_history)) {
03590           return false;
03591         }
03592       }
03593     }
03594   }
03595 
03596   return true;
03597 }
03598 
03599 ////////////////////////////////////////////////////////////////////
03600 //     Function: EggLoader::get_combine_mode
03601 //       Access: Private, Static
03602 //  Description: Extracts the combine_mode from the given egg texture,
03603 //               and returns its corresponding TextureStage value.
03604 ////////////////////////////////////////////////////////////////////
03605 TextureStage::CombineMode EggLoader::
03606 get_combine_mode(const EggTexture *egg_tex, 
03607                  EggTexture::CombineChannel channel) {
03608   switch (egg_tex->get_combine_mode(channel)) {
03609   case EggTexture::CM_unspecified:
03610     // fall through
03611 
03612   case EggTexture::CM_modulate:
03613     return TextureStage::CM_modulate;
03614 
03615   case EggTexture::CM_replace:
03616     return TextureStage::CM_replace;
03617 
03618   case EggTexture::CM_add:
03619     return TextureStage::CM_add;
03620 
03621   case EggTexture::CM_add_signed:
03622     return TextureStage::CM_add_signed;
03623 
03624   case EggTexture::CM_interpolate:
03625     return TextureStage::CM_interpolate;
03626 
03627   case EggTexture::CM_subtract:
03628     return TextureStage::CM_subtract;
03629 
03630   case EggTexture::CM_dot3_rgb:
03631     return TextureStage::CM_dot3_rgb;
03632 
03633   case EggTexture::CM_dot3_rgba:
03634     return TextureStage::CM_dot3_rgba;
03635   };
03636 
03637   return TextureStage::CM_undefined;
03638 }
03639 
03640 ////////////////////////////////////////////////////////////////////
03641 //     Function: EggLoader::get_combine_source
03642 //       Access: Private, Static
03643 //  Description: Extracts the combine_source from the given egg texture,
03644 //               and returns its corresponding TextureStage value.
03645 ////////////////////////////////////////////////////////////////////
03646 TextureStage::CombineSource EggLoader::
03647 get_combine_source(const EggTexture *egg_tex, 
03648                    EggTexture::CombineChannel channel, int n) {
03649   switch (egg_tex->get_combine_source(channel, n)) {
03650   case EggTexture::CS_unspecified:
03651     // The default source if it is unspecified is based on the
03652     // parameter index.
03653     switch (n) {
03654     case 0:
03655       return TextureStage::CS_previous;
03656     case 1:
03657       return TextureStage::CS_texture;
03658     case 2:
03659       return TextureStage::CS_constant;
03660     }
03661     // Otherwise, fall through
03662 
03663   case EggTexture::CS_texture:
03664     return TextureStage::CS_texture;
03665 
03666   case EggTexture::CS_constant:
03667     return TextureStage::CS_constant;
03668 
03669   case EggTexture::CS_primary_color:
03670     return TextureStage::CS_primary_color;
03671 
03672   case EggTexture::CS_previous:
03673     return TextureStage::CS_previous;
03674 
03675   case EggTexture::CS_constant_color_scale:
03676     return TextureStage::CS_constant_color_scale;
03677 
03678   case EggTexture::CS_last_saved_result:
03679     return TextureStage::CS_last_saved_result;
03680   };
03681 
03682   return TextureStage::CS_undefined;
03683 }
03684 
03685 ////////////////////////////////////////////////////////////////////
03686 //     Function: EggLoader::get_combine_operand
03687 //       Access: Private, Static
03688 //  Description: Extracts the combine_operand from the given egg texture,
03689 //               and returns its corresponding TextureStage value.
03690 ////////////////////////////////////////////////////////////////////
03691 TextureStage::CombineOperand EggLoader::
03692 get_combine_operand(const EggTexture *egg_tex, 
03693                     EggTexture::CombineChannel channel, int n) {
03694   switch (egg_tex->get_combine_operand(channel, n)) {
03695   case EggTexture::CS_unspecified:
03696     if (channel == EggTexture::CC_rgb) {
03697       // The default operand for RGB is src_color, except for the
03698       // third parameter, which defaults to src_alpha.
03699       return n < 2 ? TextureStage::CO_src_color : TextureStage::CO_src_alpha;
03700     } else {
03701       // The default operand for alpha is always src_alpha.
03702       return TextureStage::CO_src_alpha;
03703     }
03704 
03705   case EggTexture::CO_src_color:
03706     return TextureStage::CO_src_color;
03707 
03708   case EggTexture::CO_one_minus_src_color:
03709     return TextureStage::CO_one_minus_src_color;
03710 
03711   case EggTexture::CO_src_alpha:
03712     return TextureStage::CO_src_alpha;
03713 
03714   case EggTexture::CO_one_minus_src_alpha:
03715     return TextureStage::CO_one_minus_src_alpha;
03716   };
03717 
03718   return TextureStage::CO_undefined;
03719 }
03720 
03721 ////////////////////////////////////////////////////////////////////
03722 //     Function: EggLoader::get_color_blend_mode
03723 //       Access: Private, Static
03724 //  Description: Converts the EggGroup's BlendMode to the
03725 //               corresponding ColorBlendAttrib::Mode value.
03726 ////////////////////////////////////////////////////////////////////
03727 ColorBlendAttrib::Mode EggLoader::
03728 get_color_blend_mode(EggGroup::BlendMode mode) {
03729   switch (mode) {
03730   case EggGroup::BM_unspecified:
03731   case EggGroup::BM_none:
03732     return ColorBlendAttrib::M_none;
03733   case EggGroup::BM_add:
03734     return ColorBlendAttrib::M_add;
03735   case EggGroup::BM_subtract:
03736     return ColorBlendAttrib::M_subtract;
03737   case EggGroup::BM_inv_subtract:
03738     return ColorBlendAttrib::M_inv_subtract;
03739   case EggGroup::BM_min:
03740     return ColorBlendAttrib::M_min;
03741   case EggGroup::BM_max:
03742     return ColorBlendAttrib::M_max;
03743   }
03744 
03745   return ColorBlendAttrib::M_none;
03746 }
03747 
03748 ////////////////////////////////////////////////////////////////////
03749 //     Function: EggLoader::get_color_blend_operand
03750 //       Access: Private, Static
03751 //  Description: Converts the EggGroup's BlendOperand to the
03752 //               corresponding ColorBlendAttrib::Operand value.
03753 ////////////////////////////////////////////////////////////////////
03754 ColorBlendAttrib::Operand EggLoader::
03755 get_color_blend_operand(EggGroup::BlendOperand operand) {
03756   switch (operand) {
03757   case EggGroup::BO_zero:
03758     return ColorBlendAttrib::O_zero;
03759   case EggGroup::BO_unspecified:
03760   case EggGroup::BO_one:
03761     return ColorBlendAttrib::O_one;
03762   case EggGroup::BO_incoming_color:
03763     return ColorBlendAttrib::O_incoming_color;
03764   case EggGroup::BO_one_minus_incoming_color:
03765     return ColorBlendAttrib::O_one_minus_incoming_color;
03766   case EggGroup::BO_fbuffer_color:
03767     return ColorBlendAttrib::O_fbuffer_color;
03768   case EggGroup::BO_one_minus_fbuffer_color:
03769     return ColorBlendAttrib::O_one_minus_fbuffer_color;
03770   case EggGroup::BO_incoming_alpha:
03771     return ColorBlendAttrib::O_incoming_alpha;
03772   case EggGroup::BO_one_minus_incoming_alpha:
03773     return ColorBlendAttrib::O_one_minus_incoming_alpha;
03774   case EggGroup::BO_fbuffer_alpha:
03775     return ColorBlendAttrib::O_fbuffer_alpha;
03776   case EggGroup::BO_one_minus_fbuffer_alpha:
03777     return ColorBlendAttrib::O_one_minus_fbuffer_alpha;
03778   case EggGroup::BO_constant_color:
03779     return ColorBlendAttrib::O_constant_color;
03780   case EggGroup::BO_one_minus_constant_color:
03781     return ColorBlendAttrib::O_one_minus_constant_color;
03782   case EggGroup::BO_constant_alpha:
03783     return ColorBlendAttrib::O_constant_alpha;
03784   case EggGroup::BO_one_minus_constant_alpha:
03785     return ColorBlendAttrib::O_one_minus_constant_alpha;
03786   case EggGroup::BO_incoming_color_saturate:
03787     return ColorBlendAttrib::O_incoming_color_saturate;
03788   case EggGroup::BO_color_scale:
03789     return ColorBlendAttrib::O_color_scale;
03790   case EggGroup::BO_one_minus_color_scale:
03791     return ColorBlendAttrib::O_one_minus_color_scale;
03792   case EggGroup::BO_alpha_scale:
03793     return ColorBlendAttrib::O_alpha_scale;
03794   case EggGroup::BO_one_minus_alpha_scale:
03795     return ColorBlendAttrib::O_one_minus_alpha_scale;
03796   }
03797 
03798   return ColorBlendAttrib::O_zero;
03799 }
03800 
03801 ////////////////////////////////////////////////////////////////////
03802 //     Function: EggLoader::VertexPoolTransform::operator <
03803 //       Access: Public
03804 //  Description: 
03805 ////////////////////////////////////////////////////////////////////
03806 bool EggLoader::VertexPoolTransform::
03807 operator < (const EggLoader::VertexPoolTransform &other) const {
03808   if (_vertex_pool != other._vertex_pool) {
03809     return _vertex_pool < other._vertex_pool;
03810   }
03811   int compare = _transform.compare_to(other._transform, 0.001);
03812   if (compare != 0) {
03813     return compare < 0;
03814   }
03815   
03816   if (_bake_in_uvs.size() != other._bake_in_uvs.size()) {
03817     return _bake_in_uvs.size() < other._bake_in_uvs.size();
03818   }
03819 
03820   BakeInUVs::const_iterator ai, bi;
03821   ai = _bake_in_uvs.begin();
03822   bi = other._bake_in_uvs.begin();
03823   while (ai != _bake_in_uvs.end()) {
03824     nassertr(bi != other._bake_in_uvs.end(), false);
03825     if ((*ai) != (*bi)) {
03826       return (*ai) < (*bi);
03827     }
03828     ++ai;
03829     ++bi;
03830   }
03831   nassertr(bi == other._bake_in_uvs.end(), false);
03832 
03833   return false;
03834 }
 All Classes Functions Variables Enumerations