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