Panda3D
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 
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()) {
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 ////////////////////////////////////////////////////////////////////
2218 PT(GeomVertexData) EggLoader::
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, 0, 2);
2317  temp_format->add_array(anim_array_format);
2318 
2319  pmap<string, BitArray> slider_names;
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 
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 ////////////////////////////////////////////////////////////////////
2537 PT(TransformBlendTable) EggLoader::
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 
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  }
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 
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
TagData::const_iterator tag_begin() const
Returns an iterator that can, in conjunction with tag_end(), be used to traverse the entire set of ta...
Definition: eggGroup.I:999
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
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
Definition: eggVertex.cxx:819
AlphaMode get_alpha_mode() const
Returns the alpha mode that was set, or AM_unspecified if nothing was set.
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
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 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.
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
bool has_color() const
Returns true if a blend color has been specified for the texture.
Definition: eggTexture.I:593
const LColor & get_border_color() const
Returns the border color if one has been specified, or (0, 0, 0, 1) otherwise.
Definition: eggTexture.I:648
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...
const LVecBase3d & get_component_vec3(int n) const
Returns the 3-component vector associated with the nth component.
Definition: eggTransform.I:330
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_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
double get_component_number(int n) const
Returns the solitary number associated with the nth component.
Definition: eggTransform.I:299
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.
int get_order() const
Returns the order of the curve.
Definition: eggNurbsCurve.I:89
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
int get_num_dimensions() const
Returns the maximum number of dimensions used by any vertex in the pool.
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
bool has_alpha_scale() const
Returns true if an alpha_scale has been specified for the texture, false otherwise.
Definition: eggTexture.I:794
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
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 class keeps track of all the state we must make note of during the graph traversal, but cannot apply immediately.
int get_rgb_scale() const
Returns the rgb_scale value that has been specified for the texture, or 1 if no rgb_scale value has b...
Definition: eggTexture.I:756
This is a unit quaternion representing a rotation.
Definition: lrotation.h:92
Defines a series of disconnected points.
Definition: geomPoints.h:25
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
A virtual base class for parametric curves.
bool get_read_mipmaps() const
Returns the current setting of the read_mipmaps flag.
Definition: eggTexture.I:1056
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
CurveType get_curve_type() const
Returns the indicated type of the curve.
Definition: eggCurve.I:99
TagData::const_iterator tag_end() const
Returns an iterator that can, in conjunction with tag_begin(), be used to traverse the entire set of ...
Definition: eggGroup.I:1015
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded...
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
EggVertexPool * get_pool() const
Returns the vertex pool this vertex belongs in.
Definition: eggVertex.I:25
This is a two-component vector offset.
Definition: lvector2.h:429
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
const LTexCoord3d & get_uvw() const
Returns the texture coordinate triple, if get_num_dimensions() is 3.
Definition: eggVertexUV.I:88
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...
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
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
void apply_to_node(PandaNode *node)
Applies whatever state is appropriate to the node.
bool has_vertex_color() const
Returns true if any vertex on the primitive has a specific color set, false otherwise.
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
void set_rows(const SparseArray &rows)
Specifies the subset of rows (vertices) in the associated GeomVertexData that this TransformBlendTabl...
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
void set_format(Format format)
Changes the format value for the texture components.
Definition: texture.I:2440
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
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
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
void check_overall_color(bool &has_overall_color, LColor &overall_color) const
Scans the vertex pool for different colors on different vertices.
int get_num_vertices() const
Returns the number of indices used by all the primitives in this object.
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
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:1677
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
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
void set_data1i(int data)
Sets the write row to a particular 1-component value, and advances the write row. ...
This is a collection of textures by TRef name.
Defines a series of triangle strips.
Definition: geomTristrips.h:25
bool has_stage_name() const
Returns true if a stage name has been explicitly specified for this texture, false otherwise...
Definition: eggTexture.I:498
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
This is an abstract base class that retains some slider value, which is a linear value that typically...
Definition: vertexSlider.h:41
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
void set_magfilter(FilterType filter)
Sets the filtering method that should be used when viewing the texture up close.
Definition: texture.I:967
This class is an abstraction for evaluating NURBS surfaces.
A spherical collision volume or object.
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:160
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
int get_component_width() const
Returns the number of bytes stored for each color component of a texel.
Definition: texture.I:848
string get_name() const
Returns the name of the character.
A single <Dxyz> or <Duv> 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...
int get_num_components() const
Returns the number of components that make up the transform.
Definition: eggTransform.I:274
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...
LColor get_color() const
Returns the color set on this particular attribute.
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
size_type size() const
Returns the number of vertices in the pool.
double length_squared() const
Returns the square of the vector&#39;s length, cheap and easy.
Definition: lvecBase2.h:1799
void add_data4(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat w)
Sets the write row to a particular 4-component value, and advances the write row. ...
bool has_normals() const
Returns true if any vertex in the pool has a normal defined, false if none of them do...
VertexSlider * egg_to_slider(const string &name)
Returns the VertexSlider corresponding to the indicated egg slider name.
iterator end() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
void set_curve(NurbsCurveEvaluator *curve)
Sets the particular curve represented by the RopeNode.
Definition: ropeNode.I:70
const LVecBase2d & get_component_vec2(int n) const
Returns the 2-component vector associated with the nth component.
Definition: eggTransform.I:314
void set_row(int row, const LVecBase4d &v)
Replaces the indicated row of the matrix.
Definition: lmatrix.h:5452
void set_coordinate_system(CoordinateSystem coordsys)
Changes the coordinate system of the EggData.
Definition: eggData.cxx:279
TypedReferenceCount * get_aux_data(const string &key) const
Returns a record previously recorded via set_aux_data().
Definition: texture.cxx:636
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
ComponentType get_component_type(int n) const
Returns the type of the nth component.
Definition: eggTransform.I:284
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:34
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
This class is used within this package only to record the render state that should be assigned to eac...
EggGroup * get_group_ref(int n) const
Returns the nth <Ref> entry within this group.
Definition: eggGroup.cxx:874
virtual bool determine_decal()
Walks back up the hierarchy, looking for an EggGroup at this level or above that has the "decal" flag...
Definition: eggGroup.cxx:662
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:2149
void set_bface_flag(bool flag)
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.I:289
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this PandaNode by the indicated matrix, if it means anything to do so...
Definition: pandaNode.cxx:377
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:36
int get_priority() const
Returns the multitexture importance value that has been specified for the texture, or 0 if no priority value has been specified.
Definition: eggTexture.I:560
WrapMode determine_wrap_w() const
Determines the appropriate wrap in the W direction.
Definition: eggTexture.I:200
const string & get_uv_name() const
Returns the texcoord name that has been specified for this texture, or the empty string if no texcoor...
Definition: eggTexture.I:705
void set_data3d(double x, double y, double z)
Sets the write row to a particular 3-component value, and advances the write row. ...
WrapMode determine_wrap_v() const
Determines the appropriate wrap in the V direction.
Definition: eggTexture.I:166
Stores the total set of VertexSliders that the vertices in a particular GeomVertexData object might d...
Definition: sliderTable.h:42
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
int get_index() const
Returns the index number of the vertex within its pool.
Definition: eggVertex.I:346
void set_anisotropic_degree(int anisotropic_degree)
Sets the degree of anisotropic filtering for this texture.
Definition: eggTexture.I:252
bool has_rgb_scale() const
Returns true if an rgb_scale has been specified for the texture, false otherwise. ...
Definition: eggTexture.I:744
void get_uv_names(vector_string &uv_names, vector_string &uvw_names, vector_string &tbn_names) const
Returns the list of UV names that are defined by any vertices in the pool, as well as the subset of U...
void start_sequences()
Starts all of the SequenceNodes we created looping.
Definition: eggLoader.cxx:302
void set_data4d(double x, double y, double z, double w)
Sets the write row to a particular 4-component value, and advances the write row. ...
int get_num_vertices() const
Returns the number of vertices in the portal polygon.
Definition: portalNode.I:152
void set_panda()
Specifies that vertex animation is to be performed by Panda.
int get_subdiv() const
Returns the requested number of subdivisions, or 0 if no particular subdivisions have been requested...
Definition: eggCurve.I:76
This node is placed at key points within the scene graph to indicate the roots of "models": subtrees ...
Definition: modelNode.h:34
Defines a series of "patches", 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
int get_num_views() const
Returns the specified number of views specified for the 3-D multiview texture.
Definition: eggTexture.I:1023
const string & get_stage_name() const
Returns the stage name that has been specified for this texture, or the tref name if no texture stage...
Definition: eggTexture.I:510
This is a convenience class to specialize ConfigVariable as a string type.
void add_data4d(double x, double y, double z, double w)
Sets the write row to a particular 4-component value, and advances the write row. ...
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...
double get_knot(int k) const
Returns the nth knot value defined.
A Level-of-Detail node.
Definition: lodNode.h:31
void set_wrap_v(WrapMode wrap)
This setting determines what happens when the texture is sampled with a V value outside the range 0...
Definition: texture.I:920
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
bool almost_equal(const LVecBase4f &other, float threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase4.h:1339
int get_num_knots() const
Returns the number of knots.
void add_attrib(const RenderAttrib *attrib)
A convenience function to add the indicated render attribute to the aggregate state.
int remove_unused_vertices()
Removes all vertices from the pool that are not referenced by at least one primitive.
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
const Filename & get_alpha_filename() const
Returns the separate file assigned for the alpha channel.
Definition: eggTexture.I:858
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 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.
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
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
bool triangulate_into(EggGroupNode *container) const
Subdivides the composite primitive into triangles and adds those triangles to the indicated container...
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
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
Definition: eggGroup.cxx:756
bool almost_equal(const LVecBase3d &other, double threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase3.h:2646
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
void set_quality_level(QualityLevel quality_level)
Sets a hint to the renderer about the desired performance / quality tradeoff for this particular text...
Definition: texture.I:1333
The set of named auxiliary data that may or may not be assigned to a vertex.
Definition: eggVertexAux.h:33
void set_effect(const RenderEffect *effect)
Adds the indicated render effect to the scene graph on this node.
Definition: pandaNode.cxx:1174
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
void add_data3d(double x, double y, double z)
Sets the write row to a particular 3-component value, and advances the write row. ...
const LColor & get_blend_color() const
Returns the blend color if one has been specified, or (0, 0, 0, 0) if one has not.
Definition: eggGroup.I:822
int find_used_textures(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for textures that are referenced by ...
int get_num_vertices() const
Returns the number of vertices in the occluder polygon.
Definition: occluderNode.I:41
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
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
const Filename & get_fullpath() const
Returns the full pathname to the file, if it is known; otherwise, returns the same thing as get_filen...
int get_alpha_scale() const
Returns the alpha_scale value that has been specified for the texture, or 1 if no alpha_scale value h...
Definition: eggTexture.I:806
A single polygon.
Definition: eggPolygon.h:26
void set_compression(CompressionMode compression)
Requests that this particular Texture be compressed when it is loaded into texture memory...
Definition: texture.I:1033
bool get_saved_result() const
Returns the current setting of the saved_result flag.
Definition: eggTexture.I:416
static SparseArray lower_on(int on_bits)
Returns a SparseArray whose lower on_bits bits are on.
Definition: sparseArray.I:78
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...
const LColor & get_color() const
Returns the blend color if one has been specified, or (0, 0, 0, 1) otherwise.
Definition: eggTexture.I:604
Defines a series of disconnected line segments.
Definition: geomLines.h:25
This defines a single entry in a TransformBlendTable.
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
void set_surface(NurbsSurfaceEvaluator *surface)
Sets the particular surface represented by the SheetNode.
Definition: sheetNode.I:50
This corresponds to a.
Definition: eggTable.h:31
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void set_anisotropic_degree(int anisotropic_degree)
Specifies the level of anisotropic filtering to apply to the texture.
Definition: texture.I:990
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:760
void set_wrap_u(WrapMode wrap)
This setting determines what happens when the texture is sampled with a U value outside the range 0...
Definition: texture.I:902
void replace(iterator position, PT(EggNode) x)
Replaces the node at the indicated position with the indicated node.
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:544
bool has_priority() const
Returns true if a priority value for multitexture importance has been specified for the texture...
Definition: eggTexture.I:548
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:284
Converts an EggTable hierarchy, beginning with a <Bundle> entry, into an AnimBundle hierarchy...
double length_squared() const
Returns the square of the vector&#39;s length, cheap and easy.
Definition: lvecBase3.h:2115
bool has_value() const
Returns true if this variable has an explicit value, either from a prc file or locally set...
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
bool has_column() const
Returns true if a valid data type has been successfully set, or false if the data type does not exist...
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
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
WrapMode determine_wrap_u() const
Determines the appropriate wrap in the U direction.
Definition: eggTexture.I:132
const LMatrix3d & get_component_mat3(int n) const
Returns the 3x3 matrix associated with the nth component.
Definition: eggTransform.I:344
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
bool get_multiview() const
Returns the current setting of the multiview flag.
Definition: eggTexture.I:974
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
int make_bins(EggGroupNode *root_group)
The main entry point to EggBinMaker.
Definition: eggBinMaker.cxx:77
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 has_border_color() const
Returns true if a border color has been specified for the texture.
Definition: eggTexture.I:637
bool has_dcs_type() const
Returns true if the specified DCS type is not DC_none and not DC_unspecified.
Definition: eggGroup.I:244
Defines a series of disconnected triangles.
Definition: geomTriangles.h:25
void set_minfilter(FilterType filter)
Sets the filtering method that should be used when viewing the texture from a distance.
Definition: texture.I:951
LPoint4d get_pos4() const
This is always valid, regardless of the value of get_num_dimensions.
Definition: eggVertex.I:178
virtual Shading get_shading() const
Returns the shading properties apparent on this particular primitive.
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...
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
bool set_column(int column)
Sets up the writer to use the nth data type of the GeomVertexFormat, numbering from 0...
int get_external_index() const
Returns the number set by set_external_index().
Definition: eggVertex.I:376
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 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 set_row(int row)
Sets the start row to the indicated value.
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.
int get_num_components() const
Returns the number of color components for each texel of the texture image.
Definition: texture.I:835
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.
void set_prev_transform(const TransformState *transform, Thread *current_thread=Thread::get_current_thread())
Sets the transform that represents this node&#39;s "previous" position, one frame ago, for the purposes of detecting motion for accurate collision calculations.
Definition: pandaNode.cxx:1306
int add_blend(const TransformBlend &blend)
Adds a new blend to the table, and returns its index number.
VertexTransform * egg_to_transform(EggNode *egg_node)
Returns a JointVertexTransform suitable for applying the animation associated with the given egg node...
Converts an EggGroup hierarchy, beginning with a group with <Dart> set, to a character node with join...
void clear_vertices()
Resets the vertices of the portal to the empty list.
Definition: portalNode.I:130
void set_border_color(const LColor &color)
Specifies the solid color of the texture&#39;s border.
Definition: texture.I:1008
This is a 3-by-3 transform matrix.
Definition: lmatrix.h:110
bool get_bface_flag() const
Retrieves the backfacing flag of the polygon.
Definition: eggPrimitive.I:301
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
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2486
int get_multitexture_sort() const
Returns an integer that represents the depth to which this texture is layered on all other textures i...
Definition: eggTexture.I:1071
void get_aux_names(vector_string &aux_names) const
Returns the list of auxiliary data names that are defined by any vertices in the pool.
This corresponds to a <SwitchCondition> 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.
void add_transform(const VertexTransform *transform, PN_stdfloat weight)
Adds a new transform to the blend.
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:541
A collection of vertices.
Definition: eggVertexPool.h:46
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
void set_transform(const TransformState *transform, Thread *current_thread=Thread::get_current_thread())
Sets the transform that will be applied to this node and below.
Definition: pandaNode.cxx:1267
bool has_uv_name() const
Returns true if a texcoord name has been explicitly specified for this texture, false otherwise...
Definition: eggTexture.I:693
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 set_wrap_w(WrapMode wrap)
The W wrap direction is only used for 3-d textures.
Definition: texture.I:935
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
EggVertex * get_vertex(int index) const
Returns a particular index based on its index number.
Definition: eggPrimitive.I:466
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:642
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
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
int get_num_group_refs() const
Returns the number of <Ref> entries within this group.
Definition: eggGroup.cxx:863
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
const LMatrix4d & get_component_mat4(int n) const
Returns the 4x4 matrix associated with the nth component.
Definition: eggTransform.I:358
static TextureStage * get_stage(TextureStage *temp)
Returns a TextureStage pointer that represents the same TextureStage described by temp...
This represents the <Transform> 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...
const LVecBase4d & get_aux() const
Returns the auxiliary data quadruple.
Definition: eggVertexAux.I:32
A type of group node that holds related subnodes.
Definition: eggBin.h:30
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
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
bool has_alpha_filename() const
Returns true if a separate file for the alpha component has been applied, false otherwise.
Definition: eggTexture.I:846