Panda3D
|
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 ¢er, 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 }