Panda3D
 All Classes Functions Variables Enumerations
eggLoader.cxx
1 // Filename: eggLoader.cxx
2 // Created by: drose (26Feb02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pandabase.h"
16 
17 #include "eggLoader.h"
18 #include "eggRenderState.h"
19 #include "egg_parametrics.h"
20 #include "config_egg2pg.h"
21 #include "config_egg.h"
22 #include "nodePath.h"
23 #include "renderState.h"
24 #include "transformState.h"
25 #include "texturePool.h"
26 #include "billboardEffect.h"
27 #include "decalEffect.h"
28 #include "colorAttrib.h"
29 #include "textureAttrib.h"
30 #include "materialPool.h"
31 #include "geomNode.h"
32 #include "geomVertexFormat.h"
33 #include "geomVertexArrayFormat.h"
34 #include "geomVertexData.h"
35 #include "geomVertexWriter.h"
36 #include "geom.h"
37 #include "geomTriangles.h"
38 #include "geomTristrips.h"
39 #include "geomTrifans.h"
40 #include "geomLines.h"
41 #include "geomLinestrips.h"
42 #include "geomPoints.h"
43 #include "geomPatches.h"
44 #include "sequenceNode.h"
45 #include "switchNode.h"
46 #include "portalNode.h"
47 #include "occluderNode.h"
48 #include "polylightNode.h"
49 #include "lodNode.h"
50 #include "modelNode.h"
51 #include "modelRoot.h"
52 #include "string_utils.h"
53 #include "eggPrimitive.h"
54 #include "eggPatch.h"
55 #include "eggPoint.h"
56 #include "eggLine.h"
57 #include "eggTextureCollection.h"
58 #include "eggNurbsCurve.h"
59 #include "eggNurbsSurface.h"
60 #include "eggGroupNode.h"
61 #include "eggGroup.h"
62 #include "eggPolygon.h"
63 #include "eggTriangleStrip.h"
64 #include "eggTriangleFan.h"
65 #include "eggBin.h"
66 #include "eggTable.h"
67 #include "eggBinner.h"
68 #include "eggVertexPool.h"
69 #include "pt_EggTexture.h"
70 #include "characterMaker.h"
71 #include "character.h"
72 #include "animBundleMaker.h"
73 #include "animBundleNode.h"
74 #include "selectiveChildNode.h"
75 #include "collisionNode.h"
76 #include "collisionSphere.h"
77 #include "collisionInvSphere.h"
78 #include "collisionTube.h"
79 #include "collisionPlane.h"
80 #include "collisionPolygon.h"
81 #include "collisionFloorMesh.h"
82 #include "collisionBox.h"
83 #include "parametricCurve.h"
84 #include "nurbsCurve.h"
85 #include "nurbsCurveInterface.h"
86 #include "nurbsCurveEvaluator.h"
87 #include "nurbsSurfaceEvaluator.h"
88 #include "ropeNode.h"
89 #include "sheetNode.h"
90 #include "look_at.h"
91 #include "configVariableString.h"
92 #include "transformBlendTable.h"
93 #include "transformBlend.h"
94 #include "sparseArray.h"
95 #include "bitArray.h"
96 #include "thread.h"
97 #include "uvScrollNode.h"
98 #include "textureStagePool.h"
99 #include "cmath.h"
100 
101 #include <ctype.h>
102 #include <algorithm>
103 
104 // This class is used in make_node(EggBin *) to sort LOD instances in
105 // order by switching distance.
106 class LODInstance {
107 public:
108  LODInstance(EggNode *egg_node);
109  bool operator < (const LODInstance &other) const {
110  return _d->_switch_in < other._d->_switch_in;
111  }
112 
113  EggNode *_egg_node;
114  const EggSwitchConditionDistance *_d;
115 };
116 
117 LODInstance::
118 LODInstance(EggNode *egg_node) {
119  nassertv(egg_node != NULL);
120  _egg_node = egg_node;
121 
122  // We expect this egg node to be an EggGroup with an LOD
123  // specification. That's what the EggBinner collected together,
124  // after all.
125  EggGroup *egg_group = DCAST(EggGroup, egg_node);
126  nassertv(egg_group->has_lod());
127  const EggSwitchCondition &sw = egg_group->get_lod();
128 
129  // For now, this is the only kind of switch condition there is.
130  _d = DCAST(EggSwitchConditionDistance, &sw);
131 }
132 
133 
134 ////////////////////////////////////////////////////////////////////
135 // Function: EggLoader::Constructor
136 // Access: Public
137 // Description:
138 ////////////////////////////////////////////////////////////////////
139 EggLoader::
140 EggLoader() {
141  // We need to enforce whatever coordinate system the user asked for.
142  _data = new EggData;
143  _data->set_coordinate_system(egg_coordinate_system);
144  _error = false;
145  _dynamic_override = false;
146  _dynamic_override_char_maker = NULL;
147 }
148 
149 ////////////////////////////////////////////////////////////////////
150 // Function: EggLoader::Constructor
151 // Access: Public
152 // Description: The EggLoader constructor makes a copy of the EggData
153 // passed in.
154 ////////////////////////////////////////////////////////////////////
155 EggLoader::
156 EggLoader(const EggData *data) :
157  _data(new EggData(*data))
158 {
159  _error = false;
160  _dynamic_override = false;
161  _dynamic_override_char_maker = NULL;
162 }
163 
164 
165 ////////////////////////////////////////////////////////////////////
166 // Function: EggLoader::build_graph
167 // Access: Public
168 // Description:
169 ////////////////////////////////////////////////////////////////////
170 void EggLoader::
171 build_graph() {
172  _deferred_nodes.clear();
173 
174  // Expand all of the ObjectType flags before we do anything else;
175  // that might prune out large portions of the scene.
176  if (!expand_all_object_types(_data)) {
177  return;
178  }
179 
180  // Now, load up all of the textures.
181  load_textures();
182 
183  // Clean up the vertices.
184  _data->clear_connected_shading();
185  _data->remove_unused_vertices(true);
186  _data->get_connected_shading();
187  _data->unify_attributes(true, egg_flat_shading, true);
188 
189  // Now we need to get the connected shading again, since in unifying
190  // the attributes we may have made vertices suddenly become
191  // identical to each other, thereby connecting more primitives than
192  // before.
193  _data->clear_connected_shading();
194  _data->remove_unused_vertices(true);
195  _data->get_connected_shading();
196 
197  // Sequences and switches have special needs. Make sure that
198  // primitives parented directly to a sequence or switch are sorted
199  // into sub-groups first, to prevent them being unified into a
200  // single polyset.
201  separate_switches(_data);
202 
203  if (egg_emulate_bface) {
204  emulate_bface(_data);
205  }
206 
207  // Then bin up the polysets and LOD nodes.
208  _data->remove_invalid_primitives(true);
209  EggBinner binner(*this);
210  binner.make_bins(_data);
211 
212  // ((EggGroupNode *)_data)->write(cerr, 0);
213 
214  // Now build up the scene graph.
215  _root = new ModelRoot(_data->get_egg_filename(), _data->get_egg_timestamp());
216 
217  EggGroupNode::const_iterator ci;
218  for (ci = _data->begin(); ci != _data->end(); ++ci) {
219  make_node(*ci, _root);
220  }
221 
222  reparent_decals();
223  start_sequences();
224 
225  apply_deferred_nodes(_root, DeferredNodeProperty());
226 }
227 
228 ////////////////////////////////////////////////////////////////////
229 // Function: EggLoader::reparent_decals
230 // Access: Public
231 // Description: For each node representing a decal base geometry
232 // (i.e. a node corresponding to an EggGroup with the
233 // decal flag set), move all of its nested geometry
234 // directly below the GeomNode representing the group.
235 ////////////////////////////////////////////////////////////////////
236 void EggLoader::
238  ExtraNodes::const_iterator di;
239  for (di = _decals.begin(); di != _decals.end(); ++di) {
240  PandaNode *node = (*di);
241  nassertv(node != (PandaNode *)NULL);
242 
243  // The NodePath interface is best for this.
244  NodePath parent(node);
245 
246  // First, search for the GeomNode.
247  NodePath geom_parent;
248  int num_children = parent.get_num_children();
249  for (int i = 0; i < num_children; i++) {
250  NodePath child = parent.get_child(i);
251 
252  if (child.node()->is_of_type(GeomNode::get_class_type())) {
253  if (!geom_parent.is_empty()) {
254  // Oops, too many GeomNodes.
255  egg2pg_cat.error()
256  << "Decal onto " << parent.node()->get_name()
257  << " uses base geometry with multiple GeomNodes.\n";
258  _error = true;
259  }
260  geom_parent = child;
261  }
262  }
263 
264  if (geom_parent.is_empty()) {
265  // No children were GeomNodes.
266  egg2pg_cat.error()
267  << "Ignoring decal onto " << parent.node()->get_name()
268  << "; no geometry within group.\n";
269  _error = true;
270  } else {
271  // Now reparent all of the non-GeomNodes to this node. We have
272  // to be careful so we don't get lost as we self-modify this
273  // list.
274  int i = 0;
275  while (i < num_children) {
276  NodePath child = parent.get_child(i);
277 
278  if (child.node()->is_of_type(GeomNode::get_class_type())) {
279  i++;
280  } else {
281  child.reparent_to(geom_parent);
282  num_children--;
283  }
284  }
285 
286  // Finally, set the DecalEffect on the base geometry.
287  geom_parent.node()->set_effect(DecalEffect::make());
288  }
289  }
290 }
291 
292 ////////////////////////////////////////////////////////////////////
293 // Function: EggLoader::start_sequences
294 // Access: Public
295 // Description: Starts all of the SequenceNodes we created looping.
296 // We have to wait until the entire graph is built up to
297 // do this, because the SequenceNode needs its full set
298 // of children before it can know how many frames to
299 // loop.
300 ////////////////////////////////////////////////////////////////////
301 void EggLoader::
303  ExtraNodes::const_iterator ni;
304  for (ni = _sequences.begin(); ni != _sequences.end(); ++ni) {
305  SequenceNode *node = DCAST(SequenceNode, (*ni));
306  node->loop(true);
307  }
308 }
309 
310 ////////////////////////////////////////////////////////////////////
311 // Function: EggLoader::make_polyset
312 // Access: Public
313 // Description: Creates a polyset--that is, a Geom--from the
314 // primitives that have already been grouped into a bin.
315 // If transform is non-NULL, it represents the transform
316 // to apply to the vertices (instead of the default
317 // transform based on the bin's position within the
318 // hierarchy).
319 ////////////////////////////////////////////////////////////////////
320 void EggLoader::
321 make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform,
322  bool is_dynamic, CharacterMaker *character_maker) {
323  if (egg_bin->empty()) {
324  // If there are no children--no primitives--never mind.
325  return;
326  }
327 
328  // We know that all of the primitives in the bin have the same
329  // render state, so we can get that information from the first
330  // primitive.
331  EggGroupNode::const_iterator ci = egg_bin->begin();
332  nassertv(ci != egg_bin->end());
333  CPT(EggPrimitive) first_prim = DCAST(EggPrimitive, (*ci));
334  nassertv(first_prim != (EggPrimitive *)NULL);
335  const EggRenderState *render_state;
336  DCAST_INTO_V(render_state, first_prim->get_user_data(EggRenderState::get_class_type()));
337 
338  if (render_state->_hidden && egg_suppress_hidden) {
339  // Eat this polyset.
340  return;
341  }
342 
343  // Generate an optimal vertex pool (or multiple vertex pools, if we
344  // have a lot of vertex) for the polygons within just the bin. Each
345  // EggVertexPool translates directly to an optimal GeomVertexData
346  // structure.
347  EggVertexPools vertex_pools;
348  egg_bin->rebuild_vertex_pools(vertex_pools, (unsigned int)egg_max_vertices,
349  false);
350 
351  if (egg_mesh) {
352  // If we're using the mesher, mesh now.
353  egg_bin->mesh_triangles(render_state->_flat_shaded ? EggGroupNode::T_flat_shaded : 0);
354 
355  } else {
356  // If we're not using the mesher, at least triangulate any
357  // higher-order polygons we might have.
358  egg_bin->triangulate_polygons(EggGroupNode::T_polygon | EggGroupNode::T_convex);
359  }
360 
361  // Now that we've meshed, apply the per-prim attributes onto the
362  // vertices, so we can copy them to the GeomVertexData.
363  egg_bin->apply_first_attribute(false);
364  egg_bin->post_apply_flat_attribute(false);
365 
366  //egg_bin->write(cerr, 0);
367 
368  PT(GeomNode) geom_node;
369 
370  // Now iterate through each EggVertexPool. Normally, there's only
371  // one, but if we have a really big mesh, it might have been split
372  // into multiple vertex pools (to keep each one within the
373  // egg_max_vertices constraint).
374  EggVertexPools::iterator vpi;
375  for (vpi = vertex_pools.begin(); vpi != vertex_pools.end(); ++vpi) {
376  EggVertexPool *vertex_pool = (*vpi);
377  vertex_pool->remove_unused_vertices();
378  // vertex_pool->write(cerr, 0);
379 
380  bool has_overall_color;
381  LColor overall_color;
382  vertex_pool->check_overall_color(has_overall_color, overall_color);
383  if (!egg_flat_colors) {
384  // If flat colors aren't allowed, then we don't care whether
385  // there is an overall color. In that case, treat all vertex
386  // pools as if they contain a combination of multiple colors.
387  has_overall_color = false;
388  }
389 
390  PT(TransformBlendTable) blend_table;
391  if (is_dynamic) {
392  // Dynamic vertex pools will require a TransformBlendTable to
393  // indicate how the vertices are to be animated.
394  blend_table = make_blend_table(vertex_pool, egg_bin, character_maker);
395 
396  // Now that we've created the blend table, we can re-order the
397  // vertices in the pool to efficiently group vertices together
398  // that will share the same transform matrix. (We have to
399  // re-order them before we create primitives, below, because
400  // this will change the vertex index numbers.)
401  vertex_pool->sort_by_external_index();
402  }
403 
404  // Create a handful of GeomPrimitives corresponding to the various
405  // types of primitives that reference this vertex pool.
406  UniquePrimitives unique_primitives;
407  Primitives primitives;
408  for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
409  EggPrimitive *egg_prim;
410  DCAST_INTO_V(egg_prim, (*ci));
411  if (egg_prim->get_pool() == vertex_pool) {
412  make_primitive(render_state, egg_prim, unique_primitives, primitives,
413  has_overall_color, overall_color);
414  }
415  }
416 
417  if (!primitives.empty()) {
418  LMatrix4d mat;
419  if (transform != NULL) {
420  mat = (*transform);
421  } else {
422  mat = egg_bin->get_vertex_to_node();
423  }
424 
425  // Now convert this vertex pool to a GeomVertexData.
426  PT(GeomVertexData) vertex_data =
427  make_vertex_data(render_state, vertex_pool, egg_bin, mat, blend_table,
428  is_dynamic, character_maker, has_overall_color);
429  nassertv(vertex_data != (GeomVertexData *)NULL);
430 
431  // And create a Geom to hold the primitives.
432  PT(Geom) geom = new Geom(vertex_data);
433 
434  // Add each new primitive to the Geom.
435  Primitives::const_iterator pi;
436  for (pi = primitives.begin(); pi != primitives.end(); ++pi) {
437  PT(GeomPrimitive) primitive = (*pi);
438 
439  if (primitive->is_indexed()) {
440  // Since we may have over-allocated while we were filling up
441  // the primitives, down-allocate now.
442  primitive->reserve_num_vertices(primitive->get_num_vertices());
443  }
444 
445  geom->add_primitive(primitive);
446  }
447 
448  // vertex_data->write(cerr);
449  // geom->write(cerr);
450  // render_state->_state->write(cerr, 0);
451 
452  // Create a new GeomNode if we haven't already.
453  if (geom_node == (GeomNode *)NULL) {
454  // Now, is our parent node a GeomNode, or just an ordinary
455  // PandaNode? If it's a GeomNode, we can add the new Geom directly
456  // to our parent; otherwise, we need to create a new node.
457  if (parent->is_geom_node() && !render_state->_hidden) {
458  geom_node = DCAST(GeomNode, parent);
459 
460  } else {
461  geom_node = new GeomNode(egg_bin->get_name());
462  if (render_state->_hidden) {
463  parent->add_stashed(geom_node);
464  } else {
465  parent->add_child(geom_node);
466  }
467  }
468  }
469 
470  CPT(RenderState) geom_state = render_state->_state;
471  if (has_overall_color) {
472  if (!overall_color.almost_equal(LColor(1.0f, 1.0f, 1.0f, 1.0f))) {
473  geom_state = geom_state->add_attrib(ColorAttrib::make_flat(overall_color), -1);
474  }
475  } else {
476  geom_state = geom_state->add_attrib(ColorAttrib::make_vertex(), -1);
477  }
478 
479  geom_node->add_geom(geom, geom_state);
480  }
481  }
482 
483  if (geom_node != (GeomNode *)NULL && egg_show_normals) {
484  // Create some more geometry to visualize each normal.
485  for (vpi = vertex_pools.begin(); vpi != vertex_pools.end(); ++vpi) {
486  EggVertexPool *vertex_pool = (*vpi);
487  show_normals(vertex_pool, geom_node);
488  }
489  }
490 }
491 
492 ////////////////////////////////////////////////////////////////////
493 // Function: EggLoader::make_transform
494 // Access: Public
495 // Description: Creates a TransformState object corresponding to the
496 // indicated EggTransform.
497 ////////////////////////////////////////////////////////////////////
498 CPT(TransformState) EggLoader::
499 make_transform(const EggTransform *egg_transform) {
500  // We'll build up the transform componentwise, so we preserve any
501  // componentwise properties of the egg transform.
502 
503  CPT(TransformState) ts = TransformState::make_identity();
504  int num_components = egg_transform->get_num_components();
505  for (int i = 0; i < num_components; i++) {
506  switch (egg_transform->get_component_type(i)) {
507  case EggTransform::CT_translate2d:
508  {
509  LVecBase2 trans2d(LCAST(PN_stdfloat, egg_transform->get_component_vec2(i)));
510  LVecBase3 trans3d(trans2d[0], trans2d[1], 0.0f);
511  ts = TransformState::make_pos(trans3d)->compose(ts);
512  }
513  break;
514 
515  case EggTransform::CT_translate3d:
516  {
517  LVecBase3 trans3d(LCAST(PN_stdfloat, egg_transform->get_component_vec3(i)));
518  ts = TransformState::make_pos(trans3d)->compose(ts);
519  }
520  break;
521 
522  case EggTransform::CT_rotate2d:
523  {
524  LRotation rot(LVector3(0.0f, 0.0f, 1.0f),
525  (PN_stdfloat)egg_transform->get_component_number(i));
526  ts = TransformState::make_quat(rot)->compose(ts);
527  }
528  break;
529 
530  case EggTransform::CT_rotx:
531  {
532  LRotation rot(LVector3(1.0f, 0.0f, 0.0f),
533  (PN_stdfloat)egg_transform->get_component_number(i));
534  ts = TransformState::make_quat(rot)->compose(ts);
535  }
536  break;
537 
538  case EggTransform::CT_roty:
539  {
540  LRotation rot(LVector3(0.0f, 1.0f, 0.0f),
541  (PN_stdfloat)egg_transform->get_component_number(i));
542  ts = TransformState::make_quat(rot)->compose(ts);
543  }
544  break;
545 
546  case EggTransform::CT_rotz:
547  {
548  LRotation rot(LVector3(0.0f, 0.0f, 1.0f),
549  (PN_stdfloat)egg_transform->get_component_number(i));
550  ts = TransformState::make_quat(rot)->compose(ts);
551  }
552  break;
553 
554  case EggTransform::CT_rotate3d:
555  {
556  LRotation rot(LCAST(PN_stdfloat, egg_transform->get_component_vec3(i)),
557  (PN_stdfloat)egg_transform->get_component_number(i));
558  ts = TransformState::make_quat(rot)->compose(ts);
559  }
560  break;
561 
562  case EggTransform::CT_scale2d:
563  {
564  LVecBase2 scale2d(LCAST(PN_stdfloat, egg_transform->get_component_vec2(i)));
565  LVecBase3 scale3d(scale2d[0], scale2d[1], 1.0f);
566  ts = TransformState::make_scale(scale3d)->compose(ts);
567  }
568  break;
569 
570  case EggTransform::CT_scale3d:
571  {
572  LVecBase3 scale3d(LCAST(PN_stdfloat, egg_transform->get_component_vec3(i)));
573  ts = TransformState::make_scale(scale3d)->compose(ts);
574  }
575  break;
576 
577  case EggTransform::CT_uniform_scale:
578  {
579  PN_stdfloat scale = (PN_stdfloat)egg_transform->get_component_number(i);
580  ts = TransformState::make_scale(scale)->compose(ts);
581  }
582  break;
583 
584  case EggTransform::CT_matrix3:
585  {
586  LMatrix3 m(LCAST(PN_stdfloat, egg_transform->get_component_mat3(i)));
587  LMatrix4 mat4(m(0, 0), m(0, 1), 0.0, m(0, 2),
588  m(1, 0), m(1, 1), 0.0, m(1, 2),
589  0.0, 0.0, 1.0, 0.0,
590  m(2, 0), m(2, 1), 0.0, m(2, 2));
591 
592  ts = TransformState::make_mat(mat4)->compose(ts);
593  }
594  break;
595 
596  case EggTransform::CT_matrix4:
597  {
598  LMatrix4 mat4(LCAST(PN_stdfloat, egg_transform->get_component_mat4(i)));
599  ts = TransformState::make_mat(mat4)->compose(ts);
600  }
601  break;
602 
603  case EggTransform::CT_invalid:
604  nassertr(false, ts);
605  break;
606  }
607  }
608 
609  if (ts->components_given()) {
610  return ts;
611  }
612 
613  // Finally, we uniquify all the matrix-based TransformStates we
614  // create by complete matrix value. The TransformState class
615  // doesn't normally go this far, because of the cost of this
616  // additional uniquification step, but this is the egg loader so we
617  // don't mind spending a little bit of extra time here to get a more
618  // optimal result.
619  TransformStates::iterator tsi = _transform_states.insert(TransformStates::value_type(ts->get_mat(), ts)).first;
620 
621  return (*tsi).second;
622 }
623 
624 ////////////////////////////////////////////////////////////////////
625 // Function: EggLoader::show_normals
626 // Access: Private
627 // Description: In the presence of egg-show-normals, generate some
628 // additional geometry to represent the normals,
629 // tangents, and binormals of each vertex.
630 ////////////////////////////////////////////////////////////////////
631 void EggLoader::
632 show_normals(EggVertexPool *vertex_pool, GeomNode *geom_node) {
633  PT(GeomPrimitive) primitive = new GeomLines(Geom::UH_static);
634  CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
635  PT(GeomVertexData) vertex_data =
636  new GeomVertexData(vertex_pool->get_name(), format, Geom::UH_static);
637 
638  GeomVertexWriter vertex(vertex_data, InternalName::get_vertex());
639  GeomVertexWriter color(vertex_data, InternalName::get_color());
640 
641  EggVertexPool::const_iterator vi;
642  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
643  EggVertex *vert = (*vi);
644  LPoint3d pos = vert->get_pos3();
645 
646  if (vert->has_normal()) {
647  vertex.add_data3d(pos);
648  vertex.add_data3d(pos + vert->get_normal() * egg_normal_scale);
649  color.add_data4(1.0f, 0.0f, 0.0f, 1.0f);
650  color.add_data4(1.0f, 0.0f, 0.0f, 1.0f);
651  primitive->add_next_vertices(2);
652  primitive->close_primitive();
653  }
654 
655  // Also look for tangents and binormals in each texture coordinate
656  // set.
658  for (uvi = vert->uv_begin(); uvi != vert->uv_end(); ++uvi) {
659  EggVertexUV *uv_obj = (*uvi);
660  if (uv_obj->has_tangent()) {
661  vertex.add_data3d(pos);
662  vertex.add_data3d(pos + uv_obj->get_tangent() * egg_normal_scale);
663  color.add_data4(0.0f, 1.0f, 0.0f, 1.0f);
664  color.add_data4(0.0f, 1.0f, 0.0f, 1.0f);
665  primitive->add_next_vertices(2);
666  primitive->close_primitive();
667  }
668  if (uv_obj->has_binormal()) {
669  vertex.add_data3d(pos);
670  vertex.add_data3d(pos + uv_obj->get_binormal() * egg_normal_scale);
671  color.add_data4(0.0f, 0.0f, 1.0f, 1.0f);
672  color.add_data4(0.0f, 0.0f, 1.0f, 1.0f);
673  primitive->add_next_vertices(2);
674  primitive->close_primitive();
675  }
676  }
677  }
678 
679  PT(Geom) geom = new Geom(vertex_data);
680  geom->add_primitive(primitive);
681  geom_node->add_geom(geom);
682 }
683 
684 ////////////////////////////////////////////////////////////////////
685 // Function: EggLoader::make_nurbs_curve
686 // Access: Private
687 // Description:
688 ////////////////////////////////////////////////////////////////////
689 void EggLoader::
690 make_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
691  const LMatrix4d &mat) {
692  if (egg_load_old_curves) {
693  // Make a NurbsCurve instead of a RopeNode (old interface).
694  make_old_nurbs_curve(egg_curve, parent, mat);
695  return;
696  }
697 
698  assert(parent != NULL);
699  assert(!parent->is_geom_node());
700 
701  PT(NurbsCurveEvaluator) nurbs = ::make_nurbs_curve(egg_curve, mat);
702  if (nurbs == (NurbsCurveEvaluator *)NULL) {
703  _error = true;
704  return;
705  }
706 
707  /*
708  switch (egg_curve->get_curve_type()) {
709  case EggCurve::CT_xyz:
710  curve->set_curve_type(PCT_XYZ);
711  break;
712 
713  case EggCurve::CT_hpr:
714  curve->set_curve_type(PCT_HPR);
715  break;
716 
717  case EggCurve::CT_t:
718  curve->set_curve_type(PCT_T);
719  break;
720 
721  default:
722  break;
723  }
724  */
725 
726  PT(RopeNode) rope = new RopeNode(egg_curve->get_name());
727  rope->set_curve(nurbs);
728 
729  // Respect the subdivision values in the egg file, if any.
730  if (egg_curve->get_subdiv() != 0) {
731  int subdiv_per_segment =
732  (int)((egg_curve->get_subdiv() + 0.5) / nurbs->get_num_segments());
733  rope->set_num_subdiv(max(subdiv_per_segment, 1));
734  }
735 
736  const EggRenderState *render_state;
737  DCAST_INTO_V(render_state, egg_curve->get_user_data(EggRenderState::get_class_type()));
738  if (render_state->_hidden && egg_suppress_hidden) {
739  // Eat this primitive.
740  return;
741  }
742 
743  rope->set_state(render_state->_state);
744  rope->set_uv_mode(RopeNode::UV_parametric);
745 
746  if (egg_curve->has_vertex_color()) {
747  // If the curve had individual vertex color, enable it.
748  rope->set_use_vertex_color(true);
749  } else if (egg_curve->has_color()) {
750  // Otherwise, if the curve has overall color, apply it.
751  rope->set_attrib(ColorAttrib::make_flat(egg_curve->get_color()));
752  }
753 
754  parent->add_child(rope);
755 }
756 
757 ////////////////////////////////////////////////////////////////////
758 // Function: EggLoader::make_old_nurbs_curve
759 // Access: Private
760 // Description: This deprecated interface creates a NurbsCurve object
761 // for the EggNurbsCurve entry. It will eventually be
762 // removed in favor of the above, which creates a
763 // RopeNode.
764 ////////////////////////////////////////////////////////////////////
765 void EggLoader::
766 make_old_nurbs_curve(EggNurbsCurve *egg_curve, PandaNode *parent,
767  const LMatrix4d &mat) {
768  assert(parent != NULL);
769  assert(!parent->is_geom_node());
770 
771  PT(ParametricCurve) curve;
772  curve = new NurbsCurve;
773 
774  NurbsCurveInterface *nurbs = curve->get_nurbs_interface();
775  nassertv(nurbs != (NurbsCurveInterface *)NULL);
776 
777  if (egg_curve->get_order() < 1 || egg_curve->get_order() > 4) {
778  egg2pg_cat.error()
779  << "Invalid NURBSCurve order for " << egg_curve->get_name() << ": "
780  << egg_curve->get_order() << "\n";
781  _error = true;
782  return;
783  }
784 
785  nurbs->set_order(egg_curve->get_order());
786 
787  EggPrimitive::const_iterator pi;
788  for (pi = egg_curve->begin(); pi != egg_curve->end(); ++pi) {
789  nurbs->append_cv(LCAST(PN_stdfloat, (*pi)->get_pos4() * mat));
790  }
791 
792  int num_knots = egg_curve->get_num_knots();
793  if (num_knots != nurbs->get_num_knots()) {
794  egg2pg_cat.error()
795  << "Invalid NURBSCurve number of knots for "
796  << egg_curve->get_name() << ": got " << num_knots
797  << " knots, expected " << nurbs->get_num_knots() << "\n";
798  _error = true;
799  return;
800  }
801 
802  for (int i = 0; i < num_knots; i++) {
803  nurbs->set_knot(i, egg_curve->get_knot(i));
804  }
805 
806  switch (egg_curve->get_curve_type()) {
807  case EggCurve::CT_xyz:
808  curve->set_curve_type(PCT_XYZ);
809  break;
810 
811  case EggCurve::CT_hpr:
812  curve->set_curve_type(PCT_HPR);
813  break;
814 
815  case EggCurve::CT_t:
816  curve->set_curve_type(PCT_T);
817  break;
818 
819  default:
820  break;
821  }
822  curve->set_name(egg_curve->get_name());
823 
824  if (!curve->recompute()) {
825  egg2pg_cat.error()
826  << "Invalid NURBSCurve " << egg_curve->get_name() << "\n";
827  _error = true;
828  return;
829  }
830 
831  parent->add_child(curve);
832 }
833 
834 ////////////////////////////////////////////////////////////////////
835 // Function: EggLoader::make_nurbs_surface
836 // Access: Private
837 // Description:
838 ////////////////////////////////////////////////////////////////////
839 void EggLoader::
840 make_nurbs_surface(EggNurbsSurface *egg_surface, PandaNode *parent,
841  const LMatrix4d &mat) {
842  assert(parent != NULL);
843  assert(!parent->is_geom_node());
844 
845  PT(NurbsSurfaceEvaluator) nurbs = ::make_nurbs_surface(egg_surface, mat);
846  if (nurbs == (NurbsSurfaceEvaluator *)NULL) {
847  _error = true;
848  return;
849  }
850 
851  PT(SheetNode) sheet = new SheetNode(egg_surface->get_name());
852  sheet->set_surface(nurbs);
853 
854  // Respect the subdivision values in the egg file, if any.
855  if (egg_surface->get_u_subdiv() != 0) {
856  int u_subdiv_per_segment =
857  (int)((egg_surface->get_u_subdiv() + 0.5) / nurbs->get_num_u_segments());
858  sheet->set_num_u_subdiv(max(u_subdiv_per_segment, 1));
859  }
860  if (egg_surface->get_v_subdiv() != 0) {
861  int v_subdiv_per_segment =
862  (int)((egg_surface->get_v_subdiv() + 0.5) / nurbs->get_num_v_segments());
863  sheet->set_num_v_subdiv(max(v_subdiv_per_segment, 1));
864  }
865 
866  const EggRenderState *render_state;
867  DCAST_INTO_V(render_state, egg_surface->get_user_data(EggRenderState::get_class_type()));
868  if (render_state->_hidden && egg_suppress_hidden) {
869  // Eat this primitive.
870  return;
871  }
872 
873  sheet->set_state(render_state->_state);
874 
875  if (egg_surface->has_vertex_color()) {
876  // If the surface had individual vertex color, enable it.
877  sheet->set_use_vertex_color(true);
878  } else if (egg_surface->has_color()) {
879  // Otherwise, if the surface has overall color, apply it.
880  sheet->set_attrib(ColorAttrib::make_flat(egg_surface->get_color()));
881  }
882 
883  parent->add_child(sheet);
884 }
885 
886 ////////////////////////////////////////////////////////////////////
887 // Function: EggLoader::load_textures
888 // Access: Private
889 // Description:
890 ////////////////////////////////////////////////////////////////////
891 void EggLoader::
892 load_textures() {
893  // First, collect all the textures that are referenced.
895  tc.find_used_textures(_data);
896 
897  EggTextureCollection::iterator ti;
898  for (ti = tc.begin(); ti != tc.end(); ++ti) {
899  PT_EggTexture egg_tex = (*ti);
900 
901  TextureDef def;
902  if (load_texture(def, egg_tex)) {
903  // Now associate the pointers, so we'll be able to look up the
904  // Texture pointer given an EggTexture pointer, later.
905  _textures[egg_tex] = def;
906  }
907  }
908 }
909 
910 
911 ////////////////////////////////////////////////////////////////////
912 // Function: EggLoader::load_texture
913 // Access: Private
914 // Description:
915 ////////////////////////////////////////////////////////////////////
916 bool EggLoader::
917 load_texture(TextureDef &def, EggTexture *egg_tex) {
918  // Check to see if we should reduce the number of channels in
919  // the texture.
920  int wanted_channels = 0;
921  bool wanted_alpha = false;
922  switch (egg_tex->get_format()) {
923  case EggTexture::F_red:
924  case EggTexture::F_green:
925  case EggTexture::F_blue:
926  case EggTexture::F_alpha:
927  case EggTexture::F_luminance:
928  wanted_channels = 1;
929  wanted_alpha = false;
930  break;
931 
932  case EggTexture::F_luminance_alpha:
933  case EggTexture::F_luminance_alphamask:
934  wanted_channels = 2;
935  wanted_alpha = true;
936  break;
937 
938  case EggTexture::F_rgb:
939  case EggTexture::F_rgb12:
940  case EggTexture::F_rgb8:
941  case EggTexture::F_rgb5:
942  case EggTexture::F_rgb332:
943  wanted_channels = 3;
944  wanted_alpha = false;
945  break;
946 
947  case EggTexture::F_rgba:
948  case EggTexture::F_rgbm:
949  case EggTexture::F_rgba12:
950  case EggTexture::F_rgba8:
951  case EggTexture::F_rgba4:
952  case EggTexture::F_rgba5:
953  wanted_channels = 4;
954  wanted_alpha = true;
955  break;
956 
957  case EggTexture::F_unspecified:
958  wanted_alpha = egg_tex->has_alpha_filename();
959  }
960 
961  // Since some properties of the textures are inferred from the
962  // texture files themselves (if the properties are not explicitly
963  // specified in the egg file), then we add the textures as
964  // dependents for the egg file.
965  if (_record != (BamCacheRecord *)NULL) {
966  _record->add_dependent_file(egg_tex->get_fullpath());
967  if (egg_tex->has_alpha_filename() && wanted_alpha) {
968  _record->add_dependent_file(egg_tex->get_alpha_fullpath());
969  }
970  }
971 
972  // By convention, the egg loader will preload the simple texture images.
973  LoaderOptions options;
974  if (egg_preload_simple_textures) {
975  options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_preload_simple);
976  }
977 
978  if (!egg_ignore_filters && !egg_ignore_mipmaps) {
979  switch (egg_tex->get_minfilter()) {
980  case EggTexture::FT_nearest:
981  case EggTexture::FT_linear:
982  case EggTexture::FT_unspecified:
983  break;
984 
985  case EggTexture::FT_nearest_mipmap_nearest:
986  case EggTexture::FT_linear_mipmap_nearest:
987  case EggTexture::FT_nearest_mipmap_linear:
988  case EggTexture::FT_linear_mipmap_linear:
989  options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps);
990  }
991  }
992 
993  if (egg_tex->get_multiview()) {
994  options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_multiview);
995  if (egg_tex->has_num_views()) {
996  options.set_texture_num_views(egg_tex->get_num_views());
997  }
998  }
999 
1000  PT(Texture) tex;
1001  switch (egg_tex->get_texture_type()) {
1002  case EggTexture::TT_unspecified:
1003  case EggTexture::TT_1d_texture:
1004  options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_allow_1d);
1005  // Fall through.
1006 
1007  case EggTexture::TT_2d_texture:
1008  if (egg_tex->has_alpha_filename() && wanted_alpha) {
1009  tex = TexturePool::load_texture(egg_tex->get_fullpath(),
1010  egg_tex->get_alpha_fullpath(),
1011  wanted_channels,
1012  egg_tex->get_alpha_file_channel(),
1013  egg_tex->get_read_mipmaps(), options);
1014  } else {
1015  tex = TexturePool::load_texture(egg_tex->get_fullpath(),
1016  wanted_channels,
1017  egg_tex->get_read_mipmaps(), options);
1018  }
1019  break;
1020 
1021  case EggTexture::TT_3d_texture:
1022  tex = TexturePool::load_3d_texture(egg_tex->get_fullpath(),
1023  egg_tex->get_read_mipmaps(), options);
1024  break;
1025 
1026  case EggTexture::TT_cube_map:
1027  tex = TexturePool::load_cube_map(egg_tex->get_fullpath(),
1028  egg_tex->get_read_mipmaps(), options);
1029  break;
1030  }
1031 
1032  if (tex == (Texture *)NULL) {
1033  return false;
1034  }
1035 
1036  // Record the original filenames in the textures (as loaded from the
1037  // egg file). These filenames will be written back to the bam file
1038  // if the bam file is written out.
1039  tex->set_filename(egg_tex->get_filename());
1040  if (egg_tex->has_alpha_filename() && wanted_alpha) {
1041  tex->set_alpha_filename(egg_tex->get_alpha_filename());
1042  }
1043 
1044  // See if there is some egg data hanging on the texture. In
1045  // particular, the TxaFileFilter might have left that here for us.
1046  TypedReferenceCount *aux = tex->get_aux_data("egg");
1047  if (aux != (TypedReferenceCount *)NULL &&
1048  aux->is_of_type(EggTexture::get_class_type())) {
1049  EggTexture *aux_egg_tex = DCAST(EggTexture, aux);
1050 
1051  if (aux_egg_tex->get_alpha_mode() != EggTexture::AM_unspecified) {
1052  egg_tex->set_alpha_mode(aux_egg_tex->get_alpha_mode());
1053  }
1054  if (aux_egg_tex->get_format() != EggTexture::F_unspecified) {
1055  egg_tex->set_format(aux_egg_tex->get_format());
1056  }
1057  if (aux_egg_tex->get_minfilter() != EggTexture::FT_unspecified) {
1058  egg_tex->set_minfilter(aux_egg_tex->get_minfilter());
1059  }
1060  if (aux_egg_tex->get_magfilter() != EggTexture::FT_unspecified) {
1061  egg_tex->set_magfilter(aux_egg_tex->get_magfilter());
1062  }
1063  if (aux_egg_tex->has_anisotropic_degree()) {
1064  egg_tex->set_anisotropic_degree(aux_egg_tex->get_anisotropic_degree());
1065  }
1066  }
1067 
1068  apply_texture_attributes(tex, egg_tex);
1069 
1070  // Make a texture stage for the texture.
1071  PT(TextureStage) stage = make_texture_stage(egg_tex);
1072  def._texture = DCAST(TextureAttrib, TextureAttrib::make())->add_on_stage(stage, tex);
1073  def._stage = stage;
1074  def._egg_tex = egg_tex;
1075 
1076  return true;
1077 }
1078 
1079 
1080 ////////////////////////////////////////////////////////////////////
1081 // Function: EggLoader::apply_texture_attributes
1082 // Access: Private
1083 // Description:
1084 ////////////////////////////////////////////////////////////////////
1085 void EggLoader::
1086 apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
1087  if (egg_tex->get_compression_mode() != EggTexture::CM_default) {
1088  tex->set_compression(convert_compression_mode(egg_tex->get_compression_mode()));
1089  }
1090 
1091  EggTexture::WrapMode wrap_u = egg_tex->determine_wrap_u();
1092  EggTexture::WrapMode wrap_v = egg_tex->determine_wrap_v();
1093  EggTexture::WrapMode wrap_w = egg_tex->determine_wrap_w();
1094 
1095  if (wrap_u != EggTexture::WM_unspecified) {
1096  tex->set_wrap_u(convert_wrap_mode(wrap_u));
1097  }
1098  if (wrap_v != EggTexture::WM_unspecified) {
1099  tex->set_wrap_v(convert_wrap_mode(wrap_v));
1100  }
1101  if (wrap_w != EggTexture::WM_unspecified) {
1102  tex->set_wrap_w(convert_wrap_mode(wrap_w));
1103  }
1104 
1105  if (egg_tex->has_border_color()) {
1106  tex->set_border_color(egg_tex->get_border_color());
1107  }
1108 
1109  switch (egg_tex->get_minfilter()) {
1110  case EggTexture::FT_nearest:
1111  tex->set_minfilter(SamplerState::FT_nearest);
1112  break;
1113 
1114  case EggTexture::FT_linear:
1115  if (egg_ignore_filters) {
1116  egg2pg_cat.warning()
1117  << "Ignoring minfilter request\n";
1118  tex->set_minfilter(SamplerState::FT_nearest);
1119  } else {
1120  tex->set_minfilter(SamplerState::FT_linear);
1121  }
1122  break;
1123 
1124  case EggTexture::FT_nearest_mipmap_nearest:
1125  if (egg_ignore_filters) {
1126  egg2pg_cat.warning()
1127  << "Ignoring minfilter request\n";
1128  tex->set_minfilter(SamplerState::FT_nearest);
1129  } else if (egg_ignore_mipmaps) {
1130  egg2pg_cat.warning()
1131  << "Ignoring mipmap request\n";
1132  tex->set_minfilter(SamplerState::FT_nearest);
1133  } else {
1134  tex->set_minfilter(SamplerState::FT_nearest_mipmap_nearest);
1135  }
1136  break;
1137 
1138  case EggTexture::FT_linear_mipmap_nearest:
1139  if (egg_ignore_filters) {
1140  egg2pg_cat.warning()
1141  << "Ignoring minfilter request\n";
1142  tex->set_minfilter(SamplerState::FT_nearest);
1143  } else if (egg_ignore_mipmaps) {
1144  egg2pg_cat.warning()
1145  << "Ignoring mipmap request\n";
1146  tex->set_minfilter(SamplerState::FT_linear);
1147  } else {
1148  tex->set_minfilter(SamplerState::FT_linear_mipmap_nearest);
1149  }
1150  break;
1151 
1152  case EggTexture::FT_nearest_mipmap_linear:
1153  if (egg_ignore_filters) {
1154  egg2pg_cat.warning()
1155  << "Ignoring minfilter request\n";
1156  tex->set_minfilter(SamplerState::FT_nearest);
1157  } else if (egg_ignore_mipmaps) {
1158  egg2pg_cat.warning()
1159  << "Ignoring mipmap request\n";
1160  tex->set_minfilter(SamplerState::FT_nearest);
1161  } else {
1162  tex->set_minfilter(SamplerState::FT_nearest_mipmap_linear);
1163  }
1164  break;
1165 
1166  case EggTexture::FT_linear_mipmap_linear:
1167  if (egg_ignore_filters) {
1168  egg2pg_cat.warning()
1169  << "Ignoring minfilter request\n";
1170  tex->set_minfilter(SamplerState::FT_nearest);
1171  } else if (egg_ignore_mipmaps) {
1172  egg2pg_cat.warning()
1173  << "Ignoring mipmap request\n";
1174  tex->set_minfilter(SamplerState::FT_linear);
1175  } else {
1176  tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
1177  }
1178  break;
1179 
1180  case EggTexture::FT_unspecified:
1181  break;
1182  }
1183 
1184  switch (egg_tex->get_magfilter()) {
1185  case EggTexture::FT_nearest:
1186  case EggTexture::FT_nearest_mipmap_nearest:
1187  case EggTexture::FT_nearest_mipmap_linear:
1188  tex->set_magfilter(SamplerState::FT_nearest);
1189  break;
1190 
1191  case EggTexture::FT_linear:
1192  case EggTexture::FT_linear_mipmap_nearest:
1193  case EggTexture::FT_linear_mipmap_linear:
1194  if (egg_ignore_filters) {
1195  egg2pg_cat.warning()
1196  << "Ignoring magfilter request\n";
1197  tex->set_magfilter(SamplerState::FT_nearest);
1198  } else {
1199  tex->set_magfilter(SamplerState::FT_linear);
1200  }
1201  break;
1202 
1203  case EggTexture::FT_unspecified:
1204  break;
1205  }
1206 
1207  if (egg_tex->has_anisotropic_degree()) {
1208  tex->set_anisotropic_degree(egg_tex->get_anisotropic_degree());
1209  }
1210 
1211  if (tex->get_num_components() == 1) {
1212  switch (egg_tex->get_format()) {
1213  case EggTexture::F_red:
1214  tex->set_format(Texture::F_red);
1215  break;
1216  case EggTexture::F_green:
1217  tex->set_format(Texture::F_green);
1218  break;
1219  case EggTexture::F_blue:
1220  tex->set_format(Texture::F_blue);
1221  break;
1222  case EggTexture::F_alpha:
1223  tex->set_format(Texture::F_alpha);
1224  break;
1225  case EggTexture::F_luminance:
1226  tex->set_format(Texture::F_luminance);
1227  break;
1228 
1229  case EggTexture::F_unspecified:
1230  break;
1231 
1232  default:
1233  egg2pg_cat.warning()
1234  << "Ignoring inappropriate format " << egg_tex->get_format()
1235  << " for 1-component texture " << egg_tex->get_name() << "\n";
1236  }
1237 
1238  } else if (tex->get_num_components() == 2) {
1239  switch (egg_tex->get_format()) {
1240  case EggTexture::F_luminance_alpha:
1241  tex->set_format(Texture::F_luminance_alpha);
1242  break;
1243 
1244  case EggTexture::F_luminance_alphamask:
1245  tex->set_format(Texture::F_luminance_alphamask);
1246  break;
1247 
1248  case EggTexture::F_unspecified:
1249  break;
1250 
1251  default:
1252  egg2pg_cat.warning()
1253  << "Ignoring inappropriate format " << egg_tex->get_format()
1254  << " for 2-component texture " << egg_tex->get_name() << "\n";
1255  }
1256 
1257  } else if (tex->get_num_components() == 3) {
1258  switch (egg_tex->get_format()) {
1259  case EggTexture::F_rgb:
1260  tex->set_format(Texture::F_rgb);
1261  break;
1262  case EggTexture::F_rgb12:
1263  if (tex->get_component_width() >= 2) {
1264  // Only do this if the component width supports it.
1265  tex->set_format(Texture::F_rgb12);
1266  } else {
1267  egg2pg_cat.warning()
1268  << "Ignoring inappropriate format " << egg_tex->get_format()
1269  << " for 8-bit texture " << egg_tex->get_name() << "\n";
1270  }
1271  break;
1272  case EggTexture::F_rgb8:
1273  case EggTexture::F_rgba8:
1274  // We'll quietly accept RGBA8 for a 3-component texture, since
1275  // flt2egg generates these for 3-component as well as for
1276  // 4-component textures.
1277  tex->set_format(Texture::F_rgb8);
1278  break;
1279  case EggTexture::F_rgb5:
1280  tex->set_format(Texture::F_rgb5);
1281  break;
1282  case EggTexture::F_rgb332:
1283  tex->set_format(Texture::F_rgb332);
1284  break;
1285 
1286  case EggTexture::F_unspecified:
1287  break;
1288 
1289  default:
1290  egg2pg_cat.warning()
1291  << "Ignoring inappropriate format " << egg_tex->get_format()
1292  << " for 3-component texture " << egg_tex->get_name() << "\n";
1293  }
1294 
1295  } else if (tex->get_num_components() == 4) {
1296  switch (egg_tex->get_format()) {
1297  case EggTexture::F_rgba:
1298  tex->set_format(Texture::F_rgba);
1299  break;
1300  case EggTexture::F_rgbm:
1301  tex->set_format(Texture::F_rgbm);
1302  break;
1303  case EggTexture::F_rgba12:
1304  if (tex->get_component_width() >= 2) {
1305  // Only do this if the component width supports it.
1306  tex->set_format(Texture::F_rgba12);
1307  } else {
1308  egg2pg_cat.warning()
1309  << "Ignoring inappropriate format " << egg_tex->get_format()
1310  << " for 8-bit texture " << egg_tex->get_name() << "\n";
1311  }
1312  break;
1313  case EggTexture::F_rgba8:
1314  tex->set_format(Texture::F_rgba8);
1315  break;
1316  case EggTexture::F_rgba4:
1317  tex->set_format(Texture::F_rgba4);
1318  break;
1319  case EggTexture::F_rgba5:
1320  tex->set_format(Texture::F_rgba5);
1321  break;
1322 
1323  case EggTexture::F_unspecified:
1324  break;
1325 
1326  default:
1327  egg2pg_cat.warning()
1328  << "Ignoring inappropriate format " << egg_tex->get_format()
1329  << " for 4-component texture " << egg_tex->get_name() << "\n";
1330  }
1331  }
1332 
1333  switch (egg_tex->get_quality_level()) {
1334  case EggTexture::QL_unspecified:
1335  case EggTexture::QL_default:
1336  tex->set_quality_level(Texture::QL_default);
1337  break;
1338 
1339  case EggTexture::QL_fastest:
1340  tex->set_quality_level(Texture::QL_fastest);
1341  break;
1342 
1343  case EggTexture::QL_normal:
1344  tex->set_quality_level(Texture::QL_normal);
1345  break;
1346 
1347  case EggTexture::QL_best:
1348  tex->set_quality_level(Texture::QL_best);
1349  break;
1350  }
1351 }
1352 
1353 ////////////////////////////////////////////////////////////////////
1354 // Function: EggLoader::convert_compression_mode
1355 // Access: Private
1356 // Description: Returns the Texture::CompressionMode enum
1357 // corresponding to the EggTexture::CompressionMode.
1358 // Returns CM_default if the compression mode is
1359 // unspecified.
1360 ////////////////////////////////////////////////////////////////////
1361 Texture::CompressionMode EggLoader::
1362 convert_compression_mode(EggTexture::CompressionMode compression_mode) const {
1363  switch (compression_mode) {
1364  case EggTexture::CM_off:
1365  return Texture::CM_off;
1366 
1367  case EggTexture::CM_on:
1368  return Texture::CM_on;
1369 
1370  case EggTexture::CM_fxt1:
1371  return Texture::CM_fxt1;
1372 
1373  case EggTexture::CM_dxt1:
1374  return Texture::CM_dxt1;
1375 
1376  case EggTexture::CM_dxt2:
1377  return Texture::CM_dxt2;
1378 
1379  case EggTexture::CM_dxt3:
1380  return Texture::CM_dxt3;
1381 
1382  case EggTexture::CM_dxt4:
1383  return Texture::CM_dxt4;
1384 
1385  case EggTexture::CM_dxt5:
1386  return Texture::CM_dxt5;
1387 
1388  case EggTexture::CM_default:
1389  return Texture::CM_default;
1390  }
1391 
1392  egg2pg_cat.warning()
1393  << "Unexpected texture compression flag: " << (int)compression_mode << "\n";
1394  return Texture::CM_default;
1395 }
1396 
1397 ////////////////////////////////////////////////////////////////////
1398 // Function: EggLoader::convert_wrap_mode
1399 // Access: Private
1400 // Description: Returns the SamplerState::WrapMode enum corresponding to
1401 // the EggTexture::WrapMode. Returns WM_repeat if the
1402 // wrap mode is unspecified.
1403 ////////////////////////////////////////////////////////////////////
1404 SamplerState::WrapMode EggLoader::
1405 convert_wrap_mode(EggTexture::WrapMode wrap_mode) const {
1406  switch (wrap_mode) {
1407  case EggTexture::WM_clamp:
1408  return SamplerState::WM_clamp;
1409 
1410  case EggTexture::WM_repeat:
1411  return SamplerState::WM_repeat;
1412 
1413  case EggTexture::WM_mirror:
1414  return SamplerState::WM_mirror;
1415 
1416  case EggTexture::WM_mirror_once:
1417  return SamplerState::WM_mirror_once;
1418 
1419  case EggTexture::WM_border_color:
1420  return SamplerState::WM_border_color;
1421 
1422  case EggTexture::WM_unspecified:
1423  return SamplerState::WM_repeat;
1424  }
1425 
1426  egg2pg_cat.warning()
1427  << "Unexpected texture wrap flag: " << (int)wrap_mode << "\n";
1428  return SamplerState::WM_repeat;
1429 }
1430 
1431 ////////////////////////////////////////////////////////////////////
1432 // Function: EggLoader::make_texture_stage
1433 // Access: Private
1434 // Description: Creates a TextureStage object suitable for rendering
1435 // the indicated texture.
1436 ////////////////////////////////////////////////////////////////////
1437 PT(TextureStage) EggLoader::
1438 make_texture_stage(const EggTexture *egg_tex) {
1439  // If the egg texture specifies any relevant TextureStage
1440  // properties, or if it is multitextured on top of anything else, it
1441  // gets its own texture stage; otherwise, it gets the default
1442  // texture stage.
1443  if (!egg_tex->has_stage_name() &&
1444  !egg_tex->has_uv_name() &&
1445  !egg_tex->has_color() &&
1446  (egg_tex->get_env_type() == EggTexture::ET_unspecified ||
1447  egg_tex->get_env_type() == EggTexture::ET_modulate) &&
1448  egg_tex->get_combine_mode(EggTexture::CC_rgb) == EggTexture::CM_unspecified &&
1449  egg_tex->get_combine_mode(EggTexture::CC_alpha) == EggTexture::CM_unspecified &&
1450 
1451  !egg_tex->has_priority() &&
1452  egg_tex->get_multitexture_sort() == 0 &&
1453  !egg_tex->get_saved_result()) {
1454  return TextureStage::get_default();
1455  }
1456 
1457  PT(TextureStage) stage = new TextureStage(egg_tex->get_stage_name());
1458 
1459  switch (egg_tex->get_env_type()) {
1460  case EggTexture::ET_modulate:
1461  stage->set_mode(TextureStage::M_modulate);
1462  break;
1463 
1464  case EggTexture::ET_decal:
1465  stage->set_mode(TextureStage::M_decal);
1466  break;
1467 
1468  case EggTexture::ET_blend:
1469  stage->set_mode(TextureStage::M_blend);
1470  break;
1471 
1472  case EggTexture::ET_replace:
1473  stage->set_mode(TextureStage::M_replace);
1474  break;
1475 
1476  case EggTexture::ET_add:
1477  stage->set_mode(TextureStage::M_add);
1478  break;
1479 
1480  case EggTexture::ET_blend_color_scale:
1481  stage->set_mode(TextureStage::M_blend_color_scale);
1482  break;
1483 
1484  case EggTexture::ET_modulate_glow:
1485  stage->set_mode(TextureStage::M_modulate_glow);
1486  break;
1487 
1488  case EggTexture::ET_modulate_gloss:
1489  stage->set_mode(TextureStage::M_modulate_gloss);
1490  break;
1491 
1492  case EggTexture::ET_normal:
1493  stage->set_mode(TextureStage::M_normal);
1494  break;
1495 
1496  case EggTexture::ET_normal_height:
1497  stage->set_mode(TextureStage::M_normal_height);
1498  break;
1499 
1500  case EggTexture::ET_glow:
1501  stage->set_mode(TextureStage::M_glow);
1502  break;
1503 
1504  case EggTexture::ET_gloss:
1505  stage->set_mode(TextureStage::M_gloss);
1506  break;
1507 
1508  case EggTexture::ET_height:
1509  stage->set_mode(TextureStage::M_height);
1510  break;
1511 
1512  case EggTexture::ET_selector:
1513  stage->set_mode(TextureStage::M_selector);
1514  break;
1515 
1516  case EggTexture::ET_normal_gloss:
1517  stage->set_mode(TextureStage::M_normal_gloss);
1518  break;
1519 
1520  case EggTexture::ET_unspecified:
1521  break;
1522  }
1523 
1524  switch (egg_tex->get_combine_mode(EggTexture::CC_rgb)) {
1525  case EggTexture::CM_replace:
1526  stage->set_combine_rgb(get_combine_mode(egg_tex, EggTexture::CC_rgb),
1527  get_combine_source(egg_tex, EggTexture::CC_rgb, 0),
1528  get_combine_operand(egg_tex, EggTexture::CC_rgb, 0));
1529  break;
1530 
1531  case EggTexture::CM_modulate:
1532  case EggTexture::CM_add:
1533  case EggTexture::CM_add_signed:
1534  case EggTexture::CM_subtract:
1535  case EggTexture::CM_dot3_rgb:
1536  case EggTexture::CM_dot3_rgba:
1537  stage->set_combine_rgb(get_combine_mode(egg_tex, EggTexture::CC_rgb),
1538  get_combine_source(egg_tex, EggTexture::CC_rgb, 0),
1539  get_combine_operand(egg_tex, EggTexture::CC_rgb, 0),
1540  get_combine_source(egg_tex, EggTexture::CC_rgb, 1),
1541  get_combine_operand(egg_tex, EggTexture::CC_rgb, 1));
1542  break;
1543 
1544  case EggTexture::CM_interpolate:
1545  stage->set_combine_rgb(get_combine_mode(egg_tex, EggTexture::CC_rgb),
1546  get_combine_source(egg_tex, EggTexture::CC_rgb, 0),
1547  get_combine_operand(egg_tex, EggTexture::CC_rgb, 0),
1548  get_combine_source(egg_tex, EggTexture::CC_rgb, 1),
1549  get_combine_operand(egg_tex, EggTexture::CC_rgb, 1),
1550  get_combine_source(egg_tex, EggTexture::CC_rgb, 2),
1551  get_combine_operand(egg_tex, EggTexture::CC_rgb, 2));
1552  break;
1553 
1554  case EggTexture::CM_unspecified:
1555  break;
1556  }
1557 
1558  switch (egg_tex->get_combine_mode(EggTexture::CC_alpha)) {
1559  case EggTexture::CM_replace:
1560  stage->set_combine_alpha(get_combine_mode(egg_tex, EggTexture::CC_alpha),
1561  get_combine_source(egg_tex, EggTexture::CC_alpha, 0),
1562  get_combine_operand(egg_tex, EggTexture::CC_alpha, 0));
1563  break;
1564 
1565  case EggTexture::CM_modulate:
1566  case EggTexture::CM_add:
1567  case EggTexture::CM_add_signed:
1568  case EggTexture::CM_subtract:
1569  stage->set_combine_alpha(get_combine_mode(egg_tex, EggTexture::CC_alpha),
1570  get_combine_source(egg_tex, EggTexture::CC_alpha, 0),
1571  get_combine_operand(egg_tex, EggTexture::CC_alpha, 0),
1572  get_combine_source(egg_tex, EggTexture::CC_alpha, 1),
1573  get_combine_operand(egg_tex, EggTexture::CC_alpha, 1));
1574  break;
1575 
1576  case EggTexture::CM_interpolate:
1577  stage->set_combine_alpha(get_combine_mode(egg_tex, EggTexture::CC_alpha),
1578  get_combine_source(egg_tex, EggTexture::CC_alpha, 0),
1579  get_combine_operand(egg_tex, EggTexture::CC_alpha, 0),
1580  get_combine_source(egg_tex, EggTexture::CC_alpha, 1),
1581  get_combine_operand(egg_tex, EggTexture::CC_alpha, 1),
1582  get_combine_source(egg_tex, EggTexture::CC_alpha, 2),
1583  get_combine_operand(egg_tex, EggTexture::CC_alpha, 2));
1584  break;
1585 
1586  case EggTexture::CM_unspecified:
1587  case EggTexture::CM_dot3_rgb:
1588  case EggTexture::CM_dot3_rgba:
1589  break;
1590  }
1591 
1592 
1593  if (egg_tex->has_uv_name()) {
1594  PT(InternalName) name =
1595  InternalName::get_texcoord_name(egg_tex->get_uv_name());
1596  stage->set_texcoord_name(name);
1597  }
1598 
1599  if (egg_tex->has_rgb_scale()) {
1600  stage->set_rgb_scale(egg_tex->get_rgb_scale());
1601  }
1602 
1603  if (egg_tex->has_alpha_scale()) {
1604  stage->set_alpha_scale(egg_tex->get_alpha_scale());
1605  }
1606 
1607  stage->set_saved_result(egg_tex->get_saved_result());
1608 
1609  stage->set_sort(egg_tex->get_multitexture_sort() * 10);
1610 
1611  if (egg_tex->has_priority()) {
1612  stage->set_sort(egg_tex->get_priority());
1613  }
1614 
1615  if (egg_tex->has_color()) {
1616  stage->set_color(egg_tex->get_color());
1617  }
1618 
1619  return TextureStagePool::get_stage(stage);
1620 }
1621 
1622 ////////////////////////////////////////////////////////////////////
1623 // Function: EggLoader::separate_switches
1624 // Access: Private
1625 // Description: Walks the tree recursively, looking for EggPrimitives
1626 // that are children of sequence or switch nodes. If
1627 // any are found, they are moved within their own group
1628 // to protect them from being flattened with their
1629 // neighbors.
1630 ////////////////////////////////////////////////////////////////////
1631 void EggLoader::
1632 separate_switches(EggNode *egg_node) {
1633  bool parent_has_switch = false;
1634  if (egg_node->is_of_type(EggGroup::get_class_type())) {
1635  EggGroup *egg_group = DCAST(EggGroup, egg_node);
1636  parent_has_switch = egg_group->get_switch_flag();
1637  }
1638 
1639  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
1640  EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
1641 
1642  EggGroupNode::iterator ci;
1643  ci = egg_group->begin();
1644  while (ci != egg_group->end()) {
1645  EggGroupNode::iterator cnext;
1646  cnext = ci;
1647  ++cnext;
1648 
1649  PT(EggNode) child = (*ci);
1650  if (parent_has_switch &&
1651  child->is_of_type(EggPrimitive::get_class_type())) {
1652  // Move this child under a new node.
1653  PT(EggGroup) new_group = new EggGroup(child->get_name());
1654  egg_group->replace(ci, new_group.p());
1655  new_group->add_child(child);
1656  }
1657 
1658  separate_switches(child);
1659 
1660  ci = cnext;
1661  }
1662  }
1663 }
1664 
1665 ////////////////////////////////////////////////////////////////////
1666 // Function: EggLoader::emulate_bface
1667 // Access: Private
1668 // Description: Looks for EggPolygons with a bface flag applied to
1669 // them. Any such polygons are duplicated into a pair
1670 // of back-to-back polygons, and the bface flag is
1671 // removed.
1672 ////////////////////////////////////////////////////////////////////
1673 void EggLoader::
1674 emulate_bface(EggNode *egg_node) {
1675  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
1676  EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node);
1677  PT(EggGroupNode) dup_prims = new EggGroupNode;
1678 
1679  EggGroupNode::iterator ci;
1680  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1681  PT(EggNode) child = (*ci);
1682  if (child->is_of_type(EggPolygon::get_class_type())) {
1683  EggPolygon *poly = DCAST(EggPolygon, child);
1684  if (poly->get_bface_flag()) {
1685  poly->set_bface_flag(false);
1686 
1687  PT(EggPolygon) dup_poly = new EggPolygon(*poly);
1688  dup_poly->reverse_vertex_ordering();
1689  if (dup_poly->has_normal()) {
1690  dup_poly->set_normal(-dup_poly->get_normal());
1691  }
1692 
1693  // Also reverse the normal on any vertices.
1694  EggPolygon::iterator vi;
1695  for (vi = dup_poly->begin(); vi != dup_poly->end(); ++vi) {
1696  EggVertex *vertex = (*vi);
1697  if (vertex->has_normal()) {
1698  EggVertex dup_vertex(*vertex);
1699  dup_vertex.set_normal(-dup_vertex.get_normal());
1700  EggVertex *new_vertex = vertex->get_pool()->create_unique_vertex(dup_vertex);
1701  if (new_vertex != vertex) {
1702  new_vertex->copy_grefs_from(*vertex);
1703  dup_poly->replace(vi, new_vertex);
1704  }
1705  }
1706  }
1707  dup_prims->add_child(dup_poly);
1708  }
1709  }
1710 
1711  emulate_bface(child);
1712  }
1713 
1714  // Now that we've iterated through all the children, add in any
1715  // duplicated polygons we generated.
1716  egg_group->steal_children(*dup_prims);
1717  }
1718 }
1719 
1720 ////////////////////////////////////////////////////////////////////
1721 // Function: EggLoader::make_node
1722 // Access: Private
1723 // Description:
1724 ////////////////////////////////////////////////////////////////////
1725 PandaNode *EggLoader::
1726 make_node(EggNode *egg_node, PandaNode *parent) {
1727  if (egg_node->is_of_type(EggBin::get_class_type())) {
1728  return make_node(DCAST(EggBin, egg_node), parent);
1729  } else if (egg_node->is_of_type(EggGroup::get_class_type())) {
1730  return make_node(DCAST(EggGroup, egg_node), parent);
1731  } else if (egg_node->is_of_type(EggTable::get_class_type())) {
1732  return make_node(DCAST(EggTable, egg_node), parent);
1733  } else if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
1734  return make_node(DCAST(EggGroupNode, egg_node), parent);
1735  }
1736 
1737  return (PandaNode *)NULL;
1738 }
1739 
1740 ////////////////////////////////////////////////////////////////////
1741 // Function: EggLoader::make_node (EggBin)
1742 // Access: Private
1743 // Description:
1744 ////////////////////////////////////////////////////////////////////
1745 PandaNode *EggLoader::
1746 make_node(EggBin *egg_bin, PandaNode *parent) {
1747  // An EggBin might mean an LOD node (i.e. a parent of one or more
1748  // EggGroups with LOD specifications), or it might mean a polyset
1749  // node (a parent of one or more similar EggPrimitives).
1750  switch (egg_bin->get_bin_number()) {
1751  case EggBinner::BN_polyset:
1752  case EggBinner::BN_patches:
1753  make_polyset(egg_bin, parent, NULL, _dynamic_override, _dynamic_override_char_maker);
1754  return NULL;
1755 
1756  case EggBinner::BN_lod:
1757  return make_lod(egg_bin, parent);
1758 
1759  case EggBinner::BN_nurbs_surface:
1760  {
1761  nassertr(!egg_bin->empty(), NULL);
1762  EggNode *child = egg_bin->get_first_child();
1763  EggNurbsSurface *egg_nurbs;
1764  DCAST_INTO_R(egg_nurbs, child, NULL);
1765  const LMatrix4d &mat = egg_nurbs->get_vertex_to_node();
1766  make_nurbs_surface(egg_nurbs, parent, mat);
1767  }
1768  return NULL;
1769 
1770  case EggBinner::BN_nurbs_curve:
1771  {
1772  nassertr(!egg_bin->empty(), NULL);
1773  EggNode *child = egg_bin->get_first_child();
1774  EggNurbsCurve *egg_nurbs;
1775  DCAST_INTO_R(egg_nurbs, child, NULL);
1776  const LMatrix4d &mat = egg_nurbs->get_vertex_to_node();
1777  make_nurbs_curve(egg_nurbs, parent, mat);
1778  }
1779  return NULL;
1780 
1781  case EggBinner::BN_none:
1782  break;
1783  }
1784 
1785  // Shouldn't get here.
1786  return (PandaNode *)NULL;
1787 }
1788 
1789 ////////////////////////////////////////////////////////////////////
1790 // Function: EggLoader::make_lod
1791 // Access: Private
1792 // Description:
1793 ////////////////////////////////////////////////////////////////////
1794 PandaNode *EggLoader::
1795 make_lod(EggBin *egg_bin, PandaNode *parent) {
1796  PT(LODNode) lod_node = LODNode::make_default_lod(egg_bin->get_name());
1797 
1798  pvector<LODInstance> instances;
1799 
1800  EggGroup::const_iterator ci;
1801  for (ci = egg_bin->begin(); ci != egg_bin->end(); ++ci) {
1802  LODInstance instance(*ci);
1803  instances.push_back(instance);
1804  }
1805 
1806  // Now that we've created all of our children, put them in the
1807  // proper order and tell the LOD node about them.
1808  sort(instances.begin(), instances.end());
1809 
1810  if (!instances.empty()) {
1811  // Set up the LOD node's center. All of the children should have
1812  // the same center, because that's how we binned them.
1813  lod_node->set_center(LCAST(PN_stdfloat, instances[0]._d->_center));
1814  }
1815 
1816  for (size_t i = 0; i < instances.size(); i++) {
1817  // Create the children in the proper order within the scene graph.
1818  const LODInstance &instance = instances[i];
1819  make_node(instance._egg_node, lod_node);
1820 
1821  // All of the children should have the same center, because that's
1822  // how we binned them.
1823  nassertr(lod_node->get_center().almost_equal
1824  (LCAST(PN_stdfloat, instance._d->_center), 0.01), NULL);
1825 
1826  // Tell the LOD node about this child's switching distances.
1827  lod_node->add_switch(instance._d->_switch_in, instance._d->_switch_out);
1828  }
1829 
1830  _groups[egg_bin] = lod_node;
1831  return create_group_arc(egg_bin, parent, lod_node);
1832 }
1833 
1834 ////////////////////////////////////////////////////////////////////
1835 // Function: EggLoader::make_node (EggGroup)
1836 // Access: Private
1837 // Description:
1838 ////////////////////////////////////////////////////////////////////
1839 PandaNode *EggLoader::
1840 make_node(EggGroup *egg_group, PandaNode *parent) {
1841  PT(PandaNode) node = NULL;
1842 
1843  if (egg_group->get_dart_type() != EggGroup::DT_none) {
1844  // A group with the <Dart> flag set means to create a character.
1845  bool structured = (egg_group->get_dart_type() == EggGroup::DT_structured);
1846 
1847  CharacterMaker char_maker(egg_group, *this, structured);
1848 
1849  node = char_maker.make_node();
1850  if(structured) {
1851  //we're going to generate the rest of the children normally
1852  //except we'll be making dynamic geometry
1853  _dynamic_override = true;
1854  _dynamic_override_char_maker = &char_maker;
1855  EggGroup::const_iterator ci;
1856  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1857  make_node(*ci, node);
1858  }
1859  _dynamic_override_char_maker = NULL;
1860  _dynamic_override = false;
1861  }
1862 
1863  } else if (egg_group->get_cs_type() != EggGroup::CST_none) {
1864  // A collision group: create collision geometry.
1865  node = new CollisionNode(egg_group->get_name());
1866 
1867  make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p());
1868 
1869  // Transform all of the collision solids into local space.
1870  node->xform(LCAST(PN_stdfloat, egg_group->get_vertex_to_node()));
1871 
1872  if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
1873  // If we also specified to keep the geometry, continue the
1874  // traversal. In this case, we create a new PandaNode to be the
1875  // parent of the visible geometry and the collision geometry.
1876  PandaNode *combined = new PandaNode("");
1877  parent->add_child(combined);
1878  combined->add_child(node);
1879  node = combined;
1880 
1881  EggGroup::const_iterator ci;
1882  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1883  make_node(*ci, combined);
1884  }
1885  }
1886 
1887  node = create_group_arc(egg_group, parent, node);
1888  return node;
1889 
1890  } else if (egg_group->get_portal_flag()) {
1891  // Create a portal instead of a regular polyset. Scan the
1892  // children of this node looking for a polygon, similar to the
1893  // collision polygon case, above.
1894  PortalNode *pnode = new PortalNode(egg_group->get_name());
1895  node = pnode;
1896 
1897  set_portal_polygon(egg_group, pnode);
1898  if (pnode->get_num_vertices() == 0) {
1899  egg2pg_cat.warning()
1900  << "Portal " << egg_group->get_name() << " has no vertices!\n";
1901  }
1902 
1903  } else if (egg_group->get_occluder_flag()) {
1904  // Create an occluder instead of a regular polyset. Scan the
1905  // children of this node looking for a polygon, the same as the
1906  // portal polygon case, above.
1907  OccluderNode *pnode = new OccluderNode(egg_group->get_name());
1908  node = pnode;
1909 
1910  set_occluder_polygon(egg_group, pnode);
1911  if (pnode->get_num_vertices() == 0) {
1912  egg2pg_cat.warning()
1913  << "Occluder " << egg_group->get_name() << " has no vertices!\n";
1914  }
1915 
1916  } else if (egg_group->get_polylight_flag()) {
1917  // Create a polylight instead of a regular polyset.
1918  // use make_sphere to get the center, radius and color
1919  //egg2pg_cat.debug() << "polylight node\n";
1920  LPoint3 center;
1921  LColor color;
1922  PN_stdfloat radius;
1923 
1924  if (!make_sphere(egg_group, EggGroup::CF_none, center, radius, color)) {
1925  egg2pg_cat.warning()
1926  << "Polylight " << egg_group->get_name() << " make_sphere failed!\n";
1927  }
1928  PolylightNode *pnode = new PolylightNode(egg_group->get_name());
1929  pnode->set_pos(center);
1930  pnode->set_color(color);
1931  pnode->set_radius(radius);
1932 
1933  pnode->xform(LCAST(PN_stdfloat, egg_group->get_vertex_to_node()));
1934 
1935  node = pnode;
1936 
1937  } else if (egg_group->get_switch_flag()) {
1938  if (egg_group->get_switch_fps() != 0.0) {
1939  // Create a sequence node.
1940  node = new SequenceNode(egg_group->get_name());
1941  ((SequenceNode *)node.p())->set_frame_rate(egg_group->get_switch_fps());
1942  _sequences.insert(node);
1943  } else {
1944  // Create a switch node.
1945  node = new SwitchNode(egg_group->get_name());
1946  }
1947 
1948  EggGroup::const_iterator ci;
1949  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1950  make_node(*ci, node);
1951  }
1952  } else if (egg_group->has_scrolling_uvs()) {
1953  node = new UvScrollNode(egg_group->get_name(), egg_group->get_scroll_u(), egg_group->get_scroll_v(), egg_group->get_scroll_w(), egg_group->get_scroll_r());
1954 
1955  EggGroup::const_iterator ci;
1956  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1957  make_node(*ci, node);
1958  }
1959 
1960  } else if (egg_group->get_model_flag() || egg_group->has_dcs_type()) {
1961  // A model or DCS flag; create a model node.
1962  node = new ModelNode(egg_group->get_name());
1963  switch (egg_group->get_dcs_type()) {
1964  case EggGroup::DC_net:
1965  DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_net);
1966  break;
1967 
1968  case EggGroup::DC_no_touch:
1969  DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_no_touch);
1970  break;
1971 
1972  case EggGroup::DC_local:
1973  case EggGroup::DC_default:
1974  DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_local);
1975  break;
1976 
1977  case EggGroup::DC_none:
1978  case EggGroup::DC_unspecified:
1979  break;
1980  }
1981 
1982  EggGroup::const_iterator ci;
1983  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1984  make_node(*ci, node);
1985  }
1986 
1987  } else {
1988  // A normal group; just create a normal node, and traverse. But
1989  // if all of the children of this group are polysets, anticipate
1990  // this for the benefit of smaller grouping, and create a single
1991  // GeomNode for all of the children.
1992  bool all_polysets = false;
1993  bool any_hidden = false;
1994 
1995  // We don't want to ever create a GeomNode under a "decal" flag,
1996  // since that can confuse the decal reparenting.
1997  if (!egg_group->determine_decal()) {
1998  check_for_polysets(egg_group, all_polysets, any_hidden);
1999  }
2000 
2001  if (all_polysets && !any_hidden) {
2002  node = new GeomNode(egg_group->get_name());
2003  } else {
2004  node = new PandaNode(egg_group->get_name());
2005  }
2006 
2007  EggGroup::const_iterator ci;
2008  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2009  make_node(*ci, node);
2010  }
2011  }
2012 
2013  if (node == (PandaNode *)NULL) {
2014  return NULL;
2015  }
2016 
2017  // Associate any instances with this node.
2018  int num_group_refs = egg_group->get_num_group_refs();
2019  for (int gri = 0; gri < num_group_refs; ++gri) {
2020  EggGroup *group_ref = egg_group->get_group_ref(gri);
2021  Groups::const_iterator gi = _groups.find(group_ref);
2022  if (gi != _groups.end()) {
2023  PandaNode *node_ref = (*gi).second;
2024  node->add_child(node_ref);
2025  }
2026  }
2027 
2028  _groups[egg_group] = node;
2029  return create_group_arc(egg_group, parent, node);
2030 }
2031 
2032 ////////////////////////////////////////////////////////////////////
2033 // Function: EggLoader::create_group_arc
2034 // Access: Private
2035 // Description: Creates the arc parenting a new group to the scene
2036 // graph, and applies any relevant attribs to the
2037 // arc according to the EggGroup node that inspired the
2038 // group.
2039 ////////////////////////////////////////////////////////////////////
2040 PandaNode *EggLoader::
2041 create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
2042  parent->add_child(node);
2043 
2044  // If the group had a transform, apply it to the node.
2045  if (egg_group->has_transform()) {
2046  CPT(TransformState) transform = make_transform(egg_group);
2047  node->set_transform(transform);
2048  node->set_prev_transform(transform);
2049  }
2050 
2051  // If the group has a billboard flag, apply that.
2052  switch (egg_group->get_billboard_type()) {
2053  case EggGroup::BT_point_camera_relative:
2054  node->set_effect(BillboardEffect::make_point_eye());
2055  break;
2056 
2057  case EggGroup::BT_point_world_relative:
2058  node->set_effect(BillboardEffect::make_point_world());
2059  break;
2060 
2061  case EggGroup::BT_axis:
2062  node->set_effect(BillboardEffect::make_axis());
2063  break;
2064 
2065  case EggGroup::BT_none:
2066  break;
2067  }
2068 
2069  if (egg_group->get_decal_flag()) {
2070  if (egg_ignore_decals) {
2071  egg2pg_cat.error()
2072  << "Ignoring decal flag on " << egg_group->get_name() << "\n";
2073  _error = true;
2074  }
2075 
2076  // If the group has the "decal" flag set, it means that all of the
2077  // descendant groups will be decaled onto the geometry within
2078  // this group. This means we'll need to reparent things a bit
2079  // afterward.
2080  _decals.insert(node);
2081  }
2082 
2083  // Copy all the tags from the group onto the node.
2084  EggGroup::TagData::const_iterator ti;
2085  for (ti = egg_group->tag_begin(); ti != egg_group->tag_end(); ++ti) {
2086  node->set_tag((*ti).first, (*ti).second);
2087  }
2088 
2089  if (egg_group->get_blend_mode() != EggGroup::BM_unspecified &&
2090  egg_group->get_blend_mode() != EggGroup::BM_none) {
2091  // Apply a ColorBlendAttrib to the group.
2092  ColorBlendAttrib::Mode mode = get_color_blend_mode(egg_group->get_blend_mode());
2093  ColorBlendAttrib::Operand a = get_color_blend_operand(egg_group->get_blend_operand_a());
2094  ColorBlendAttrib::Operand b = get_color_blend_operand(egg_group->get_blend_operand_b());
2095  LColor color = egg_group->get_blend_color();
2096  node->set_attrib(ColorBlendAttrib::make(mode, a, b, color));
2097  }
2098 
2099  // If the group specified some property that should propagate down
2100  // to the leaves, we have to remember this node and apply the
2101  // property later, after we've created the actual geometry.
2103  if (egg_group->has_collide_mask()) {
2104  def._from_collide_mask = egg_group->get_collide_mask();
2105  def._into_collide_mask = egg_group->get_collide_mask();
2106  def._flags |=
2107  DeferredNodeProperty::F_has_from_collide_mask |
2108  DeferredNodeProperty::F_has_into_collide_mask;
2109  }
2110  if (egg_group->has_from_collide_mask()) {
2111  def._from_collide_mask = egg_group->get_from_collide_mask();
2112  def._flags |= DeferredNodeProperty::F_has_from_collide_mask;
2113  }
2114  if (egg_group->has_into_collide_mask()) {
2115  def._into_collide_mask = egg_group->get_into_collide_mask();
2116  def._flags |= DeferredNodeProperty::F_has_into_collide_mask;
2117  }
2118 
2119  if (def._flags != 0) {
2120  _deferred_nodes[node] = def;
2121  }
2122 
2123  return node;
2124 }
2125 
2126 ////////////////////////////////////////////////////////////////////
2127 // Function: EggLoader::make_node (EggTable)
2128 // Access: Private
2129 // Description:
2130 ////////////////////////////////////////////////////////////////////
2131 PandaNode *EggLoader::
2132 make_node(EggTable *egg_table, PandaNode *parent) {
2133  if (egg_table->get_table_type() != EggTable::TT_bundle) {
2134  // We only do anything with bundles. Isolated tables are treated
2135  // as ordinary groups.
2136  return make_node(DCAST(EggGroupNode, egg_table), parent);
2137  }
2138 
2139  // It's an actual bundle, so make an AnimBundle from it and its
2140  // descendants.
2141  AnimBundleMaker bundle_maker(egg_table);
2142  AnimBundleNode *node = bundle_maker.make_node();
2143  parent->add_child(node);
2144  return node;
2145 }
2146 
2147 
2148 ////////////////////////////////////////////////////////////////////
2149 // Function: EggLoader::make_node (EggGroupNode)
2150 // Access: Private
2151 // Description:
2152 ////////////////////////////////////////////////////////////////////
2153 PandaNode *EggLoader::
2154 make_node(EggGroupNode *egg_group, PandaNode *parent) {
2155  PandaNode *node = new PandaNode(egg_group->get_name());
2156 
2157  EggGroupNode::const_iterator ci;
2158  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2159  make_node(*ci, node);
2160  }
2161 
2162  parent->add_child(node);
2163  return node;
2164 }
2165 
2166 ////////////////////////////////////////////////////////////////////
2167 // Function: EggLoader::check_for_polysets
2168 // Access: Private
2169 // Description: Sets all_polysets true if all of the children of this
2170 // node represent a polyset. Sets any_hidden true if
2171 // any of those polysets are flagged hidden.
2172 ////////////////////////////////////////////////////////////////////
2173 void EggLoader::
2174 check_for_polysets(EggGroup *egg_group, bool &all_polysets, bool &any_hidden) {
2175  all_polysets = (!egg_group->empty());
2176  any_hidden = false;
2177 
2178  EggGroup::const_iterator ci;
2179  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2180  if ((*ci)->is_of_type(EggBin::get_class_type())) {
2181  EggBin *egg_bin = DCAST(EggBin, (*ci));
2182  if (egg_bin->get_bin_number() == EggBinner::BN_polyset) {
2183  // We know that all of the primitives in the bin have the same
2184  // render state, so we can get that information from the first
2185  // primitive.
2186  EggGroup::const_iterator bci = egg_bin->begin();
2187  nassertv(bci != egg_bin->end());
2188  const EggPrimitive *first_prim;
2189  DCAST_INTO_V(first_prim, (*bci));
2190  const EggRenderState *render_state;
2191  DCAST_INTO_V(render_state, first_prim->get_user_data(EggRenderState::get_class_type()));
2192 
2193  if (render_state->_hidden) {
2194  any_hidden = true;
2195  }
2196  } else {
2197  all_polysets = false;
2198  return;
2199  }
2200  } else if ((*ci)->is_of_type(EggGroup::get_class_type())) {
2201  // Other kinds of children, like vertex pools, comments,
2202  // textures, etc., are ignored; but groups indicate more nodes,
2203  // so if we find a nested group it means we're not all polysets.
2204  all_polysets = false;
2205  return;
2206  }
2207  }
2208 }
2209 
2210 ////////////////////////////////////////////////////////////////////
2211 // Function: EggLoader::make_vertex_data
2212 // Access: Private
2213 // Description: Creates a GeomVertexData structure from the vertex
2214 // pool, for the indicated transform space. If a
2215 // GeomVertexData has already been created for this
2216 // transform, just returns it.
2217 ////////////////////////////////////////////////////////////////////
2219 make_vertex_data(const EggRenderState *render_state,
2220  EggVertexPool *vertex_pool, EggNode *primitive_home,
2221  const LMatrix4d &transform, TransformBlendTable *blend_table,
2222  bool is_dynamic, CharacterMaker *character_maker,
2223  bool ignore_color) {
2224  VertexPoolTransform vpt;
2225  vpt._vertex_pool = vertex_pool;
2226  vpt._bake_in_uvs = render_state->_bake_in_uvs;
2227  vpt._transform = transform;
2228 
2229  VertexPoolData::iterator di;
2230  di = _vertex_pool_data.find(vpt);
2231  if (di != _vertex_pool_data.end()) {
2232  return (*di).second;
2233  }
2234 
2235  PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat;
2236  array_format->add_column
2237  (InternalName::get_vertex(), vertex_pool->get_num_dimensions(),
2238  Geom::NT_stdfloat, Geom::C_point);
2239 
2240  if (vertex_pool->has_normals()) {
2241  array_format->add_column
2242  (InternalName::get_normal(), 3,
2243  Geom::NT_stdfloat, Geom::C_normal);
2244  }
2245 
2246  if (!ignore_color) {
2247  // Let's not use Direct3D-style colors on platforms where we only
2248  // have OpenGL anyway.
2249 #ifdef _WIN32
2250  array_format->add_column(InternalName::get_color(), 1,
2251  Geom::NT_packed_dabc, Geom::C_color);
2252 #else
2253  array_format->add_column(InternalName::get_color(), 4,
2254  Geom::NT_uint8, Geom::C_color);
2255 #endif
2256  }
2257 
2258  vector_string uv_names, uvw_names, tbn_names;
2259  vertex_pool->get_uv_names(uv_names, uvw_names, tbn_names);
2260  vector_string::const_iterator ni;
2261  for (ni = uv_names.begin(); ni != uv_names.end(); ++ni) {
2262  string name = (*ni);
2263 
2264  PT(InternalName) iname = InternalName::get_texcoord_name(name);
2265 
2266  if (find(uvw_names.begin(), uvw_names.end(), name) != uvw_names.end()) {
2267  // This one actually represents 3-d texture coordinates.
2268  array_format->add_column
2269  (iname, 3, Geom::NT_stdfloat, Geom::C_texcoord);
2270  } else {
2271  array_format->add_column
2272  (iname, 2, Geom::NT_stdfloat, Geom::C_texcoord);
2273  }
2274  }
2275  for (ni = tbn_names.begin(); ni != tbn_names.end(); ++ni) {
2276  string name = (*ni);
2277 
2278  PT(InternalName) iname_t = InternalName::get_tangent_name(name);
2279  PT(InternalName) iname_b = InternalName::get_binormal_name(name);
2280 
2281  array_format->add_column
2282  (iname_t, 3, Geom::NT_stdfloat, Geom::C_vector);
2283  array_format->add_column
2284  (iname_b, 3, Geom::NT_stdfloat, Geom::C_vector);
2285  }
2286 
2287  vector_string aux_names;
2288  vertex_pool->get_aux_names(aux_names);
2289  for (ni = aux_names.begin(); ni != aux_names.end(); ++ni) {
2290  string name = (*ni);
2291  PT(InternalName) iname = InternalName::make(name);
2292  array_format->add_column
2293  (iname, 4, Geom::NT_stdfloat, Geom::C_other);
2294  }
2295 
2296  PT(GeomVertexFormat) temp_format = new GeomVertexFormat(array_format);
2297 
2298  PT(SliderTable) slider_table;
2299  string name = _data->get_egg_filename().get_basename_wo_extension();
2300 
2301  if (is_dynamic) {
2302  // If it's a dynamic object, we need a TransformBlendTable and
2303  // maybe a SliderTable, and additional columns in the vertex data:
2304  // one that indexes into the blend table per vertex, and also
2305  // one for each different type of morph delta.
2306 
2307  // Tell the format that we're setting it up for Panda-based
2308  // animation.
2309  GeomVertexAnimationSpec animation;
2310  animation.set_panda();
2311  temp_format->set_animation(animation);
2312 
2313  PT(GeomVertexArrayFormat) anim_array_format = new GeomVertexArrayFormat;
2314  anim_array_format->add_column
2315  (InternalName::get_transform_blend(), 1,
2316  Geom::NT_uint16, Geom::C_index);
2317  temp_format->add_array(anim_array_format);
2318 
2319  pmap<string, BitArray> slider_names;
2320  EggVertexPool::const_iterator vi;
2321  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
2322  EggVertex *vertex = (*vi);
2323 
2324  EggMorphVertexList::const_iterator mvi;
2325  for (mvi = vertex->_dxyzs.begin(); mvi != vertex->_dxyzs.end(); ++mvi) {
2326  slider_names[(*mvi).get_name()].set_bit(vertex->get_index());
2327  record_morph(anim_array_format, character_maker, (*mvi).get_name(),
2328  InternalName::get_vertex(), 3);
2329  }
2330  if (vertex->has_normal()) {
2331  EggMorphNormalList::const_iterator mni;
2332  for (mni = vertex->_dnormals.begin(); mni != vertex->_dnormals.end(); ++mni) {
2333  slider_names[(*mni).get_name()].set_bit(vertex->get_index());
2334  record_morph(anim_array_format, character_maker, (*mni).get_name(),
2335  InternalName::get_normal(), 3);
2336  }
2337  }
2338  if (!ignore_color && vertex->has_color()) {
2339  EggMorphColorList::const_iterator mci;
2340  for (mci = vertex->_drgbas.begin(); mci != vertex->_drgbas.end(); ++mci) {
2341  slider_names[(*mci).get_name()].set_bit(vertex->get_index());
2342  record_morph(anim_array_format, character_maker, (*mci).get_name(),
2343  InternalName::get_color(), 4);
2344  }
2345  }
2347  for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
2348  EggVertexUV *egg_uv = (*uvi);
2349  string name = egg_uv->get_name();
2350  bool has_w = (find(uvw_names.begin(), uvw_names.end(), name) != uvw_names.end());
2351  PT(InternalName) iname = InternalName::get_texcoord_name(name);
2352 
2353  EggMorphTexCoordList::const_iterator mti;
2354  for (mti = egg_uv->_duvs.begin(); mti != egg_uv->_duvs.end(); ++mti) {
2355  slider_names[(*mti).get_name()].set_bit(vertex->get_index());
2356  record_morph(anim_array_format, character_maker, (*mti).get_name(),
2357  iname, has_w ? 3 : 2);
2358  }
2359  }
2360  }
2361 
2362  if (!slider_names.empty()) {
2363  // If we have any sliders at all, create a table for them.
2364 
2365  slider_table = new SliderTable;
2366 
2368  for (si = slider_names.begin(); si != slider_names.end(); ++si) {
2369  PT(VertexSlider) slider = character_maker->egg_to_slider((*si).first);
2370  slider_table->add_slider(slider, (*si).second);
2371  }
2372  }
2373 
2374  // We'll also assign the character name to the vertex data, so it
2375  // will show up in PStats.
2376  name = character_maker->get_name();
2377  }
2378 
2379  temp_format->maybe_align_columns_for_animation();
2380 
2381  CPT(GeomVertexFormat) format =
2382  GeomVertexFormat::register_format(temp_format);
2383 
2384  // Now create a new GeomVertexData using the indicated format. It
2385  // is actually correct to create it with UH_static even though it
2386  // represents a dynamic object, because the vertex data itself won't
2387  // be changing--just the result of applying the animation is
2388  // dynamic.
2389  PT(GeomVertexData) vertex_data =
2390  new GeomVertexData(name, format, Geom::UH_static);
2391  vertex_data->reserve_num_rows(vertex_pool->size());
2392 
2393  vertex_data->set_transform_blend_table(blend_table);
2394  if (slider_table != (SliderTable *)NULL) {
2395  vertex_data->set_slider_table(SliderTable::register_table(slider_table));
2396  }
2397 
2398  // And fill in the data from the vertex pool.
2400  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
2401  GeomVertexWriter gvw(vertex_data);
2402  EggVertex *vertex = (*vi);
2403  gvw.set_row(vertex->get_index());
2404 
2405  gvw.set_column(InternalName::get_vertex());
2406  gvw.add_data4d(vertex->get_pos4() * transform);
2407 
2408  if (is_dynamic) {
2409  EggMorphVertexList::const_iterator mvi;
2410  for (mvi = vertex->_dxyzs.begin(); mvi != vertex->_dxyzs.end(); ++mvi) {
2411  const EggMorphVertex &morph = (*mvi);
2412  CPT(InternalName) delta_name =
2413  InternalName::get_morph(InternalName::get_vertex(), morph.get_name());
2414  gvw.set_column(delta_name);
2415  gvw.add_data3d(morph.get_offset() * transform);
2416  }
2417  }
2418 
2419  if (vertex->has_normal()) {
2420  gvw.set_column(InternalName::get_normal());
2421  LNormald orig_normal = vertex->get_normal();
2422  LNormald transformed_normal = normalize(orig_normal * transform);
2423  gvw.add_data3d(transformed_normal);
2424 
2425  if (is_dynamic) {
2426  EggMorphNormalList::const_iterator mni;
2427  for (mni = vertex->_dnormals.begin(); mni != vertex->_dnormals.end(); ++mni) {
2428  const EggMorphNormal &morph = (*mni);
2429  CPT(InternalName) delta_name =
2430  InternalName::get_morph(InternalName::get_normal(), morph.get_name());
2431  gvw.set_column(delta_name);
2432  LNormald morphed_normal = orig_normal + morph.get_offset();
2433  LNormald transformed_morphed_normal = normalize(morphed_normal * transform);
2434  LVector3d delta = transformed_morphed_normal - transformed_normal;
2435  gvw.add_data3d(delta);
2436  }
2437  }
2438  }
2439 
2440  if (!ignore_color && vertex->has_color()) {
2441  gvw.set_column(InternalName::get_color());
2442  gvw.add_data4(vertex->get_color());
2443 
2444  if (is_dynamic) {
2445  EggMorphColorList::const_iterator mci;
2446  for (mci = vertex->_drgbas.begin(); mci != vertex->_drgbas.end(); ++mci) {
2447  const EggMorphColor &morph = (*mci);
2448  CPT(InternalName) delta_name =
2449  InternalName::get_morph(InternalName::get_color(), morph.get_name());
2450  gvw.set_column(delta_name);
2451  gvw.add_data4(morph.get_offset());
2452  }
2453  }
2454  }
2455 
2456  EggVertex::const_uv_iterator uvi;
2457  for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
2458  EggVertexUV *egg_uv = (*uvi);
2459  LTexCoord3d orig_uvw = egg_uv->get_uvw();
2460  LTexCoord3d uvw = egg_uv->get_uvw();
2461 
2462  string name = egg_uv->get_name();
2463  PT(InternalName) iname = InternalName::get_texcoord_name(name);
2464  gvw.set_column(iname);
2465 
2466  BakeInUVs::const_iterator buv = render_state->_bake_in_uvs.find(iname);
2467  if (buv != render_state->_bake_in_uvs.end()) {
2468  // If we are to bake in a texture matrix, do so now.
2469  uvw = uvw * (*buv).second->get_transform3d();
2470  }
2471 
2472  gvw.set_data3d(uvw);
2473 
2474  if (is_dynamic) {
2475  EggMorphTexCoordList::const_iterator mti;
2476  for (mti = egg_uv->_duvs.begin(); mti != egg_uv->_duvs.end(); ++mti) {
2477  const EggMorphTexCoord &morph = (*mti);
2478  CPT(InternalName) delta_name =
2479  InternalName::get_morph(iname, morph.get_name());
2480  gvw.set_column(delta_name);
2481  LTexCoord3d duvw = morph.get_offset();
2482  if (buv != render_state->_bake_in_uvs.end()) {
2483  LTexCoord3d new_uvw = orig_uvw + duvw;
2484  duvw = (new_uvw * (*buv).second->get_transform3d()) - uvw;
2485  }
2486 
2487  gvw.add_data3d(duvw);
2488  }
2489  }
2490 
2491  // Also add the tangent and binormal, if present.
2492  if (egg_uv->has_tangent() && egg_uv->has_binormal()) {
2493  PT(InternalName) iname = InternalName::get_tangent_name(name);
2494  gvw.set_column(iname);
2495  if (gvw.has_column()) {
2496  LVector3d tangent = egg_uv->get_tangent();
2497  LVector3d binormal = egg_uv->get_binormal();
2498  gvw.add_data3d(tangent);
2499  gvw.set_column(InternalName::get_binormal_name(name));
2500  gvw.add_data3d(binormal);
2501  }
2502  }
2503  }
2504 
2506  for (auxi = vertex->aux_begin(); auxi != vertex->aux_end(); ++auxi) {
2507  EggVertexAux *egg_aux = (*auxi);
2508  LVecBase4d aux = egg_aux->get_aux();
2509 
2510  string name = egg_aux->get_name();
2511  PT(InternalName) iname = InternalName::make(name);
2512  gvw.set_column(iname);
2513 
2514  gvw.set_data4d(aux);
2515  }
2516 
2517  if (is_dynamic) {
2518  int table_index = vertex->get_external_index();
2519  gvw.set_column(InternalName::get_transform_blend());
2520  gvw.set_data1i(table_index);
2521  }
2522  }
2523 
2524  bool inserted = _vertex_pool_data.insert
2525  (VertexPoolData::value_type(vpt, vertex_data)).second;
2526  nassertr(inserted, vertex_data);
2527 
2529  return vertex_data;
2530 }
2531 
2532 ////////////////////////////////////////////////////////////////////
2533 // Function: EggLoader::make_blend_table
2534 // Access: Private
2535 // Description:
2536 ////////////////////////////////////////////////////////////////////
2538 make_blend_table(EggVertexPool *vertex_pool, EggNode *primitive_home,
2539  CharacterMaker *character_maker) {
2540  PT(TransformBlendTable) blend_table;
2541  blend_table = new TransformBlendTable;
2542  blend_table->set_rows(SparseArray::lower_on(vertex_pool->size()));
2543 
2544  EggVertexPool::const_iterator vi;
2545  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
2546  EggVertex *vertex = (*vi);
2547 
2548  // Figure out the transforms affecting this particular vertex.
2549  TransformBlend blend;
2550  if (vertex->gref_size() == 0) {
2551  // If the vertex has no explicit membership, it belongs right
2552  // where it is.
2553  PT(VertexTransform) vt = character_maker->egg_to_transform(primitive_home);
2554  nassertr(vt != (VertexTransform *)NULL, NULL);
2555  blend.add_transform(vt, 1.0f);
2556  } else {
2557  // If the vertex does have an explicit membership, ignore its
2558  // parentage and assign it where it wants to be.
2559  double quantize = egg_vertex_membership_quantize;
2560  EggVertex::GroupRef::const_iterator gri;
2561  for (gri = vertex->gref_begin(); gri != vertex->gref_end(); ++gri) {
2562  EggGroup *egg_joint = (*gri);
2563  double membership = egg_joint->get_vertex_membership(vertex);
2564  if (quantize != 0.0) {
2565  membership = cfloor(membership / quantize + 0.5) * quantize;
2566  }
2567 
2568  PT(VertexTransform) vt = character_maker->egg_to_transform(egg_joint);
2569  nassertr(vt != (VertexTransform *)NULL, NULL);
2570  blend.add_transform(vt, membership);
2571  }
2572  }
2573  if (egg_vertex_max_num_joints >= 0) {
2574  blend.limit_transforms(egg_vertex_max_num_joints);
2575  }
2576  blend.normalize_weights();
2577 
2578  int table_index = blend_table->add_blend(blend);
2579 
2580  // We take advantage of the "external index" field of the
2581  // EggVertex to temporarily store the transform blend index.
2582  vertex->set_external_index(table_index);
2583  }
2584 
2585  return blend_table;
2586 }
2587 
2588 ////////////////////////////////////////////////////////////////////
2589 // Function: EggLoader::record_morph
2590 // Access: Private
2591 // Description:
2592 ////////////////////////////////////////////////////////////////////
2593 void EggLoader::
2594 record_morph(GeomVertexArrayFormat *array_format,
2595  CharacterMaker *character_maker,
2596  const string &morph_name, InternalName *column_name,
2597  int num_components) {
2598  PT(InternalName) delta_name =
2599  InternalName::get_morph(column_name, morph_name);
2600  if (!array_format->has_column(delta_name)) {
2601  array_format->add_column
2602  (delta_name, num_components,
2603  Geom::NT_stdfloat, Geom::C_morph_delta);
2604  }
2605 }
2606 
2607 ////////////////////////////////////////////////////////////////////
2608 // Function: EggLoader::make_primitive
2609 // Access: Private
2610 // Description: Creates a GeomPrimitive corresponding to the
2611 // indicated EggPrimitive, and adds it to the set.
2612 ////////////////////////////////////////////////////////////////////
2613 void EggLoader::
2614 make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim,
2615  EggLoader::UniquePrimitives &unique_primitives,
2616  EggLoader::Primitives &primitives,
2617  bool has_overall_color, const LColor &overall_color) {
2618  PT(GeomPrimitive) primitive;
2619  if (egg_prim->is_of_type(EggPolygon::get_class_type())) {
2620  if (egg_prim->size() == 3) {
2621  primitive = new GeomTriangles(Geom::UH_static);
2622  }
2623 
2624  } else if (egg_prim->is_of_type(EggTriangleStrip::get_class_type())) {
2625  primitive = new GeomTristrips(Geom::UH_static);
2626 
2627  } else if (egg_prim->is_of_type(EggTriangleFan::get_class_type())) {
2628  primitive = new GeomTrifans(Geom::UH_static);
2629 
2630  } else if (egg_prim->is_of_type(EggLine::get_class_type())) {
2631  if (egg_prim->size() == 2) {
2632  primitive = new GeomLines(Geom::UH_static);
2633  } else {
2634  primitive = new GeomLinestrips(Geom::UH_static);
2635  }
2636 
2637  } else if (egg_prim->is_of_type(EggPoint::get_class_type())) {
2638  primitive = new GeomPoints(Geom::UH_static);
2639 
2640  } else if (egg_prim->is_of_type(EggPatch::get_class_type())) {
2641  int num_vertices = egg_prim->size();
2642  primitive = new GeomPatches(num_vertices, Geom::UH_static);
2643  }
2644 
2645  if (primitive == (GeomPrimitive *)NULL) {
2646  // Don't know how to make this kind of primitive.
2647  egg2pg_cat.warning()
2648  << "Ignoring " << egg_prim->get_type() << "\n";
2649  return;
2650  }
2651 
2652  if (render_state->_flat_shaded) {
2653  primitive->set_shade_model(GeomPrimitive::SM_flat_first_vertex);
2654 
2655  } else if (egg_prim->get_shading() == EggPrimitive::S_overall) {
2656  primitive->set_shade_model(GeomPrimitive::SM_uniform);
2657 
2658  } else {
2659  primitive->set_shade_model(GeomPrimitive::SM_smooth);
2660  }
2661 
2662  // Insert the primitive into the set, but if we already have a
2663  // primitive of that type, reset the pointer to that one instead.
2664  PrimitiveUnifier pu(primitive);
2665  pair<UniquePrimitives::iterator, bool> result =
2666  unique_primitives.insert(UniquePrimitives::value_type(pu, primitive));
2667 
2668  if (result.second) {
2669  // This was the first primitive of this type. Store it.
2670  primitives.push_back(primitive);
2671 
2672  if (egg2pg_cat.is_debug()) {
2673  egg2pg_cat.debug()
2674  << "First primitive of type " << primitive->get_type()
2675  << ": " << primitive << "\n";
2676  }
2677  }
2678 
2679  GeomPrimitive *orig_prim = (*result.first).second;
2680 
2681  // Make sure we don't try to put more than egg_max_indices into any
2682  // one GeomPrimitive.
2683  if (orig_prim->get_num_vertices() + egg_prim->size() <= (unsigned int)egg_max_indices) {
2684  primitive = orig_prim;
2685 
2686  } else if (orig_prim != primitive) {
2687  // If the old primitive is full, keep the new primitive from now
2688  // on.
2689  (*result.first).second = primitive;
2690 
2691  if (egg2pg_cat.is_debug()) {
2692  egg2pg_cat.debug()
2693  << "Next primitive of type " << primitive->get_type()
2694  << ": " << primitive << "\n";
2695  }
2696  primitives.push_back(primitive);
2697  }
2698 
2699  // Now add the vertices.
2700  EggPrimitive::const_iterator vi;
2701  for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
2702  primitive->add_vertex((*vi)->get_index());
2703  }
2704  primitive->close_primitive();
2705 }
2706 
2707 ////////////////////////////////////////////////////////////////////
2708 // Function: EggLoader::set_portal_polygon
2709 // Access: Private
2710 // Description: Defines the PortalNode from the first polygon found
2711 // within this group.
2712 ////////////////////////////////////////////////////////////////////
2713 void EggLoader::
2714 set_portal_polygon(EggGroup *egg_group, PortalNode *pnode) {
2715  pnode->clear_vertices();
2716 
2717  PT(EggPolygon) poly = find_first_polygon(egg_group);
2718  if (poly != (EggPolygon *)NULL) {
2719  LMatrix4d mat = poly->get_vertex_to_node();
2720 
2721  EggPolygon::const_iterator vi;
2722  for (vi = poly->begin(); vi != poly->end(); ++vi) {
2723  LVertexd vert = (*vi)->get_pos3() * mat;
2724  pnode->add_vertex(LCAST(PN_stdfloat, vert));
2725  }
2726  }
2727 }
2728 
2729 ////////////////////////////////////////////////////////////////////
2730 // Function: EggLoader::set_occluder_polygon
2731 // Access: Private
2732 // Description: Defines the OccluderNode from the first polygon found
2733 // within this group.
2734 ////////////////////////////////////////////////////////////////////
2735 void EggLoader::
2736 set_occluder_polygon(EggGroup *egg_group, OccluderNode *pnode) {
2737  PT(EggPolygon) poly = find_first_polygon(egg_group);
2738  if (poly != (EggPolygon *)NULL) {
2739  if (poly->size() != 4) {
2740  egg2pg_cat.error()
2741  << "Invalid number of vertices for " << egg_group->get_name() << "\n";
2742  } else {
2743  LMatrix4d mat = poly->get_vertex_to_node();
2744 
2745  EggPolygon::const_iterator vi;
2746  LPoint3d v0 = (*poly)[0]->get_pos3() * mat;
2747  LPoint3d v1 = (*poly)[1]->get_pos3() * mat;
2748  LPoint3d v2 = (*poly)[2]->get_pos3() * mat;
2749  LPoint3d v3 = (*poly)[3]->get_pos3() * mat;
2750  pnode->set_vertices(LCAST(PN_stdfloat, v0),
2751  LCAST(PN_stdfloat, v1),
2752  LCAST(PN_stdfloat, v2),
2753  LCAST(PN_stdfloat, v3));
2754 
2755  if (poly->get_bface_flag()) {
2756  pnode->set_double_sided(true);
2757  }
2758  }
2759  }
2760 }
2761 
2762 ////////////////////////////////////////////////////////////////////
2763 // Function: EggLoader::find_first_polygon
2764 // Access: Private
2765 // Description: Returns the first EggPolygon found at or below the
2766 // indicated node.
2767 ////////////////////////////////////////////////////////////////////
2768 PT(EggPolygon) EggLoader::
2769 find_first_polygon(EggGroup *egg_group) {
2770  // Does this group have any polygons?
2771  EggGroup::const_iterator ci;
2772  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2773  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
2774  // Yes! Return the polygon.
2775  return DCAST(EggPolygon, (*ci));
2776  }
2777  }
2778 
2779  // Well, the group had no polygons; look for a child group that
2780  // does.
2781  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2782  if ((*ci)->is_of_type(EggGroup::get_class_type())) {
2783  EggGroup *child_group = DCAST(EggGroup, *ci);
2784  PT(EggPolygon) found = find_first_polygon(child_group);
2785  if (found != (EggPolygon *)NULL) {
2786  return found;
2787  }
2788  }
2789  }
2790 
2791  // We got nothing.
2792  return NULL;
2793 }
2794 
2795 ////////////////////////////////////////////////////////////////////
2796 // Function: EggLoader::make_sphere
2797 // Access: Private
2798 // Description: Creates a single generic Sphere corresponding
2799 // to the polygons associated with this group.
2800 // This sphere is used by make_collision_sphere and
2801 // Polylight sphere. It could be used for other spheres.
2802 ////////////////////////////////////////////////////////////////////
2803 bool EggLoader::
2804 make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags,
2805  LPoint3 &center, PN_stdfloat &radius, LColor &color) {
2806  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2807  if (geom_group != (EggGroup *)NULL) {
2808  // Collect all of the vertices.
2809  pset<EggVertex *> vertices;
2810 
2811  EggGroup::const_iterator ci;
2812  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2813  if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
2814  EggPrimitive *prim = DCAST(EggPrimitive, *ci);
2815  EggPrimitive::const_iterator pi;
2816  for (pi = prim->begin(); pi != prim->end(); ++pi) {
2817  vertices.insert(*pi);
2818  }
2819  }
2820  }
2821 
2822  // Now average together all of the vertices to get a center.
2823  int num_vertices = 0;
2824  LPoint3d d_center(0.0, 0.0, 0.0);
2826 
2827  for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
2828  EggVertex *vtx = (*vi);
2829  d_center += vtx->get_pos3();
2830  num_vertices++;
2831  }
2832 
2833  if (num_vertices > 0) {
2834  d_center /= (double)num_vertices;
2835  //egg2pg_cat.debug() << "make_sphere d_center: " << d_center << "\n";
2836 
2837  // And the furthest vertex determines the radius.
2838  double radius2 = 0.0;
2839  for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
2840  EggVertex *vtx = (*vi);
2841  LPoint3d p3 = vtx->get_pos3();
2842  LVector3d v = p3 - d_center;
2843  radius2 = max(radius2, v.length_squared());
2844  }
2845 
2846  center = LCAST(PN_stdfloat, d_center);
2847  radius = sqrtf(radius2);
2848 
2849  //egg2pg_cat.debug() << "make_sphere radius: " << radius << "\n";
2850  vi = vertices.begin();
2851  EggVertex *clr_vtx = (*vi);
2852  color = clr_vtx->get_color();
2853  return true;
2854  }
2855  }
2856  return false;
2857 }
2858 
2859 ////////////////////////////////////////////////////////////////////
2860 // Function: EggLoader::make_box
2861 // Access: Private
2862 // Description: Creates a single generic Box corresponding
2863 // to the polygons associated with this group.
2864 // This box is used by make_collision_box.
2865 ////////////////////////////////////////////////////////////////////
2866 bool EggLoader::
2867 make_box(EggGroup *egg_group, EggGroup::CollideFlags flags,
2868  LPoint3 &min_p, LPoint3 &max_p, LColor &color) {
2869  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2870  if (geom_group != (EggGroup *)NULL) {
2871  // Collect all of the vertices.
2872  pset<EggVertex *> vertices;
2873 
2874  EggGroup::const_iterator ci;
2875  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2876  if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
2877  EggPrimitive *prim = DCAST(EggPrimitive, *ci);
2878  EggPrimitive::const_iterator pi;
2879  for (pi = prim->begin(); pi != prim->end(); ++pi) {
2880  vertices.insert(*pi);
2881  }
2882  }
2883  }
2884 
2885  // Now find the min/max points
2887  vi = vertices.begin();
2888 
2889  if (vi == vertices.end()) {
2890  // No vertices, no bounding box.
2891  min_p.set(0, 0, 0);
2892  max_p.set(0, 0, 0);
2893  return false;
2894  }
2895 
2896  EggVertex *vertex = (*vi);
2897  LVertexd min_pd = vertex->get_pos3();
2898  LVertexd max_pd = min_pd;
2899  color = vertex->get_color();
2900 
2901  for (++vi; vi != vertices.end(); ++vi) {
2902  vertex = (*vi);
2903  const LVertexd &pos = vertex->get_pos3();
2904  min_pd.set(min(min_pd[0], pos[0]),
2905  min(min_pd[1], pos[1]),
2906  min(min_pd[2], pos[2]));
2907  max_pd.set(max(max_pd[0], pos[0]),
2908  max(max_pd[1], pos[1]),
2909  max(max_pd[2], pos[2]));
2910  }
2911 
2912  min_p = LCAST(PN_stdfloat, min_pd);
2913  max_p = LCAST(PN_stdfloat, max_pd);
2914  return (min_pd != max_pd);
2915  }
2916  return false;
2917 }
2918 
2919 ////////////////////////////////////////////////////////////////////
2920 // Function: EggLoader::make_collision_solids
2921 // Access: Private
2922 // Description: Creates CollisionSolids corresponding to the
2923 // collision geometry indicated at the given node and
2924 // below.
2925 ////////////////////////////////////////////////////////////////////
2926 void EggLoader::
2927 make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
2928  CollisionNode *cnode) {
2929  if (egg_group->get_cs_type() != EggGroup::CST_none) {
2930  start_group = egg_group;
2931  }
2932 
2933  switch (start_group->get_cs_type()) {
2934  case EggGroup::CST_none:
2935  // No collision flags; do nothing. Don't even traverse further.
2936  return;
2937 
2938  case EggGroup::CST_plane:
2939  make_collision_plane(egg_group, cnode, start_group->get_collide_flags());
2940  break;
2941 
2942  case EggGroup::CST_polygon:
2943  make_collision_polygon(egg_group, cnode, start_group->get_collide_flags());
2944  break;
2945 
2946  case EggGroup::CST_polyset:
2947  make_collision_polyset(egg_group, cnode, start_group->get_collide_flags());
2948  break;
2949 
2950  case EggGroup::CST_sphere:
2951  make_collision_sphere(egg_group, cnode, start_group->get_collide_flags());
2952  break;
2953 
2954  case EggGroup::CST_box:
2955  make_collision_box(egg_group, cnode, start_group->get_collide_flags());
2956  break;
2957 
2958  case EggGroup::CST_inv_sphere:
2959  make_collision_inv_sphere(egg_group, cnode, start_group->get_collide_flags());
2960  break;
2961 
2962  case EggGroup::CST_tube:
2963  make_collision_tube(egg_group, cnode, start_group->get_collide_flags());
2964  break;
2965 
2966  case EggGroup::CST_floor_mesh:
2967  make_collision_floor_mesh(egg_group, cnode, start_group->get_collide_flags());
2968  break;
2969  }
2970 
2971  if ((start_group->get_collide_flags() & EggGroup::CF_descend) != 0) {
2972  // Now pick up everything below.
2973  EggGroup::const_iterator ci;
2974  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2975  if ((*ci)->is_of_type(EggGroup::get_class_type())) {
2976  make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode);
2977  }
2978  }
2979  } else {
2980  egg2pg_cat.warning()
2981  << "Using <Collide> without 'descend' is deprecated. 'descend' "
2982  << "will become the default in a future version of Panda3D.\n";
2983  }
2984 }
2985 
2986 ////////////////////////////////////////////////////////////////////
2987 // Function: EggLoader::make_collision_plane
2988 // Access: Private
2989 // Description: Creates a single CollisionPlane corresponding
2990 // to the first polygon associated with this group.
2991 ////////////////////////////////////////////////////////////////////
2992 void EggLoader::
2993 make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
2994  EggGroup::CollideFlags flags) {
2995  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2996  if (geom_group != (EggGroup *)NULL) {
2997  EggGroup::const_iterator ci;
2998  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2999  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
3000  CollisionPlane *csplane =
3001  create_collision_plane(DCAST(EggPolygon, *ci), egg_group);
3002  if (csplane != (CollisionPlane *)NULL) {
3003  apply_collision_flags(csplane, flags);
3004  cnode->add_solid(csplane);
3005  return;
3006  }
3007  } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
3008  EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
3009  PT(EggGroup) temp_group = new EggGroup;
3010  if (comp->triangulate_into(temp_group)) {
3011  make_collision_plane(temp_group, cnode, flags);
3012  return;
3013  }
3014  }
3015  }
3016  }
3017 }
3018 
3019 
3020 
3021 ////////////////////////////////////////////////////////////////////
3022 // Function: EggLoader::make_collision_floor_mesh
3023 // Access: Private
3024 // Description: Creates a single CollisionPolygon corresponding
3025 // to the first polygon associated with this group.
3026 ////////////////////////////////////////////////////////////////////
3027 void EggLoader::
3028 make_collision_floor_mesh(EggGroup *egg_group, CollisionNode *cnode,
3029  EggGroup::CollideFlags flags) {
3030 
3031  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
3032 
3033 
3034  if (geom_group != (EggGroup *)NULL) {
3035  create_collision_floor_mesh(cnode, geom_group,flags);
3036  }
3037 }
3038 
3039 ////////////////////////////////////////////////////////////////////
3040 // Function: EggLoader::make_collision_polygon
3041 // Access: Private
3042 // Description: Creates a single CollisionPolygon corresponding
3043 // to the first polygon associated with this group.
3044 ////////////////////////////////////////////////////////////////////
3045 void EggLoader::
3046 make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
3047  EggGroup::CollideFlags flags) {
3048 
3049  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
3050  if (geom_group != (EggGroup *)NULL) {
3051  EggGroup::const_iterator ci;
3052  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
3053  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
3054  create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
3055  egg_group, flags);
3056  } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
3057  EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
3058  PT(EggGroup) temp_group = new EggGroup;
3059  if (comp->triangulate_into(temp_group)) {
3060  make_collision_polygon(temp_group, cnode, flags);
3061  return;
3062  }
3063  }
3064  }
3065  }
3066 }
3067 
3068 
3069 ////////////////////////////////////////////////////////////////////
3070 // Function: EggLoader::make_collision_polyset
3071 // Access: Private
3072 // Description: Creates a series of CollisionPolygons corresponding
3073 // to the polygons associated with this group.
3074 ////////////////////////////////////////////////////////////////////
3075 void EggLoader::
3076 make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
3077  EggGroup::CollideFlags flags) {
3078  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
3079  if (geom_group != (EggGroup *)NULL) {
3080  EggGroup::const_iterator ci;
3081  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
3082  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
3083  create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
3084  egg_group, flags);
3085  } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
3086  EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
3087  PT(EggGroup) temp_group = new EggGroup;
3088  if (comp->triangulate_into(temp_group)) {
3089  make_collision_polyset(temp_group, cnode, flags);
3090  }
3091  }
3092  }
3093  }
3094 }
3095 
3096 ////////////////////////////////////////////////////////////////////
3097 // Function: EggLoader::make_collision_sphere
3098 // Access: Private
3099 // Description: Creates a single CollisionSphere corresponding
3100 // to the polygons associated with this group.
3101 ////////////////////////////////////////////////////////////////////
3102 void EggLoader::
3103 make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
3104  EggGroup::CollideFlags flags) {
3105  LPoint3 center;
3106  PN_stdfloat radius;
3107  LColor dummycolor;
3108  if (make_sphere(egg_group, flags, center, radius, dummycolor)) {
3109  CollisionSphere *cssphere =
3110  new CollisionSphere(center, radius);
3111  apply_collision_flags(cssphere, flags);
3112  cnode->add_solid(cssphere);
3113  }
3114 }
3115 
3116 ////////////////////////////////////////////////////////////////////
3117 // Function: EggLoader::make_collision_box
3118 // Access: Private
3119 // Description: Creates a single CollisionBox corresponding
3120 // to the polygons associated with this group.
3121 ////////////////////////////////////////////////////////////////////
3122 void EggLoader::
3123 make_collision_box(EggGroup *egg_group, CollisionNode *cnode,
3124  EggGroup::CollideFlags flags) {
3125  LPoint3 min_p;
3126  LPoint3 max_p;
3127  LColor dummycolor;
3128  if (make_box(egg_group, flags, min_p, max_p, dummycolor)) {
3129  CollisionBox *csbox =
3130  new CollisionBox(min_p, max_p);
3131  apply_collision_flags(csbox, flags);
3132  cnode->add_solid(csbox);
3133  }
3134 }
3135 
3136 ////////////////////////////////////////////////////////////////////
3137 // Function: EggLoader::make_collision_inv_sphere
3138 // Access: Private
3139 // Description: Creates a single CollisionInvSphere corresponding
3140 // to the polygons associated with this group.
3141 ////////////////////////////////////////////////////////////////////
3142 void EggLoader::
3143 make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode,
3144  EggGroup::CollideFlags flags) {
3145  LPoint3 center;
3146  PN_stdfloat radius;
3147  LColor dummycolor;
3148  if (make_sphere(egg_group, flags, center, radius, dummycolor)) {
3149  CollisionInvSphere *cssphere =
3150  new CollisionInvSphere(center, radius);
3151  apply_collision_flags(cssphere, flags);
3152  cnode->add_solid(cssphere);
3153  }
3154 }
3155 
3156 ////////////////////////////////////////////////////////////////////
3157 // Function: EggLoader::make_collision_tube
3158 // Access: Private
3159 // Description: Creates a single CollisionTube corresponding
3160 // to the polygons associated with this group.
3161 ////////////////////////////////////////////////////////////////////
3162 void EggLoader::
3163 make_collision_tube(EggGroup *egg_group, CollisionNode *cnode,
3164  EggGroup::CollideFlags flags) {
3165  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
3166  if (geom_group != (EggGroup *)NULL) {
3167  // Collect all of the vertices.
3168  pset<EggVertex *> vertices;
3169 
3170  EggGroup::const_iterator ci;
3171  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
3172  if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
3173  EggPrimitive *prim = DCAST(EggPrimitive, *ci);
3174  EggPrimitive::const_iterator pi;
3175  for (pi = prim->begin(); pi != prim->end(); ++pi) {
3176  vertices.insert(*pi);
3177  }
3178  }
3179  }
3180 
3181  // Now store the 3-d values in a vector for convenient access (and
3182  // also determine the centroid). We compute this in node space.
3183  size_t num_vertices = vertices.size();
3184  if (num_vertices != 0) {
3185  pvector<LPoint3d> vpos;
3186  vpos.reserve(num_vertices);
3187 
3188  LPoint3d center(0.0, 0.0, 0.0);
3190  for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
3191  EggVertex *vtx = (*vi);
3192  const LPoint3d &pos = vtx->get_pos3();
3193  vpos.push_back(pos);
3194  center += pos;
3195  }
3196  center /= (double)num_vertices;
3197 
3198  // Now that we have the centroid, we have to try to figure out
3199  // the cylinder's major axis. Start by finding a point farthest
3200  // from the centroid.
3201  size_t i;
3202  double radius2 = 0.0;
3203  LPoint3d far_a = center;
3204  for (i = 0; i < num_vertices; i++) {
3205  double dist2 = (vpos[i] - center).length_squared();
3206  if (dist2 > radius2) {
3207  radius2 = dist2;
3208  far_a = vpos[i];
3209  }
3210  }
3211 
3212  // The point we have found above, far_a, must be one one of the
3213  // endcaps. Now find another point, far_b, that is the farthest
3214  // from far_a. This will be a point on the other endcap.
3215  radius2 = 0.0;
3216  LPoint3d far_b = center;
3217  for (i = 0; i < num_vertices; i++) {
3218  double dist2 = (vpos[i] - far_a).length_squared();
3219  if (dist2 > radius2) {
3220  radius2 = dist2;
3221  far_b = vpos[i];
3222  }
3223  }
3224 
3225  // Now we have far_a and far_b, one point on each endcap.
3226  // However, these points are not necessarily centered on the
3227  // endcaps, so we haven't figured out the cylinder's axis yet
3228  // (the line between far_a and far_b will probably pass through
3229  // the cylinder at an angle).
3230 
3231  // So we still need to determine the full set of points in each
3232  // endcap. To do this, we pass back through the set of points,
3233  // categorizing each point into either "endcap a" or "endcap b".
3234  // We also leave a hefty chunk of points in the middle
3235  // uncategorized; this helps prevent us from getting a little
3236  // bit lopsided with points near the middle that may appear to
3237  // be closer to the wrong endcap.
3238  LPoint3d cap_a_center(0.0, 0.0, 0.0);
3239  LPoint3d cap_b_center(0.0, 0.0, 0.0);
3240  int num_a = 0;
3241  int num_b = 0;
3242 
3243  // This is the threshold length; points farther away from the
3244  // center than this are deemed to be in one endcap or the other.
3245  double center_length = (far_a - far_b).length() / 4.0;
3246  double center_length2 = center_length * center_length;
3247 
3248  for (i = 0; i < num_vertices; i++) {
3249  double dist2 = (vpos[i] - center).length_squared();
3250  if (dist2 > center_length2) {
3251  // This point is farther away from the center than
3252  // center_length; therefore it belongs in an endcap.
3253  double dist_a2 = (vpos[i] - far_a).length_squared();
3254  double dist_b2 = (vpos[i] - far_b).length_squared();
3255  if (dist_a2 < dist_b2) {
3256  // It's in endcap a.
3257  cap_a_center += vpos[i];
3258  num_a++;
3259  } else {
3260  // It's in endcap b.
3261  cap_b_center += vpos[i];
3262  num_b++;
3263  }
3264  }
3265  }
3266 
3267  if (num_a > 0 && num_b > 0) {
3268  cap_a_center /= (double)num_a;
3269  cap_b_center /= (double)num_b;
3270 
3271 
3272  // Now we finally have the major axis of the cylinder.
3273  LVector3d axis = cap_b_center - cap_a_center;
3274  axis.normalize();
3275 
3276  // If the axis is *almost* parallel with a major axis, assume
3277  // it is meant to be exactly parallel.
3278  if (IS_THRESHOLD_ZERO(axis[0], 0.01)) {
3279  axis[0] = 0.0;
3280  }
3281  if (IS_THRESHOLD_ZERO(axis[1], 0.01)) {
3282  axis[1] = 0.0;
3283  }
3284  if (IS_THRESHOLD_ZERO(axis[2], 0.01)) {
3285  axis[2] = 0.0;
3286  }
3287  axis.normalize();
3288 
3289  // Transform all of the points so that the major axis is along
3290  // the Y axis, and the origin is the center. This is very
3291  // similar to the CollisionTube's idea of its canonical
3292  // orientation (although not exactly the same, since it is
3293  // centered on the origin instead of having point_a on the
3294  // origin). It makes it easier to determine the length and
3295  // radius of the cylinder.
3296  LMatrix4d mat;
3297  look_at(mat, axis, LVector3d(0.0, 0.0, 1.0), CS_zup_right);
3298  mat.set_row(3, center);
3299  LMatrix4d inv_mat;
3300  inv_mat.invert_from(mat);
3301 
3302  for (i = 0; i < num_vertices; i++) {
3303  vpos[i] = vpos[i] * inv_mat;
3304  }
3305 
3306  double max_radius2 = 0.0;
3307 
3308  // Now determine the radius.
3309  for (i = 0; i < num_vertices; i++) {
3310  LVector2d v(vpos[i][0], vpos[i][2]);
3311  double radius2 = v.length_squared();
3312  if (radius2 > max_radius2) {
3313  max_radius2 = radius2;
3314  }
3315  }
3316 
3317  // And with the radius, we can determine the length. We need
3318  // to know the radius first because we want the round endcaps
3319  // to enclose all points.
3320  double min_y = 0.0;
3321  double max_y = 0.0;
3322 
3323  for (i = 0; i < num_vertices; i++) {
3324  LVector2d v(vpos[i][0], vpos[i][2]);
3325  double radius2 = v.length_squared();
3326 
3327  if (vpos[i][1] < min_y) {
3328  // Adjust the Y pos to account for the point's distance
3329  // from the axis.
3330  double factor = sqrt(max_radius2 - radius2);
3331  min_y = min(min_y, vpos[i][1] + factor);
3332 
3333  } else if (vpos[i][1] > max_y) {
3334  double factor = sqrt(max_radius2 - radius2);
3335  max_y = max(max_y, vpos[i][1] - factor);
3336  }
3337  }
3338 
3339  double length = max_y - min_y;
3340  double radius = sqrt(max_radius2);
3341 
3342  // Finally, we have everything we need to define the cylinder.
3343  LVector3d half = axis * (length / 2.0);
3344  LPoint3d point_a = center - half;
3345  LPoint3d point_b = center + half;
3346 
3347  CollisionTube *cstube =
3348  new CollisionTube(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b),
3349  radius);
3350  apply_collision_flags(cstube, flags);
3351  cnode->add_solid(cstube);
3352  }
3353  }
3354  }
3355 }
3356 
3357 ////////////////////////////////////////////////////////////////////
3358 // Function: EggLoader::apply_collision_flags
3359 // Access: Private
3360 // Description: Does funny stuff to the CollisionSolid as
3361 // appropriate, based on the settings of the given
3362 // CollideFlags.
3363 ////////////////////////////////////////////////////////////////////
3364 void EggLoader::
3365 apply_collision_flags(CollisionSolid *solid, EggGroup::CollideFlags flags) {
3366  if ((flags & EggGroup::CF_intangible) != 0) {
3367  solid->set_tangible(false);
3368  }
3369  if ((flags & EggGroup::CF_level) != 0) {
3371  }
3372 }
3373 
3374 ////////////////////////////////////////////////////////////////////
3375 // Function: EggLoader::find_collision_geometry
3376 // Access: Private
3377 // Description: Looks for the node, at or below the indicated node,
3378 // that contains the associated collision geometry.
3379 ////////////////////////////////////////////////////////////////////
3380 EggGroup *EggLoader::
3381 find_collision_geometry(EggGroup *egg_group, EggGroup::CollideFlags flags) {
3382  if ((flags & EggGroup::CF_descend) != 0) {
3383  // If we have the "descend" instruction, we'll get to it when we
3384  // get to it. Don't worry about it now.
3385  return egg_group;
3386  }
3387 
3388  // Does this group have any polygons?
3389  EggGroup::const_iterator ci;
3390  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
3391  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
3392  // Yes! Use this group.
3393  return egg_group;
3394  }
3395  }
3396 
3397  // Well, the group had no polygons; look for a child group that has
3398  // the same collision type.
3399  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
3400  if ((*ci)->is_of_type(EggGroup::get_class_type())) {
3401  EggGroup *child_group = DCAST(EggGroup, *ci);
3402  if (child_group->get_cs_type() == egg_group->get_cs_type()) {
3403  return child_group;
3404  }
3405  }
3406  }
3407 
3408  // We got nothing.
3409  return NULL;
3410 }
3411 
3412 ////////////////////////////////////////////////////////////////////
3413 // Function: EggLoader::create_collision_plane
3414 // Access: Private
3415 // Description: Creates a single CollisionPlane from the indicated
3416 // EggPolygon.
3417 ////////////////////////////////////////////////////////////////////
3418 CollisionPlane *EggLoader::
3419 create_collision_plane(EggPolygon *egg_poly, EggGroup *parent_group) {
3420  if (!egg_poly->cleanup()) {
3421  egg2pg_cat.info()
3422  << "Ignoring degenerate collision plane in " << parent_group->get_name()
3423  << "\n";
3424  return NULL;
3425  }
3426 
3427  if (!egg_poly->is_planar()) {
3428  egg2pg_cat.warning()
3429  << "Non-planar polygon defining collision plane in "
3430  << parent_group->get_name()
3431  << "\n";
3432  }
3433 
3434  pvector<LVertex> vertices;
3435  if (!egg_poly->empty()) {
3436  EggPolygon::const_iterator vi;
3437  vi = egg_poly->begin();
3438 
3439  LVertexd vert = (*vi)->get_pos3();
3440  vertices.push_back(LCAST(PN_stdfloat, vert));
3441 
3442  LVertexd last_vert = vert;
3443  ++vi;
3444  while (vi != egg_poly->end()) {
3445  vert = (*vi)->get_pos3();
3446  if (!vert.almost_equal(last_vert)) {
3447  vertices.push_back(LCAST(PN_stdfloat, vert));
3448  }
3449 
3450  last_vert = vert;
3451  ++vi;
3452  }
3453  }
3454 
3455  if (vertices.size() < 3) {
3456  return NULL;
3457  }
3458  LPlane plane(vertices[0], vertices[1], vertices[2]);
3459  return new CollisionPlane(plane);
3460 }
3461 
3462 ////////////////////////////////////////////////////////////////////
3463 // Function: EggLoader::create_collision_polygons
3464 // Access: Private
3465 // Description: Creates one or more CollisionPolygons from the
3466 // indicated EggPolygon, and adds them to the indicated
3467 // CollisionNode.
3468 ////////////////////////////////////////////////////////////////////
3469 void EggLoader::
3470 create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
3471  EggGroup *parent_group,
3472  EggGroup::CollideFlags flags) {
3473 
3474  PT(EggGroup) group = new EggGroup;
3475 
3476  if (!egg_poly->triangulate_into(group, false)) {
3477  egg2pg_cat.info()
3478  << "Ignoring degenerate collision polygon in "
3479  << parent_group->get_name()
3480  << "\n";
3481  return;
3482  }
3483 
3484  if (group->size() != 1) {
3485  egg2pg_cat.info()
3486  << "Triangulating concave or non-planar collision polygon in "
3487  << parent_group->get_name()
3488  << "\n";
3489  }
3490 
3491  EggGroup::iterator ci;
3492  for (ci = group->begin(); ci != group->end(); ++ci) {
3493  EggPolygon *poly = DCAST(EggPolygon, *ci);
3494 
3495  pvector<LVertex> vertices;
3496  if (!poly->empty()) {
3497  EggPolygon::const_iterator vi;
3498  vi = poly->begin();
3499 
3500  LVertexd vert = (*vi)->get_pos3();
3501  vertices.push_back(LCAST(PN_stdfloat, vert));
3502 
3503  LVertexd last_vert = vert;
3504  ++vi;
3505  while (vi != poly->end()) {
3506  vert = (*vi)->get_pos3();
3507  if (!vert.almost_equal(last_vert)) {
3508  vertices.push_back(LCAST(PN_stdfloat, vert));
3509  }
3510 
3511  last_vert = vert;
3512  ++vi;
3513  }
3514  }
3515 
3516  if (vertices.size() >= 3) {
3517  const LVertex *vertices_begin = &vertices[0];
3518  const LVertex *vertices_end = vertices_begin + vertices.size();
3519  PT(CollisionPolygon) cspoly =
3520  new CollisionPolygon(vertices_begin, vertices_end);
3521  if (cspoly->is_valid()) {
3522  apply_collision_flags(cspoly, flags);
3523  cnode->add_solid(cspoly);
3524  }
3525  }
3526  }
3527 }
3528 
3529 ////////////////////////////////////////////////////////////////////
3530 // Function: EggLoader::create_collision_floor_mesh
3531 // Access: Private
3532 // Description: Creates a CollisionFloorMesh from the
3533 // indicated EggPolygons, and adds it to the indicated
3534 // CollisionNode.
3535 ////////////////////////////////////////////////////////////////////
3536 void EggLoader::
3537 create_collision_floor_mesh(CollisionNode *cnode,
3538  EggGroup *parent_group,
3539  EggGroup::CollideFlags flags) {
3540 
3541  PT(EggGroup) group = new EggGroup;
3542  EggVertexPool pool("floorMesh");
3543  pool.local_object();
3544  EggGroup::const_iterator egi;
3545  for (egi = parent_group->begin(); egi != parent_group->end(); ++egi) {
3546  if ((*egi)->is_of_type(EggPolygon::get_class_type())) {
3547  EggPolygon * poly = DCAST(EggPolygon, *egi);
3548  if (!poly->triangulate_into(group, false)) {
3549  egg2pg_cat.info()
3550  << "Ignoring degenerate collision polygon in "
3551  << parent_group->get_name()
3552  << "\n";
3553  return;
3554  }
3555 
3556  }
3557  }
3558  if(group->size() == 0) {
3559  egg2pg_cat.info()
3560  << "empty collision solid\n";
3561  return;
3562  }
3564  pvector<CollisionFloorMesh::TriangleIndices> triangles;
3565 
3566  EggGroup::iterator ci;
3567  for (ci = group->begin(); ci != group->end(); ++ci) {
3568  EggPolygon *poly = DCAST(EggPolygon, *ci);
3569  if (poly->get_num_vertices() == 3) {
3571 
3572  //generate a shared vertex triangle from the vertex pool
3573  tri.p1=pool.create_unique_vertex(*poly->get_vertex(0))->get_index();
3574  tri.p2=pool.create_unique_vertex(*poly->get_vertex(1))->get_index();
3575  tri.p3=pool.create_unique_vertex(*poly->get_vertex(2))->get_index();
3576 
3577  triangles.push_back(tri);
3578  } else if (poly->get_num_vertices() == 4) {
3579  //this is a case that really shouldn't happen, but appears to be required
3580  //-split up the quad int 2 tris.
3583 
3584  //generate a shared vertex triangle from the vertex pool
3585  tri.p1=pool.create_unique_vertex(*poly->get_vertex(0))->get_index();
3586  tri.p2=pool.create_unique_vertex(*poly->get_vertex(1))->get_index();
3587  tri.p3=pool.create_unique_vertex(*poly->get_vertex(2))->get_index();
3588 
3589  triangles.push_back(tri);
3590 
3591  //generate a shared vertex triangle from the vertex pool
3592  tri2.p1=tri.p1;
3593  tri2.p2=tri.p3;
3594  tri2.p3=pool.create_unique_vertex(*poly->get_vertex(3))->get_index();
3595 
3596 
3597  triangles.push_back(tri2);
3598  }
3599  }
3600 
3601  //Now we have a set of triangles, and a pool
3602  PT(CollisionFloorMesh) csfloor = new CollisionFloorMesh;
3603 
3604 
3605  EggVertexPool::const_iterator vi;
3606  for (vi = pool.begin(); vi != pool.end(); vi++) {
3607  csfloor->add_vertex(LCAST(PN_stdfloat,(*vi)->get_pos3()));
3608  }
3609 
3611 
3612  for (ti = triangles.begin(); ti != triangles.end(); ti++) {
3613  CollisionFloorMesh::TriangleIndices triangle = *ti;
3614  csfloor->add_triangle(triangle.p1, triangle.p2, triangle.p3);
3615  }
3616  cnode->add_solid(csfloor);
3617 }
3618 
3619 
3620 ////////////////////////////////////////////////////////////////////
3621 // Function: EggLoader::apply_deferred_nodes
3622 // Access: Private
3623 // Description: Walks back over the tree and applies the
3624 // DeferredNodeProperties that were saved up along the
3625 // way.
3626 ////////////////////////////////////////////////////////////////////
3627 void EggLoader::
3628 apply_deferred_nodes(PandaNode *node, const DeferredNodeProperty &prop) {
3629  DeferredNodeProperty next_prop(prop);
3630 
3631  // Do we have a DeferredNodeProperty associated with this node?
3632  DeferredNodes::const_iterator dni;
3633  dni = _deferred_nodes.find(node);
3634 
3635  if (dni != _deferred_nodes.end()) {
3636  const DeferredNodeProperty &def = (*dni).second;
3637  next_prop.compose(def);
3638  }
3639 
3640  // Now apply the accumulated state to the node.
3641  next_prop.apply_to_node(node);
3642 
3643  int num_children = node->get_num_children();
3644  for (int i = 0; i < num_children; i++) {
3645  apply_deferred_nodes(node->get_child(i), next_prop);
3646  }
3647 }
3648 
3649 ////////////////////////////////////////////////////////////////////
3650 // Function: EggLoader::expand_all_object_types
3651 // Access: Private
3652 // Description: Walks the hierarchy and calls expand_object_types()
3653 // on each node, to expand all of the ObjectType
3654 // definitions in the file at once. Also prunes any
3655 // nodes that are flagged "backstage".
3656 //
3657 // The return value is true if this node should be kept,
3658 // false if it should be pruned.
3659 ////////////////////////////////////////////////////////////////////
3660 bool EggLoader::
3661 expand_all_object_types(EggNode *egg_node) {
3662  if (egg_node->is_of_type(EggGroup::get_class_type())) {
3663  EggGroup *egg_group = DCAST(EggGroup, egg_node);
3664 
3665  if (egg_group->get_num_object_types() != 0) {
3666  pset<string> expanded;
3667  pvector<string> expanded_history;
3668  if (!expand_object_types(egg_group, expanded, expanded_history)) {
3669  return false;
3670  }
3671  }
3672  }
3673 
3674  // Now recurse on children, and we might prune children from this
3675  // list as we go.
3676  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
3677  EggGroupNode *egg_group_node = DCAST(EggGroupNode, egg_node);
3678  EggGroupNode::const_iterator ci;
3679  ci = egg_group_node->begin();
3680  while (ci != egg_group_node->end()) {
3681  EggGroupNode::const_iterator cnext = ci;
3682  ++cnext;
3683 
3684  if (!expand_all_object_types(*ci)) {
3685  // Prune this child.
3686  egg_group_node->erase(ci);
3687  }
3688  ci = cnext;
3689  }
3690  }
3691 
3692  return true;
3693 }
3694 
3695 ////////////////////////////////////////////////////////////////////
3696 // Function: EggLoader::expand_object_types
3697 // Access: Private
3698 // Description: Recursively expands the group's ObjectType string(s).
3699 // It's recursive because an ObjectType string might
3700 // itself expand to another ObjectType string, which is
3701 // allowed; but we don't want to get caught in a cycle.
3702 //
3703 // The return value is true if the object type is
3704 // expanded and the node is valid, or false if the node
3705 // should be ignored (e.g. ObjectType "backstage").
3706 ////////////////////////////////////////////////////////////////////
3707 bool EggLoader::
3708 expand_object_types(EggGroup *egg_group, const pset<string> &expanded,
3709  const pvector<string> &expanded_history) {
3710  int num_object_types = egg_group->get_num_object_types();
3711 
3712  // First, copy out the object types so we can recursively modify the
3713  // list.
3714  vector_string object_types;
3715  int i;
3716  for (i = 0; i < num_object_types; i++) {
3717  object_types.push_back(egg_group->get_object_type(i));
3718  }
3719  egg_group->clear_object_types();
3720 
3721  for (i = 0; i < num_object_types; i++) {
3722  string object_type = object_types[i];
3723  pset<string> new_expanded(expanded);
3724 
3725  // Check for a cycle.
3726  if (!new_expanded.insert(object_type).second) {
3727  egg2pg_cat.error()
3728  << "Cycle in ObjectType expansions:\n";
3730  for (pi = expanded_history.begin();
3731  pi != expanded_history.end();
3732  ++pi) {
3733  egg2pg_cat.error(false)
3734  << (*pi) << " -> ";
3735  }
3736  egg2pg_cat.error(false) << object_type << "\n";
3737  _error = true;
3738 
3739  } else {
3740  // No cycle; continue.
3741  pvector<string> new_expanded_history(expanded_history);
3742  new_expanded_history.push_back(object_type);
3743 
3744  if (!do_expand_object_type(egg_group, new_expanded,
3745  new_expanded_history, object_type)) {
3746  // Ignorable group; stop here.
3747  return false;
3748  }
3749  }
3750  }
3751 
3752  return true;
3753 }
3754 
3755 ////////////////////////////////////////////////////////////////////
3756 // Function: EggLoader::do_expand_object_types
3757 // Access: Private
3758 // Description: Further implementation of expand_object_types().
3759 ////////////////////////////////////////////////////////////////////
3760 bool EggLoader::
3761 do_expand_object_type(EggGroup *egg_group, const pset<string> &expanded,
3762  const pvector<string> &expanded_history,
3763  const string &object_type) {
3764  // Try to find the egg syntax that the given objecttype is
3765  // shorthand for. First, look in the config file.
3766 
3767  ConfigVariableString egg_object_type
3768  ("egg-object-type-" + downcase(object_type), "");
3769  string egg_syntax = egg_object_type;
3770 
3771  if (!egg_object_type.has_value()) {
3772  // It wasn't defined in a config file. Maybe it's built in?
3773 
3774  if (cmp_nocase_uh(object_type, "barrier") == 0) {
3775  egg_syntax = "<Collide> { Polyset descend }";
3776 
3777  } else if (cmp_nocase_uh(object_type, "solidpoly") == 0) {
3778  egg_syntax = "<Collide> { Polyset descend solid }";
3779 
3780  } else if (cmp_nocase_uh(object_type, "turnstile") == 0) {
3781  egg_syntax = "<Collide> { Polyset descend turnstile }";
3782 
3783  } else if (cmp_nocase_uh(object_type, "sphere") == 0) {
3784  egg_syntax = "<Collide> { Sphere descend }";
3785 
3786  } else if (cmp_nocase_uh(object_type, "tube") == 0) {
3787  egg_syntax = "<Collide> { Tube descend }";
3788 
3789  } else if (cmp_nocase_uh(object_type, "trigger") == 0) {
3790  egg_syntax = "<Collide> { Polyset descend intangible }";
3791 
3792  } else if (cmp_nocase_uh(object_type, "trigger_sphere") == 0) {
3793  egg_syntax = "<Collide> { Sphere descend intangible }";
3794 
3795  } else if (cmp_nocase_uh(object_type, "eye_trigger") == 0) {
3796  egg_syntax = "<Collide> { Polyset descend intangible center }";
3797 
3798  } else if (cmp_nocase_uh(object_type, "bubble") == 0) {
3799  egg_syntax = "<Collide> { Sphere keep descend }";
3800 
3801  } else if (cmp_nocase_uh(object_type, "ghost") == 0) {
3802  egg_syntax = "<Scalar> collide-mask { 0 }";
3803 
3804  } else if (cmp_nocase_uh(object_type, "dcs") == 0) {
3805  egg_syntax = "<DCS> { 1 }";
3806 
3807  } else if (cmp_nocase_uh(object_type, "model") == 0) {
3808  egg_syntax = "<Model> { 1 }";
3809 
3810  } else if (cmp_nocase_uh(object_type, "none") == 0) {
3811  // ObjectType "none" is a special case, meaning nothing in particular.
3812  return true;
3813 
3814  } else if (cmp_nocase_uh(object_type, "backstage") == 0) {
3815  // Ignore "backstage" geometry.
3816  return false;
3817 
3818  } else {
3819  egg2pg_cat.error()
3820  << "Unknown ObjectType " << object_type << "\n";
3821  _error = true;
3822  egg2pg_cat.debug() << "returning true\n";
3823  return true;
3824  }
3825  }
3826 
3827  if (!egg_syntax.empty()) {
3828  if (!egg_group->parse_egg(egg_syntax)) {
3829  egg2pg_cat.error()
3830  << "Error while parsing definition for ObjectType "
3831  << object_type << "\n";
3832  _error = true;
3833 
3834  } else {
3835  // Now we've parsed the object type syntax, which might have
3836  // added more object types. Recurse if necessary.
3837  if (egg_group->get_num_object_types() != 0) {
3838  if (!expand_object_types(egg_group, expanded, expanded_history)) {
3839  return false;
3840  }
3841  }
3842  }
3843  }
3844 
3845  return true;
3846 }
3847 
3848 ////////////////////////////////////////////////////////////////////
3849 // Function: EggLoader::get_combine_mode
3850 // Access: Private, Static
3851 // Description: Extracts the combine_mode from the given egg texture,
3852 // and returns its corresponding TextureStage value.
3853 ////////////////////////////////////////////////////////////////////
3854 TextureStage::CombineMode EggLoader::
3855 get_combine_mode(const EggTexture *egg_tex,
3856  EggTexture::CombineChannel channel) {
3857  switch (egg_tex->get_combine_mode(channel)) {
3858  case EggTexture::CM_unspecified:
3859  // fall through
3860 
3861  case EggTexture::CM_modulate:
3862  return TextureStage::CM_modulate;
3863 
3864  case EggTexture::CM_replace:
3865  return TextureStage::CM_replace;
3866 
3867  case EggTexture::CM_add:
3868  return TextureStage::CM_add;
3869 
3870  case EggTexture::CM_add_signed:
3871  return TextureStage::CM_add_signed;
3872 
3873  case EggTexture::CM_interpolate:
3874  return TextureStage::CM_interpolate;
3875 
3876  case EggTexture::CM_subtract:
3877  return TextureStage::CM_subtract;
3878 
3879  case EggTexture::CM_dot3_rgb:
3880  return TextureStage::CM_dot3_rgb;
3881 
3882  case EggTexture::CM_dot3_rgba:
3883  return TextureStage::CM_dot3_rgba;
3884  };
3885 
3886  return TextureStage::CM_undefined;
3887 }
3888 
3889 ////////////////////////////////////////////////////////////////////
3890 // Function: EggLoader::get_combine_source
3891 // Access: Private, Static
3892 // Description: Extracts the combine_source from the given egg texture,
3893 // and returns its corresponding TextureStage value.
3894 ////////////////////////////////////////////////////////////////////
3895 TextureStage::CombineSource EggLoader::
3896 get_combine_source(const EggTexture *egg_tex,
3897  EggTexture::CombineChannel channel, int n) {
3898  switch (egg_tex->get_combine_source(channel, n)) {
3899  case EggTexture::CS_unspecified:
3900  // The default source if it is unspecified is based on the
3901  // parameter index.
3902  switch (n) {
3903  case 0:
3904  return TextureStage::CS_previous;
3905  case 1:
3906  return TextureStage::CS_texture;
3907  case 2:
3908  return TextureStage::CS_constant;
3909  }
3910  // Otherwise, fall through
3911 
3912  case EggTexture::CS_texture:
3913  return TextureStage::CS_texture;
3914 
3915  case EggTexture::CS_constant:
3916  return TextureStage::CS_constant;
3917 
3918  case EggTexture::CS_primary_color:
3919  return TextureStage::CS_primary_color;
3920 
3921  case EggTexture::CS_previous:
3922  return TextureStage::CS_previous;
3923 
3924  case EggTexture::CS_constant_color_scale:
3925  return TextureStage::CS_constant_color_scale;
3926 
3927  case EggTexture::CS_last_saved_result:
3928  return TextureStage::CS_last_saved_result;
3929  };
3930 
3931  return TextureStage::CS_undefined;
3932 }
3933 
3934 ////////////////////////////////////////////////////////////////////
3935 // Function: EggLoader::get_combine_operand
3936 // Access: Private, Static
3937 // Description: Extracts the combine_operand from the given egg texture,
3938 // and returns its corresponding TextureStage value.
3939 ////////////////////////////////////////////////////////////////////
3940 TextureStage::CombineOperand EggLoader::
3941 get_combine_operand(const EggTexture *egg_tex,
3942  EggTexture::CombineChannel channel, int n) {
3943  switch (egg_tex->get_combine_operand(channel, n)) {
3944  case EggTexture::CS_unspecified:
3945  if (channel == EggTexture::CC_rgb) {
3946  // The default operand for RGB is src_color, except for the
3947  // third parameter, which defaults to src_alpha.
3948  return n < 2 ? TextureStage::CO_src_color : TextureStage::CO_src_alpha;
3949  } else {
3950  // The default operand for alpha is always src_alpha.
3951  return TextureStage::CO_src_alpha;
3952  }
3953 
3954  case EggTexture::CO_src_color:
3955  return TextureStage::CO_src_color;
3956 
3957  case EggTexture::CO_one_minus_src_color:
3958  return TextureStage::CO_one_minus_src_color;
3959 
3960  case EggTexture::CO_src_alpha:
3961  return TextureStage::CO_src_alpha;
3962 
3963  case EggTexture::CO_one_minus_src_alpha:
3964  return TextureStage::CO_one_minus_src_alpha;
3965  };
3966 
3967  return TextureStage::CO_undefined;
3968 }
3969 
3970 ////////////////////////////////////////////////////////////////////
3971 // Function: EggLoader::get_color_blend_mode
3972 // Access: Private, Static
3973 // Description: Converts the EggGroup's BlendMode to the
3974 // corresponding ColorBlendAttrib::Mode value.
3975 ////////////////////////////////////////////////////////////////////
3976 ColorBlendAttrib::Mode EggLoader::
3977 get_color_blend_mode(EggGroup::BlendMode mode) {
3978  switch (mode) {
3979  case EggGroup::BM_unspecified:
3980  case EggGroup::BM_none:
3981  return ColorBlendAttrib::M_none;
3982  case EggGroup::BM_add:
3983  return ColorBlendAttrib::M_add;
3984  case EggGroup::BM_subtract:
3985  return ColorBlendAttrib::M_subtract;
3986  case EggGroup::BM_inv_subtract:
3987  return ColorBlendAttrib::M_inv_subtract;
3988  case EggGroup::BM_min:
3989  return ColorBlendAttrib::M_min;
3990  case EggGroup::BM_max:
3991  return ColorBlendAttrib::M_max;
3992  }
3993 
3994  return ColorBlendAttrib::M_none;
3995 }
3996 
3997 ////////////////////////////////////////////////////////////////////
3998 // Function: EggLoader::get_color_blend_operand
3999 // Access: Private, Static
4000 // Description: Converts the EggGroup's BlendOperand to the
4001 // corresponding ColorBlendAttrib::Operand value.
4002 ////////////////////////////////////////////////////////////////////
4003 ColorBlendAttrib::Operand EggLoader::
4004 get_color_blend_operand(EggGroup::BlendOperand operand) {
4005  switch (operand) {
4006  case EggGroup::BO_zero:
4007  return ColorBlendAttrib::O_zero;
4008  case EggGroup::BO_unspecified:
4009  case EggGroup::BO_one:
4010  return ColorBlendAttrib::O_one;
4011  case EggGroup::BO_incoming_color:
4012  return ColorBlendAttrib::O_incoming_color;
4013  case EggGroup::BO_one_minus_incoming_color:
4014  return ColorBlendAttrib::O_one_minus_incoming_color;
4015  case EggGroup::BO_fbuffer_color:
4016  return ColorBlendAttrib::O_fbuffer_color;
4017  case EggGroup::BO_one_minus_fbuffer_color:
4018  return ColorBlendAttrib::O_one_minus_fbuffer_color;
4019  case EggGroup::BO_incoming_alpha:
4020  return ColorBlendAttrib::O_incoming_alpha;
4021  case EggGroup::BO_one_minus_incoming_alpha:
4022  return ColorBlendAttrib::O_one_minus_incoming_alpha;
4023  case EggGroup::BO_fbuffer_alpha:
4024  return ColorBlendAttrib::O_fbuffer_alpha;
4025  case EggGroup::BO_one_minus_fbuffer_alpha:
4026  return ColorBlendAttrib::O_one_minus_fbuffer_alpha;
4027  case EggGroup::BO_constant_color:
4028  return ColorBlendAttrib::O_constant_color;
4029  case EggGroup::BO_one_minus_constant_color:
4030  return ColorBlendAttrib::O_one_minus_constant_color;
4031  case EggGroup::BO_constant_alpha:
4032  return ColorBlendAttrib::O_constant_alpha;
4033  case EggGroup::BO_one_minus_constant_alpha:
4034  return ColorBlendAttrib::O_one_minus_constant_alpha;
4035  case EggGroup::BO_incoming_color_saturate:
4036  return ColorBlendAttrib::O_incoming_color_saturate;
4037  case EggGroup::BO_color_scale:
4038  return ColorBlendAttrib::O_color_scale;
4039  case EggGroup::BO_one_minus_color_scale:
4040  return ColorBlendAttrib::O_one_minus_color_scale;
4041  case EggGroup::BO_alpha_scale:
4042  return ColorBlendAttrib::O_alpha_scale;
4043  case EggGroup::BO_one_minus_alpha_scale:
4044  return ColorBlendAttrib::O_one_minus_alpha_scale;
4045  }
4046 
4047  return ColorBlendAttrib::O_zero;
4048 }
4049 
4050 ////////////////////////////////////////////////////////////////////
4051 // Function: EggLoader::VertexPoolTransform::operator <
4052 // Access: Public
4053 // Description:
4054 ////////////////////////////////////////////////////////////////////
4055 bool EggLoader::VertexPoolTransform::
4056 operator < (const EggLoader::VertexPoolTransform &other) const {
4057  if (_vertex_pool != other._vertex_pool) {
4058  return _vertex_pool < other._vertex_pool;
4059  }
4060  int compare = _transform.compare_to(other._transform, 0.001);
4061  if (compare != 0) {
4062  return compare < 0;
4063  }
4064 
4065  if (_bake_in_uvs.size() != other._bake_in_uvs.size()) {
4066  return _bake_in_uvs.size() < other._bake_in_uvs.size();
4067  }
4068 
4069  BakeInUVs::const_iterator ai, bi;
4070  ai = _bake_in_uvs.begin();
4071  bi = other._bake_in_uvs.begin();
4072  while (ai != _bake_in_uvs.end()) {
4073  nassertr(bi != other._bake_in_uvs.end(), false);
4074  if ((*ai) != (*bi)) {
4075  return (*ai) < (*bi);
4076  }
4077  ++ai;
4078  ++bi;
4079  }
4080  nassertr(bi == other._bake_in_uvs.end(), false);
4081 
4082  return false;
4083 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights...
Definition: eggPrimitive.h:51
A node of this type is created automatically at the root of each model file that is loaded...
Definition: modelRoot.h:31
bool almost_equal(const LVecBase4f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase4.h:1323
bool close_primitive()
Indicates that the previous n calls to add_vertex(), since the last call to close_primitive(), have fully defined a new primitive.
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this node by the indicated matrix, if it means anything to do so...
The set of UV&#39;s that may or may not be assigned to a vertex.
Definition: eggVertexUV.h:32
This class records a set of integers, where each integer is either present or not present in the set...
Definition: sparseArray.h:49
void set_tag(const string &key, const string &value, Thread *current_thread=Thread::get_current_thread())
Associates a user-defined value with a user-defined key which is stored on the node.
Definition: pandaNode.cxx:1403
bool parse_egg(const string &egg_syntax)
Parses the egg syntax given in the indicate string as if it had been read from the egg file within th...
Definition: eggNode.cxx:268
void set_tangible(bool tangible)
Sets the current state of the &#39;tangible&#39; flag.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
void set_vertices(const LPoint3 &v0, const LPoint3 &v1, const LPoint3 &v2, const LPoint3 &v3)
Replaces the four vertices of the occluder polygon.
Definition: occluderNode.I:24
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
EggUserData * get_user_data() const
Returns the user data pointer most recently stored on this object, or NULL if nothing was previously ...
Definition: eggObject.cxx:102
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
int get_anisotropic_degree() const
Returns the anisotropic filtering degree that has been specified for this texture, or 0 if nothing has been specified.
Definition: eggTexture.I:289
The base class for primitives such as triangle strips and triangle fans, which include several compon...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
static Texture * load_cube_map(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a cube map texture that is specified with a series of 6 pages, numbered 0 through 5...
Definition: texturePool.I:146
LColor get_color() const
Returns the color set on this particular attribute.
virtual Shading get_shading() const
Returns the shading properties apparent on this particular primitive.
CurveType get_curve_type() const
Returns the indicated type of the curve.
Definition: eggCurve.I:99
This class keeps track of all the state we must make note of during the graph traversal, but cannot apply immediately.
This is a unit quaternion representing a rotation.
Definition: lrotation.h:92
Defines a series of disconnected points.
Definition: geomPoints.h:25
bool get_bface_flag() const
Retrieves the backfacing flag of the polygon.
Definition: eggPrimitive.I:301
A virtual base class for parametric curves.
int get_num_vertices() const
Returns the number of vertices in the portal polygon.
Definition: portalNode.I:152
EggVertex * add_vertex(EggVertex *vertex, int index=-1)
Adds the indicated vertex to the pool.
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded...
This is a two-component vector offset.
Definition: lvector2.h:416
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:26
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
bool get_multiview() const
Returns the current setting of the multiview flag.
Definition: eggTexture.I:974
The abstract base class for all things that can collide with other things in the world, and all the things they can collide with (except geometry).
int triangulate_polygons(int flags)
Replace all higher-order polygons at this point in the scene graph and below with triangles...
virtual bool recompute()
Recalculates the curve, if necessary.
void set_double_sided(bool value)
If true, the back-face will also be used to occlude.
Definition: occluderNode.I:61
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:33
A cuboid collision volume or object.
Definition: collisionBox.h:30
This class is an abstraction for evaluating NURBS curves.
Defines a series of triangle fans.
Definition: geomTrifans.h:25
int get_order() const
Returns the order of the curve.
Definition: eggNurbsCurve.I:89
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of children of the referenced node.
Definition: nodePath.I:406
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
void loop(bool restart)
Starts the entire animation looping.
Definition: animInterface.I:57
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:63
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:160
bool has_dcs_type() const
Returns true if the specified DCS type is not DC_none and not DC_unspecified.
Definition: eggGroup.I:244
TypedReferenceCount * get_aux_data(const string &key) const
Returns a record previously recorded via set_aux_data().
Definition: texture.cxx:629
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:1661
A base class for things which need to inherit from both TypedObject and from ReferenceCount.
A node that automatically cycles through rendering each one of its children according to its frame ra...
Definition: sequenceNode.h:29
GroupRef::const_iterator gref_end() const
Returns an iterator that can, in conjunction with gref_begin(), be used to traverse the entire set of...
Definition: eggVertex.cxx:805
void set_texture_num_views(int num_views)
Specifies the expected number of views to load for the texture.
This class draws a visible representation of the NURBS curve stored in its NurbsCurveEvaluator.
Definition: ropeNode.h:38
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
This is a collection of textures by TRef name.
bool almost_equal(const LVecBase3d &other, double threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase3.h:2614
Defines a series of triangle strips.
Definition: geomTristrips.h:25
This is an abstract base class that retains some slider value, which is a linear value that typically...
Definition: vertexSlider.h:41
void set_slider_table(const SliderTable *table)
Replaces the SliderTable on this vertex data with the indicated table.
double get_knot(int k) const
Returns the nth knot value defined.
const LMatrix4d & get_vertex_to_node() const
Returns the transformation matrix suitable for converting the vertices as read from the egg file into...
Definition: eggNode.I:203
void apply_first_attribute(bool recurse)
Sets the first vertex of the triangle (or each component) to the primitive normal and/or color...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This class is an abstraction for evaluating NURBS surfaces.
A spherical collision volume or object.
void set_external_index(int external_index)
Sets a special index number that is associated with the EggVertex (but is not written to the egg file...
Definition: eggVertex.I:365
GroupRef::const_iterator gref_begin() const
Returns an iterator that can, in conjunction with gref_end(), be used to traverse the entire set of g...
Definition: eggVertex.cxx:789
A single &lt;Dxyz&gt; or &lt;Duv&gt; or some such entry.
Definition: eggMorph.h:33
This abstract class defines the interface only for a Nurbs-style curve, with knots and coordinates in...
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
void set_radius(PN_stdfloat r)
Set radius of the spherical light volume.
void set_effective_normal(const LVector3 &effective_normal)
Records a false normal for this CollisionSolid that will be reported by the collision system with all...
bool get_read_mipmaps() const
Returns the current setting of the read_mipmaps flag.
Definition: eggTexture.I:1056
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:263
int get_num_views() const
Returns the specified number of views specified for the 3-D multiview texture.
Definition: eggTexture.I:1023
int get_external_index() const
Returns the number set by set_external_index().
Definition: eggVertex.I:376
void set_row(int row, const LVecBase4d &v)
Replaces the indicated row of the matrix.
Definition: lmatrix.h:5452
int get_v_subdiv() const
Returns the requested number of subdivisions in the U direction, or 0 if no particular subdivisions h...
Definition: eggSurface.I:104
const_aux_iterator aux_begin() const
Returns an iterator that allows walking through the complete set of auxiliary data on the vertex...
Definition: eggVertex.I:287
void set_coordinate_system(CoordinateSystem coordsys)
Changes the coordinate system of the EggData.
Definition: eggData.cxx:279
int get_num_vertices() const
Returns the number of vertices in the occluder polygon.
Definition: occluderNode.I:41
const_uv_iterator uv_begin() const
Returns an iterator that allows walking through the complete set of named UV&#39;s on the vertex...
Definition: eggVertex.I:273
int get_num_knots() const
Returns the number of knots.
virtual bool cleanup()
Cleans up modeling errors in whatever context this makes sense.
Definition: eggPolygon.cxx:36
void limit_transforms(int max_transforms)
If the total number of transforms in the blend exceeds max_transforms, removes the n least-important ...
void set_pos(const LPoint3 &position)
Set this light&#39;s position.
Definition: polylightNode.I:88
A node in the scene graph that can hold an occluder polygon, which must be a rectangle.
Definition: occluderNode.h:35
bool has_anisotropic_degree() const
Returns true if a value for the anisotropic filtering degree has been specified for this texture...
Definition: eggTexture.I:277
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:34
const_uv_iterator uv_end() const
Returns an iterator that allows walking through the complete set of named UV&#39;s on the vertex...
Definition: eggVertex.I:301
bool is_planar() const
Returns true if all of the polygon&#39;s vertices lie within the same plane, false otherwise.
Definition: eggPolygon.cxx:96
bool has_vertex_color() const
Returns true if any vertex on the primitive has a specific color set, false otherwise.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
A dynamic array with an unlimited number of bits.
Definition: bitArray.h:42
This class is used within this package only to record the render state that should be assigned to eac...
virtual bool determine_decal()
Walks back up the hierarchy, looking for an EggGroup at this level or above that has the &quot;decal&quot; flag...
Definition: eggGroup.cxx:662
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:2132
void set_bface_flag(bool flag)
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.I:289
The main glue of the egg hierarchy, this corresponds to the &lt;Group&gt;, &lt;Instance&gt;, and &lt;Joint&gt; type nod...
Definition: eggGroup.h:36
EggGroup * get_group_ref(int n) const
Returns the nth &lt;Ref&gt; entry within this group.
Definition: eggGroup.cxx:874
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition: sliderTable.h:42
void set_num_subdiv(int num_subdiv)
Specifies the number of subdivisions per cubic segment (that is, per unique knot value) to draw in a ...
Definition: ropeNode.I:297
void add_vertex(int vertex)
Adds the indicated vertex to the list of vertex indices used by the graphics primitive type...
void reparent_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread())
Removes the referenced node of the NodePath from its current parent and attaches it to the referenced...
Definition: nodePath.cxx:523
A node in the scene graph that can hold a Portal Polygon, which is a rectangle.
Definition: portalNode.h:34
LPoint4d get_pos4() const
This is always valid, regardless of the value of get_num_dimensions.
Definition: eggVertex.I:178
void set_anisotropic_degree(int anisotropic_degree)
Sets the degree of anisotropic filtering for this texture.
Definition: eggTexture.I:252
void start_sequences()
Starts all of the SequenceNodes we created looping.
Definition: eggLoader.cxx:302
void set_panda()
Specifies that vertex animation is to be performed by Panda.
NodePath get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns a NodePath representing the nth child of the referenced node.
Definition: nodePath.I:418
This node is placed at key points within the scene graph to indicate the roots of &quot;models&quot;: subtrees ...
Definition: modelNode.h:34
Defines a series of &quot;patches&quot;, fixed-size groupings of vertices that must be processed by a tessellat...
Definition: geomPatches.h:27
This class draws a visible representation of the NURBS surface stored in its NurbsSurfaceEvaluator.
Definition: sheetNode.h:36
This is a convenience class to specialize ConfigVariable as a string type.
This is an abstract base class that holds a pointer to some transform, computed in some arbitrary way...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
A Level-of-Detail node.
Definition: lodNode.h:31
bool has_alpha_filename() const
Returns true if a separate file for the alpha component has been applied, false otherwise.
Definition: eggTexture.I:846
int get_u_subdiv() const
Returns the requested number of subdivisions in the U direction, or 0 if no particular subdivisions h...
Definition: eggSurface.I:77
int get_alpha_file_channel() const
Returns the particular channel that has been specified for the alpha-file image, or 0 if no channel h...
Definition: eggTexture.I:944
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
void add_attrib(const RenderAttrib *attrib)
A convenience function to add the indicated render attribute to the aggregate state.
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of child nodes this node has.
Definition: pandaNode.I:68
int remove_unused_vertices()
Removes all vertices from the pool that are not referenced by at least one primitive.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
void steal_children(EggGroupNode &other)
Moves all the children from the other node to this one.
const Filename & get_fullpath() const
Returns the full pathname to the file, if it is known; otherwise, returns the same thing as get_filen...
void mesh_triangles(int flags)
Combine triangles together into triangle strips, at this group and below.
A container for geometry primitives.
Definition: geom.h:58
void set_shade_model(ShadeModel shade_model)
Changes the ShadeModel hint for this primitive.
Definition: geomPrimitive.I:45
bool invert_from(const LMatrix4d &other)
Computes the inverse of the other matrix, and stores the result in this matrix.
Definition: lmatrix.h:6438
A Nonuniform Rational B-Spline.
Definition: nurbsCurve.h:48
void compose(const DeferredNodeProperty &other)
Composes this state with the next one encountered on a lower node during the apply traversal...
void set_alpha_mode(AlphaMode mode)
Specifies precisely how the transparency for this geometry should be achieved, or if it should be use...
A PolylightNode.
Definition: polylightNode.h:31
void set_alpha_filename(const Filename &alpha_filename)
Sets the name of the file that contains the image&#39;s alpha channel contents.
Definition: texture.I:2301
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
const LTexCoord3d & get_uvw() const
Returns the texture coordinate triple, if get_num_dimensions() is 3.
Definition: eggVertexUV.I:88
The set of named auxiliary data that may or may not be assigned to a vertex.
Definition: eggVertexAux.h:33
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
void set_effect(const RenderEffect *effect)
Adds the indicated render effect to the scene graph on this node.
Definition: pandaNode.cxx:1174
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
Definition: eggGroup.cxx:756
double length_squared() const
Returns the square of the vector&#39;s length, cheap and easy.
Definition: lvecBase3.h:2098
A parametric NURBS curve.
Definition: eggNurbsCurve.h:28
A special binner used only within this package to pre-process the egg tree for the loader and group t...
Definition: eggBinner.h:34
static Texture * load_texture(const Filename &filename, int primary_file_num_channels=0, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads the given filename up into a texture, if it has not already been loaded, and returns the new te...
Definition: texturePool.I:58
int find_used_textures(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for textures that are referenced by ...
bool has_transform() const
Returns true if the transform is nonempty, false if it is empty (no transform components have been ad...
Definition: eggTransform.I:163
void add_stashed(PandaNode *child_node, int sort=0, Thread *current_thread=Thread::get_current_thread())
Adds a new child to the node, directly as a stashed child.
Definition: pandaNode.cxx:919
This node is placed at key points within the scene graph to animate uvs.
Definition: uvScrollNode.h:29
A single polygon.
Definition: eggPolygon.h:26
void set_attrib(const RenderAttrib *attrib, int override=0)
Adds the indicated render attribute to the scene graph on this node.
Definition: pandaNode.cxx:1107
This object represents a solid made entirely of triangles, which will only be tested again z axis ali...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
void rebuild_vertex_pools(EggVertexPools &vertex_pools, unsigned int max_vertices, bool recurse)
Copies vertices used by the primitives at this group node (and below, if recurse is true) into one or...
Defines a series of disconnected line segments.
Definition: geomLines.h:25
const Filename & get_alpha_fullpath() const
Returns the full pathname to the alpha file, if it is known; otherwise, returns the same thing as get...
Definition: eggTexture.I:879
This defines a single entry in a TransformBlendTable.
bool triangulate_into(EggGroupNode *container, bool convex_also) const
Subdivides the polygon into triangles and adds each one to the indicated container.
Definition: eggPolygon.I:84
This corresponds to a.
Definition: eggTable.h:31
void set_curve_type(int type)
Sets the flag indicating the use to which the curve is intended to be put.
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:746
AlphaMode get_alpha_mode() const
Returns the alpha mode that was set, or AM_unspecified if nothing was set.
PandaNode * get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the nth child node of this node.
Definition: pandaNode.I:82
const_aux_iterator aux_end() const
Returns an iterator that allows walking through the complete set of auxiliary data on the vertex...
Definition: eggVertex.I:315
const LVecBase4d & get_aux() const
Returns the auxiliary data quadruple.
Definition: eggVertexAux.I:32
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:531
int get_num_vertices() const
Returns the number of indices used by all the primitives in this object.
Converts an EggTable hierarchy, beginning with a &lt;Bundle&gt; entry, into an AnimBundle hierarchy...
const Filename & get_alpha_filename() const
Returns the separate file assigned for the alpha channel.
Definition: eggTexture.I:858
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
Defines a series of line strips.
void sort_by_external_index()
Re-orders (and re-numbers) the vertices in this vertex pool so that they appear in increasing order b...
A node in the scene graph that can hold any number of CollisionSolids.
Definition: collisionNode.h:33
A parametric NURBS surface.
EggNode * get_first_child()
Returns the first child in the group&#39;s list of children, or NULL if the list of children is empty...
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
Defines a series of disconnected triangles.
Definition: geomTriangles.h:25
void reparent_decals()
For each node representing a decal base geometry (i.e.
Definition: eggLoader.cxx:237
This structure collects together the different combinations of transforms and blend amounts used by a...
void set_color(const LColor &color)
Set the light&#39;s color...
This is our own Panda specialization on the default STL set.
Definition: pset.h:52
void copy_grefs_from(const EggVertex &other)
Copies all the group references from the other vertex onto this one.
Definition: eggVertex.cxx:848
void normalize_weights()
Rescales all of the weights on the various transforms so that they sum to 1.0.
static Texture * load_3d_texture(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a 3-D texture that is specified with a series of n pages, all numbered in sequence, and beginning with index 0.
Definition: texturePool.I:104
int add_solid(const CollisionSolid *solid)
Adds the indicated solid to the node.
int add_blend(const TransformBlend &blend)
Adds a new blend to the table, and returns its index number.
void set_num_u_subdiv(int num_u_subdiv)
Specifies the number of subdivisions per cubic segment (that is, per unique knot value) to draw in a ...
Definition: sheetNode.I:103
Converts an EggGroup hierarchy, beginning with a group with &lt;Dart&gt; set, to a character node with join...
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2486
void clear_vertices()
Resets the vertices of the portal to the empty list.
Definition: portalNode.I:130
EggVertexPool * get_pool() const
Returns the vertex pool this vertex belongs in.
Definition: eggVertex.I:25
This is a 3-by-3 transform matrix.
Definition: lmatrix.h:110
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
Definition: eggVertex.cxx:819
EggVertex * get_vertex(int index) const
Returns a particular index based on its index number.
Definition: eggPrimitive.I:466
static TextureStage * get_default()
Returns the default TextureStage that will be used for all texturing that does not name a particular ...
Definition: textureStage.I:766
This is a node that contains a pointer to an AnimBundle.
This implements a solid roughly in cylindrical shape.
Definition: collisionTube.h:30
void add_child(PandaNode *child_node, int sort=0, Thread *current_thread=Thread::get_current_thread())
Adds a new child to the node.
Definition: pandaNode.cxx:654
bool has_num_views() const
Returns true if the number of views has been specified for the 3-D multiview texture, false otherwise.
Definition: eggTexture.I:1012
This corresponds to a &lt;SwitchCondition&gt; entry within a group.
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:38
void add_vertex(const LPoint3 &vertex)
Adds a new vertex to the portal polygon.
Definition: portalNode.I:142
void post_apply_flat_attribute(bool recurse)
Intended as a followup to apply_last_attribute(), this also sets an attribute on the first vertices o...
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
Definition: lvector3.h:527
A collection of vertices.
Definition: eggVertexPool.h:46
int get_num_group_refs() const
Returns the number of &lt;Ref&gt; entries within this group.
Definition: eggGroup.cxx:863
A SwitchCondition that switches the levels-of-detail based on distance from the camera&#39;s eyepoint...
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
void make_polyset(EggBin *egg_bin, PandaNode *parent, const LMatrix4d *transform, bool is_dynamic, CharacterMaker *character_maker)
Creates a polyset–that is, a Geom–from the primitives that have already been grouped into a bin...
Definition: eggLoader.cxx:321
void check_overall_color(bool &has_overall_color, LColor &overall_color) const
Scans the vertex pool for different colors on different vertices.
A node that renders only one of its children, according to the user&#39;s indication. ...
Definition: switchNode.h:27
void set_filename(const Filename &filename)
Sets the name of the file that contains the image&#39;s contents.
Definition: texture.I:2267
int get_index() const
Returns the index number of the vertex within its pool.
Definition: eggVertex.I:346
static TextureStage * get_stage(TextureStage *temp)
Returns a TextureStage pointer that represents the same TextureStage described by temp...
This represents the &lt;Transform&gt; entry of a group or texture node: a list of component transform opera...
Definition: eggTransform.h:33
An inverted sphere: this is a sphere whose collision surface is the inside surface of the sphere...
Converts an egg data structure, possibly read from an egg file but not necessarily, into a scene graph suitable for rendering.
Definition: eggLoader.h:70
A type of group node that holds related subnodes.
Definition: eggBin.h:30
EggVertexPool * get_pool() const
Returns the vertex pool associated with the vertices of the primitive, or NULL if the primitive has n...
Definition: eggPrimitive.I:480