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