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