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  // Piggy-back the desired transform to apply onto the node, since we can't
1818  // break the ABI in 1.10.
1819  node->set_transform(TransformState::make_mat(LCAST(PN_stdfloat, egg_group->get_vertex_to_node())));
1820  make_collision_solids(egg_group, egg_group, (CollisionNode *)node.p());
1821  node->clear_transform();
1822 
1823  if ((egg_group->get_collide_flags() & EggGroup::CF_keep) != 0) {
1824  // If we also specified to keep the geometry, continue the traversal.
1825  // In this case, we create a new PandaNode to be the parent of the
1826  // visible geometry and the collision geometry.
1827  PandaNode *combined = new PandaNode("");
1828  parent->add_child(combined);
1829  combined->add_child(node);
1830  node = combined;
1831 
1832  EggGroup::const_iterator ci;
1833  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1834  make_node(*ci, combined);
1835  }
1836  }
1837 
1838  node = create_group_arc(egg_group, parent, node);
1839  return node;
1840 
1841  } else if (egg_group->get_portal_flag()) {
1842  // Create a portal instead of a regular polyset. Scan the children of
1843  // this node looking for a polygon, similar to the collision polygon case,
1844  // above.
1845  PortalNode *pnode = new PortalNode(egg_group->get_name());
1846  node = pnode;
1847 
1848  set_portal_polygon(egg_group, pnode);
1849  if (pnode->get_num_vertices() == 0) {
1850  egg2pg_cat.warning()
1851  << "Portal " << egg_group->get_name() << " has no vertices!\n";
1852  }
1853 
1854  } else if (egg_group->get_occluder_flag()) {
1855  // Create an occluder instead of a regular polyset. Scan the children of
1856  // this node looking for a polygon, the same as the portal polygon case,
1857  // above.
1858  OccluderNode *pnode = new OccluderNode(egg_group->get_name());
1859  node = pnode;
1860 
1861  set_occluder_polygon(egg_group, pnode);
1862  if (pnode->get_num_vertices() == 0) {
1863  egg2pg_cat.warning()
1864  << "Occluder " << egg_group->get_name() << " has no vertices!\n";
1865  }
1866 
1867  } else if (egg_group->get_polylight_flag()) {
1868  // Create a polylight instead of a regular polyset. use make_sphere to
1869  // get the center, radius and color egg2pg_cat.debug() << "polylight
1870  // node\n";
1871  LPoint3 center;
1872  LColor color;
1873  PN_stdfloat radius;
1874 
1875  if (!make_sphere(egg_group, EggGroup::CF_none, center, radius, color)) {
1876  egg2pg_cat.warning()
1877  << "Polylight " << egg_group->get_name() << " make_sphere failed!\n";
1878  }
1879  PolylightNode *pnode = new PolylightNode(egg_group->get_name());
1880  pnode->set_pos(center);
1881  pnode->set_color(color);
1882  pnode->set_radius(radius);
1883 
1884  pnode->xform(LCAST(PN_stdfloat, egg_group->get_vertex_to_node()));
1885 
1886  node = pnode;
1887 
1888  } else if (egg_group->get_switch_flag()) {
1889  if (egg_group->get_switch_fps() != 0.0) {
1890  // Create a sequence node.
1891  node = new SequenceNode(egg_group->get_name());
1892  ((SequenceNode *)node.p())->set_frame_rate(egg_group->get_switch_fps());
1893  _sequences.insert(node);
1894  } else {
1895  // Create a switch node.
1896  node = new SwitchNode(egg_group->get_name());
1897  }
1898 
1899  EggGroup::const_iterator ci;
1900  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1901  make_node(*ci, node);
1902  }
1903  } else if (egg_group->has_scrolling_uvs()) {
1904  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());
1905 
1906  EggGroup::const_iterator ci;
1907  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1908  make_node(*ci, node);
1909  }
1910 
1911  } else if (egg_group->get_model_flag() || egg_group->has_dcs_type()) {
1912  // A model or DCS flag; create a model node.
1913  node = new ModelNode(egg_group->get_name());
1914  switch (egg_group->get_dcs_type()) {
1915  case EggGroup::DC_net:
1916  DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_net);
1917  break;
1918 
1919  case EggGroup::DC_no_touch:
1920  DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_no_touch);
1921  break;
1922 
1923  case EggGroup::DC_local:
1924  case EggGroup::DC_default:
1925  DCAST(ModelNode, node)->set_preserve_transform(ModelNode::PT_local);
1926  break;
1927 
1928  case EggGroup::DC_none:
1929  case EggGroup::DC_unspecified:
1930  break;
1931  }
1932 
1933  EggGroup::const_iterator ci;
1934  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1935  make_node(*ci, node);
1936  }
1937 
1938  } else {
1939  // A normal group; just create a normal node, and traverse. But if all of
1940  // the children of this group are polysets, anticipate this for the
1941  // benefit of smaller grouping, and create a single GeomNode for all of
1942  // the children.
1943  bool all_polysets = false;
1944  bool any_hidden = false;
1945 
1946  // We don't want to ever create a GeomNode under a "decal" flag, since
1947  // that can confuse the decal reparenting.
1948  if (!egg_group->determine_decal()) {
1949  check_for_polysets(egg_group, all_polysets, any_hidden);
1950  }
1951 
1952  if (all_polysets && !any_hidden) {
1953  node = new GeomNode(egg_group->get_name());
1954  } else {
1955  node = new PandaNode(egg_group->get_name());
1956  }
1957 
1958  EggGroup::const_iterator ci;
1959  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
1960  make_node(*ci, node);
1961  }
1962  }
1963 
1964  if (node == nullptr) {
1965  return nullptr;
1966  }
1967 
1968  // Associate any instances with this node.
1969  int num_group_refs = egg_group->get_num_group_refs();
1970  for (int gri = 0; gri < num_group_refs; ++gri) {
1971  EggGroup *group_ref = egg_group->get_group_ref(gri);
1972  Groups::const_iterator gi = _groups.find(group_ref);
1973  if (gi != _groups.end()) {
1974  PandaNode *node_ref = (*gi).second;
1975  node->add_child(node_ref);
1976  }
1977  }
1978 
1979  _groups[egg_group] = node;
1980  return create_group_arc(egg_group, parent, node);
1981 }
1982 
1983 /**
1984  * Creates the arc parenting a new group to the scene graph, and applies any
1985  * relevant attribs to the arc according to the EggGroup node that inspired
1986  * the group.
1987  */
1988 PandaNode *EggLoader::
1989 create_group_arc(EggGroup *egg_group, PandaNode *parent, PandaNode *node) {
1990  parent->add_child(node);
1991 
1992  // If the group had a transform, apply it to the node.
1993  if (egg_group->has_transform()) {
1994  CPT(TransformState) transform = make_transform(egg_group);
1995  node->set_transform(transform);
1996  node->set_prev_transform(transform);
1997  }
1998 
1999  // If the group has a billboard flag, apply that.
2000  switch (egg_group->get_billboard_type()) {
2001  case EggGroup::BT_point_camera_relative:
2002  node->set_effect(BillboardEffect::make_point_eye());
2003  break;
2004 
2005  case EggGroup::BT_point_world_relative:
2006  node->set_effect(BillboardEffect::make_point_world());
2007  break;
2008 
2009  case EggGroup::BT_axis:
2010  node->set_effect(BillboardEffect::make_axis());
2011  break;
2012 
2013  case EggGroup::BT_none:
2014  break;
2015  }
2016 
2017  if (egg_group->get_decal_flag()) {
2018  if (egg_ignore_decals) {
2019  egg2pg_cat.error()
2020  << "Ignoring decal flag on " << egg_group->get_name() << "\n";
2021  _error = true;
2022  }
2023 
2024  // If the group has the "decal" flag set, it means that all of the
2025  // descendant groups will be decaled onto the geometry within this group.
2026  // This means we'll need to reparent things a bit afterward.
2027  _decals.insert(node);
2028  }
2029 
2030  // Copy all the tags from the group onto the node.
2031  EggGroup::TagData::const_iterator ti;
2032  for (ti = egg_group->tag_begin(); ti != egg_group->tag_end(); ++ti) {
2033  node->set_tag((*ti).first, (*ti).second);
2034  }
2035 
2036  if (egg_group->get_blend_mode() != EggGroup::BM_unspecified &&
2037  egg_group->get_blend_mode() != EggGroup::BM_none) {
2038  // Apply a ColorBlendAttrib to the group.
2039  ColorBlendAttrib::Mode mode = get_color_blend_mode(egg_group->get_blend_mode());
2040  ColorBlendAttrib::Operand a = get_color_blend_operand(egg_group->get_blend_operand_a());
2041  ColorBlendAttrib::Operand b = get_color_blend_operand(egg_group->get_blend_operand_b());
2042  LColor color = egg_group->get_blend_color();
2043  node->set_attrib(ColorBlendAttrib::make(mode, a, b, color));
2044  }
2045 
2046  // If the group specified some property that should propagate down to the
2047  // leaves, we have to remember this node and apply the property later, after
2048  // we've created the actual geometry.
2050  if (egg_group->has_collide_mask()) {
2051  def._from_collide_mask = egg_group->get_collide_mask();
2052  def._into_collide_mask = egg_group->get_collide_mask();
2053  def._flags |=
2054  DeferredNodeProperty::F_has_from_collide_mask |
2055  DeferredNodeProperty::F_has_into_collide_mask;
2056  }
2057  if (egg_group->has_from_collide_mask()) {
2058  def._from_collide_mask = egg_group->get_from_collide_mask();
2059  def._flags |= DeferredNodeProperty::F_has_from_collide_mask;
2060  }
2061  if (egg_group->has_into_collide_mask()) {
2062  def._into_collide_mask = egg_group->get_into_collide_mask();
2063  def._flags |= DeferredNodeProperty::F_has_into_collide_mask;
2064  }
2065 
2066  if (def._flags != 0) {
2067  _deferred_nodes[node] = def;
2068  }
2069 
2070  return node;
2071 }
2072 
2073 /**
2074  *
2075  */
2076 PandaNode *EggLoader::
2077 make_node(EggTable *egg_table, PandaNode *parent) {
2078  if (egg_table->get_table_type() != EggTable::TT_bundle) {
2079  // We only do anything with bundles. Isolated tables are treated as
2080  // ordinary groups.
2081  return make_node(DCAST(EggGroupNode, egg_table), parent);
2082  }
2083 
2084  // It's an actual bundle, so make an AnimBundle from it and its descendants.
2085  AnimBundleMaker bundle_maker(egg_table);
2086  AnimBundleNode *node = bundle_maker.make_node();
2087  parent->add_child(node);
2088  return node;
2089 }
2090 
2091 
2092 /**
2093  *
2094  */
2095 PandaNode *EggLoader::
2096 make_node(EggGroupNode *egg_group, PandaNode *parent) {
2097  PandaNode *node = new PandaNode(egg_group->get_name());
2098 
2099  EggGroupNode::const_iterator ci;
2100  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2101  make_node(*ci, node);
2102  }
2103 
2104  parent->add_child(node);
2105  return node;
2106 }
2107 
2108 /**
2109  * Sets all_polysets true if all of the children of this node represent a
2110  * polyset. Sets any_hidden true if any of those polysets are flagged hidden.
2111  */
2112 void EggLoader::
2113 check_for_polysets(EggGroup *egg_group, bool &all_polysets, bool &any_hidden) {
2114  all_polysets = (!egg_group->empty());
2115  any_hidden = false;
2116 
2117  EggGroup::const_iterator ci;
2118  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2119  if ((*ci)->is_of_type(EggBin::get_class_type())) {
2120  EggBin *egg_bin = DCAST(EggBin, (*ci));
2121  if (egg_bin->get_bin_number() == EggBinner::BN_polyset) {
2122  // We know that all of the primitives in the bin have the same render
2123  // state, so we can get that information from the first primitive.
2124  EggGroup::const_iterator bci = egg_bin->begin();
2125  nassertv(bci != egg_bin->end());
2126  const EggPrimitive *first_prim;
2127  DCAST_INTO_V(first_prim, (*bci));
2128  const EggRenderState *render_state;
2129  DCAST_INTO_V(render_state, first_prim->get_user_data(EggRenderState::get_class_type()));
2130 
2131  if (render_state->_hidden) {
2132  any_hidden = true;
2133  }
2134  } else {
2135  all_polysets = false;
2136  return;
2137  }
2138  } else if ((*ci)->is_of_type(EggGroup::get_class_type())) {
2139  // Other kinds of children, like vertex pools, comments, textures, etc.,
2140  // are ignored; but groups indicate more nodes, so if we find a nested
2141  // group it means we're not all polysets.
2142  all_polysets = false;
2143  return;
2144  }
2145  }
2146 }
2147 
2148 /**
2149  * Creates a GeomVertexData structure from the vertex pool, for the indicated
2150  * transform space. If a GeomVertexData has already been created for this
2151  * transform, just returns it.
2152  */
2153 PT(GeomVertexData) EggLoader::
2154 make_vertex_data(const EggRenderState *render_state,
2155  EggVertexPool *vertex_pool, EggNode *primitive_home,
2156  const LMatrix4d &transform, TransformBlendTable *blend_table,
2157  bool is_dynamic, CharacterMaker *character_maker,
2158  bool ignore_color) {
2159  VertexPoolTransform vpt;
2160  vpt._vertex_pool = vertex_pool;
2161  vpt._bake_in_uvs = render_state->_bake_in_uvs;
2162  vpt._transform = transform;
2163 
2164  VertexPoolData::iterator di;
2165  di = _vertex_pool_data.find(vpt);
2166  if (di != _vertex_pool_data.end()) {
2167  return (*di).second;
2168  }
2169 
2170  PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat;
2171  array_format->add_column
2172  (InternalName::get_vertex(), vertex_pool->get_num_dimensions(),
2173  Geom::NT_stdfloat, Geom::C_point);
2174 
2175  if (vertex_pool->has_normals()) {
2176  array_format->add_column
2177  (InternalName::get_normal(), 3,
2178  Geom::NT_stdfloat, Geom::C_normal);
2179  }
2180 
2181  if (!ignore_color) {
2182  // Let's not use Direct3D-style colors on platforms where we only have
2183  // OpenGL anyway.
2184 #ifdef _WIN32
2185  array_format->add_column(InternalName::get_color(), 1,
2186  Geom::NT_packed_dabc, Geom::C_color);
2187 #else
2188  array_format->add_column(InternalName::get_color(), 4,
2189  Geom::NT_uint8, Geom::C_color);
2190 #endif
2191  }
2192 
2193  vector_string uv_names, uvw_names, tbn_names;
2194  vertex_pool->get_uv_names(uv_names, uvw_names, tbn_names);
2195  vector_string::const_iterator ni;
2196  for (ni = uv_names.begin(); ni != uv_names.end(); ++ni) {
2197  string name = (*ni);
2198 
2199  PT(InternalName) iname = InternalName::get_texcoord_name(name);
2200 
2201  if (find(uvw_names.begin(), uvw_names.end(), name) != uvw_names.end()) {
2202  // This one actually represents 3-d texture coordinates.
2203  array_format->add_column
2204  (iname, 3, Geom::NT_stdfloat, Geom::C_texcoord);
2205  } else {
2206  array_format->add_column
2207  (iname, 2, Geom::NT_stdfloat, Geom::C_texcoord);
2208  }
2209  }
2210  for (ni = tbn_names.begin(); ni != tbn_names.end(); ++ni) {
2211  string name = (*ni);
2212 
2213  PT(InternalName) iname_t = InternalName::get_tangent_name(name);
2214  PT(InternalName) iname_b = InternalName::get_binormal_name(name);
2215 
2216  array_format->add_column
2217  (iname_t, 3, Geom::NT_stdfloat, Geom::C_vector);
2218  array_format->add_column
2219  (iname_b, 3, Geom::NT_stdfloat, Geom::C_vector);
2220  }
2221 
2222  vector_string aux_names;
2223  vertex_pool->get_aux_names(aux_names);
2224  for (ni = aux_names.begin(); ni != aux_names.end(); ++ni) {
2225  string name = (*ni);
2226  PT(InternalName) iname = InternalName::make(name);
2227  array_format->add_column
2228  (iname, 4, Geom::NT_stdfloat, Geom::C_other);
2229  }
2230 
2231  PT(GeomVertexFormat) temp_format = new GeomVertexFormat(array_format);
2232 
2233  PT(SliderTable) slider_table;
2234  string name = _data->get_egg_filename().get_basename_wo_extension();
2235 
2236  if (is_dynamic) {
2237  // If it's a dynamic object, we need a TransformBlendTable and maybe a
2238  // SliderTable, and additional columns in the vertex data: one that
2239  // indexes into the blend table per vertex, and also one for each
2240  // different type of morph delta.
2241 
2242  // Tell the format that we're setting it up for Panda-based animation.
2243  GeomVertexAnimationSpec animation;
2244  animation.set_panda();
2245  temp_format->set_animation(animation);
2246 
2247  PT(GeomVertexArrayFormat) anim_array_format = new GeomVertexArrayFormat;
2248  anim_array_format->add_column
2249  (InternalName::get_transform_blend(), 1,
2250  Geom::NT_uint16, Geom::C_index, 0, 2);
2251  temp_format->add_array(anim_array_format);
2252 
2253  pmap<string, BitArray> slider_names;
2255  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
2256  EggVertex *vertex = (*vi);
2257 
2258  EggMorphVertexList::const_iterator mvi;
2259  for (mvi = vertex->_dxyzs.begin(); mvi != vertex->_dxyzs.end(); ++mvi) {
2260  slider_names[(*mvi).get_name()].set_bit(vertex->get_index());
2261  record_morph(anim_array_format, character_maker, (*mvi).get_name(),
2262  InternalName::get_vertex(), 3);
2263  }
2264  if (vertex->has_normal()) {
2265  EggMorphNormalList::const_iterator mni;
2266  for (mni = vertex->_dnormals.begin(); mni != vertex->_dnormals.end(); ++mni) {
2267  slider_names[(*mni).get_name()].set_bit(vertex->get_index());
2268  record_morph(anim_array_format, character_maker, (*mni).get_name(),
2269  InternalName::get_normal(), 3);
2270  }
2271  }
2272  if (!ignore_color && vertex->has_color()) {
2273  EggMorphColorList::const_iterator mci;
2274  for (mci = vertex->_drgbas.begin(); mci != vertex->_drgbas.end(); ++mci) {
2275  slider_names[(*mci).get_name()].set_bit(vertex->get_index());
2276  record_morph(anim_array_format, character_maker, (*mci).get_name(),
2277  InternalName::get_color(), 4);
2278  }
2279  }
2281  for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
2282  EggVertexUV *egg_uv = (*uvi);
2283  string name = egg_uv->get_name();
2284  bool has_w = (find(uvw_names.begin(), uvw_names.end(), name) != uvw_names.end());
2285  PT(InternalName) iname = InternalName::get_texcoord_name(name);
2286 
2287  EggMorphTexCoordList::const_iterator mti;
2288  for (mti = egg_uv->_duvs.begin(); mti != egg_uv->_duvs.end(); ++mti) {
2289  slider_names[(*mti).get_name()].set_bit(vertex->get_index());
2290  record_morph(anim_array_format, character_maker, (*mti).get_name(),
2291  iname, has_w ? 3 : 2);
2292  }
2293  }
2294  }
2295 
2296  if (!slider_names.empty()) {
2297  // If we have any sliders at all, create a table for them.
2298 
2299  slider_table = new SliderTable;
2300 
2302  for (si = slider_names.begin(); si != slider_names.end(); ++si) {
2303  PT(VertexSlider) slider = character_maker->egg_to_slider((*si).first);
2304  slider_table->add_slider(slider, (*si).second);
2305  }
2306  }
2307 
2308  // We'll also assign the character name to the vertex data, so it will
2309  // show up in PStats.
2310  name = character_maker->get_name();
2311  }
2312 
2313  temp_format->maybe_align_columns_for_animation();
2314 
2315  CPT(GeomVertexFormat) format =
2316  GeomVertexFormat::register_format(temp_format);
2317 
2318  // Now create a new GeomVertexData using the indicated format. It is
2319  // actually correct to create it with UH_static even though it represents a
2320  // dynamic object, because the vertex data itself won't be changing--just
2321  // the result of applying the animation is dynamic.
2322  PT(GeomVertexData) vertex_data =
2323  new GeomVertexData(name, format, Geom::UH_static);
2324  vertex_data->reserve_num_rows(vertex_pool->size());
2325 
2326  vertex_data->set_transform_blend_table(blend_table);
2327  if (slider_table != nullptr) {
2328  vertex_data->set_slider_table(SliderTable::register_table(slider_table));
2329  }
2330 
2331  // And fill in the data from the vertex pool.
2333  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
2334  GeomVertexWriter gvw(vertex_data);
2335  EggVertex *vertex = (*vi);
2336  gvw.set_row(vertex->get_index());
2337 
2338  gvw.set_column(InternalName::get_vertex());
2339  gvw.add_data4d(vertex->get_pos4() * transform);
2340 
2341  if (is_dynamic) {
2342  EggMorphVertexList::const_iterator mvi;
2343  for (mvi = vertex->_dxyzs.begin(); mvi != vertex->_dxyzs.end(); ++mvi) {
2344  const EggMorphVertex &morph = (*mvi);
2345  CPT(InternalName) delta_name =
2346  InternalName::get_morph(InternalName::get_vertex(), morph.get_name());
2347  gvw.set_column(delta_name);
2348  gvw.add_data3d(morph.get_offset() * transform);
2349  }
2350  }
2351 
2352  if (vertex->has_normal()) {
2353  gvw.set_column(InternalName::get_normal());
2354  LNormald orig_normal = vertex->get_normal();
2355  LNormald transformed_normal = normalize(orig_normal * transform);
2356  gvw.add_data3d(transformed_normal);
2357 
2358  if (is_dynamic) {
2359  EggMorphNormalList::const_iterator mni;
2360  for (mni = vertex->_dnormals.begin(); mni != vertex->_dnormals.end(); ++mni) {
2361  const EggMorphNormal &morph = (*mni);
2362  CPT(InternalName) delta_name =
2363  InternalName::get_morph(InternalName::get_normal(), morph.get_name());
2364  gvw.set_column(delta_name);
2365  LNormald morphed_normal = orig_normal + morph.get_offset();
2366  LNormald transformed_morphed_normal = normalize(morphed_normal * transform);
2367  LVector3d delta = transformed_morphed_normal - transformed_normal;
2368  gvw.add_data3d(delta);
2369  }
2370  }
2371  }
2372 
2373  if (!ignore_color && vertex->has_color()) {
2374  gvw.set_column(InternalName::get_color());
2375  gvw.add_data4(vertex->get_color());
2376 
2377  if (is_dynamic) {
2378  EggMorphColorList::const_iterator mci;
2379  for (mci = vertex->_drgbas.begin(); mci != vertex->_drgbas.end(); ++mci) {
2380  const EggMorphColor &morph = (*mci);
2381  CPT(InternalName) delta_name =
2382  InternalName::get_morph(InternalName::get_color(), morph.get_name());
2383  gvw.set_column(delta_name);
2384  gvw.add_data4(morph.get_offset());
2385  }
2386  }
2387  }
2388 
2390  for (uvi = vertex->uv_begin(); uvi != vertex->uv_end(); ++uvi) {
2391  EggVertexUV *egg_uv = (*uvi);
2392  LTexCoord3d orig_uvw = egg_uv->get_uvw();
2393  LTexCoord3d uvw = egg_uv->get_uvw();
2394 
2395  string name = egg_uv->get_name();
2396  PT(InternalName) iname = InternalName::get_texcoord_name(name);
2397  gvw.set_column(iname);
2398 
2399  BakeInUVs::const_iterator buv = render_state->_bake_in_uvs.find(iname);
2400  if (buv != render_state->_bake_in_uvs.end()) {
2401  // If we are to bake in a texture matrix, do so now.
2402  uvw = uvw * (*buv).second->get_transform3d();
2403  }
2404 
2405  gvw.set_data3d(uvw);
2406 
2407  if (is_dynamic) {
2408  EggMorphTexCoordList::const_iterator mti;
2409  for (mti = egg_uv->_duvs.begin(); mti != egg_uv->_duvs.end(); ++mti) {
2410  const EggMorphTexCoord &morph = (*mti);
2411  CPT(InternalName) delta_name =
2412  InternalName::get_morph(iname, morph.get_name());
2413  gvw.set_column(delta_name);
2414  LTexCoord3d duvw = morph.get_offset();
2415  if (buv != render_state->_bake_in_uvs.end()) {
2416  LTexCoord3d new_uvw = orig_uvw + duvw;
2417  duvw = (new_uvw * (*buv).second->get_transform3d()) - uvw;
2418  }
2419 
2420  gvw.add_data3d(duvw);
2421  }
2422  }
2423 
2424  // Also add the tangent and binormal, if present.
2425  if (egg_uv->has_tangent() && egg_uv->has_binormal()) {
2426  PT(InternalName) iname = InternalName::get_tangent_name(name);
2427  gvw.set_column(iname);
2428  if (gvw.has_column()) {
2429  LVector3d tangent = egg_uv->get_tangent();
2430  LVector3d binormal = egg_uv->get_binormal();
2431  gvw.add_data3d(tangent);
2432  gvw.set_column(InternalName::get_binormal_name(name));
2433  gvw.add_data3d(binormal);
2434  }
2435  }
2436  }
2437 
2439  for (auxi = vertex->aux_begin(); auxi != vertex->aux_end(); ++auxi) {
2440  EggVertexAux *egg_aux = (*auxi);
2441  LVecBase4d aux = egg_aux->get_aux();
2442 
2443  string name = egg_aux->get_name();
2444  PT(InternalName) iname = InternalName::make(name);
2445  gvw.set_column(iname);
2446 
2447  gvw.set_data4d(aux);
2448  }
2449 
2450  if (is_dynamic) {
2451  int table_index = vertex->get_external_index();
2452  gvw.set_column(InternalName::get_transform_blend());
2453  gvw.set_data1i(table_index);
2454  }
2455  }
2456 
2457  bool inserted = _vertex_pool_data.insert
2458  (VertexPoolData::value_type(vpt, vertex_data)).second;
2459  nassertr(inserted, vertex_data);
2460 
2462  return vertex_data;
2463 }
2464 
2465 /**
2466  *
2467  */
2468 PT(TransformBlendTable) EggLoader::
2469 make_blend_table(EggVertexPool *vertex_pool, EggNode *primitive_home,
2470  CharacterMaker *character_maker) {
2471  PT(TransformBlendTable) blend_table;
2472  blend_table = new TransformBlendTable;
2473  blend_table->set_rows(SparseArray::lower_on(vertex_pool->size()));
2474 
2476  for (vi = vertex_pool->begin(); vi != vertex_pool->end(); ++vi) {
2477  EggVertex *vertex = (*vi);
2478 
2479  // Figure out the transforms affecting this particular vertex.
2480  TransformBlend blend;
2481  if (vertex->gref_size() == 0) {
2482  // If the vertex has no explicit membership, it belongs right where it
2483  // is.
2484  PT(VertexTransform) vt = character_maker->egg_to_transform(primitive_home);
2485  nassertr(vt != nullptr, nullptr);
2486  blend.add_transform(vt, 1.0f);
2487  } else {
2488  // If the vertex does have an explicit membership, ignore its parentage
2489  // and assign it where it wants to be.
2490  double quantize = egg_vertex_membership_quantize;
2491  EggVertex::GroupRef::const_iterator gri;
2492  for (gri = vertex->gref_begin(); gri != vertex->gref_end(); ++gri) {
2493  EggGroup *egg_joint = (*gri);
2494  double membership = egg_joint->get_vertex_membership(vertex);
2495  if (quantize != 0.0) {
2496  membership = cfloor(membership / quantize + 0.5) * quantize;
2497  }
2498 
2499  PT(VertexTransform) vt = character_maker->egg_to_transform(egg_joint);
2500  nassertr(vt != nullptr, nullptr);
2501  blend.add_transform(vt, membership);
2502  }
2503  }
2504  if (egg_vertex_max_num_joints >= 0) {
2505  blend.limit_transforms(egg_vertex_max_num_joints);
2506  }
2507  blend.normalize_weights();
2508 
2509  int table_index = blend_table->add_blend(blend);
2510 
2511  // We take advantage of the "external index" field of the EggVertex to
2512  // temporarily store the transform blend index.
2513  vertex->set_external_index(table_index);
2514  }
2515 
2516  return blend_table;
2517 }
2518 
2519 /**
2520  *
2521  */
2522 void EggLoader::
2523 record_morph(GeomVertexArrayFormat *array_format,
2524  CharacterMaker *character_maker,
2525  const string &morph_name, InternalName *column_name,
2526  int num_components) {
2527  PT(InternalName) delta_name =
2528  InternalName::get_morph(column_name, morph_name);
2529  if (!array_format->has_column(delta_name)) {
2530  array_format->add_column
2531  (delta_name, num_components,
2532  Geom::NT_stdfloat, Geom::C_morph_delta);
2533  }
2534 }
2535 
2536 /**
2537  * Creates a GeomPrimitive corresponding to the indicated EggPrimitive, and
2538  * adds it to the set.
2539  */
2540 void EggLoader::
2541 make_primitive(const EggRenderState *render_state, EggPrimitive *egg_prim,
2542  EggLoader::UniquePrimitives &unique_primitives,
2543  EggLoader::Primitives &primitives,
2544  bool has_overall_color, const LColor &overall_color) {
2545  PT(GeomPrimitive) primitive;
2546  if (egg_prim->is_of_type(EggPolygon::get_class_type())) {
2547  if (egg_prim->size() == 3) {
2548  primitive = new GeomTriangles(Geom::UH_static);
2549  }
2550 
2551  } else if (egg_prim->is_of_type(EggTriangleStrip::get_class_type())) {
2552  primitive = new GeomTristrips(Geom::UH_static);
2553 
2554  } else if (egg_prim->is_of_type(EggTriangleFan::get_class_type())) {
2555  primitive = new GeomTrifans(Geom::UH_static);
2556 
2557  } else if (egg_prim->is_of_type(EggLine::get_class_type())) {
2558  if (egg_prim->size() == 2) {
2559  primitive = new GeomLines(Geom::UH_static);
2560  } else {
2561  primitive = new GeomLinestrips(Geom::UH_static);
2562  }
2563 
2564  } else if (egg_prim->is_of_type(EggPoint::get_class_type())) {
2565  primitive = new GeomPoints(Geom::UH_static);
2566 
2567  } else if (egg_prim->is_of_type(EggPatch::get_class_type())) {
2568  int num_vertices = egg_prim->size();
2569  primitive = new GeomPatches(num_vertices, Geom::UH_static);
2570  }
2571 
2572  if (primitive == nullptr) {
2573  // Don't know how to make this kind of primitive.
2574  egg2pg_cat.warning()
2575  << "Ignoring " << egg_prim->get_type() << "\n";
2576  return;
2577  }
2578 
2579  if (render_state->_flat_shaded) {
2580  primitive->set_shade_model(GeomPrimitive::SM_flat_first_vertex);
2581 
2582  } else if (egg_prim->get_shading() == EggPrimitive::S_overall) {
2583  primitive->set_shade_model(GeomPrimitive::SM_uniform);
2584 
2585  } else {
2586  primitive->set_shade_model(GeomPrimitive::SM_smooth);
2587  }
2588 
2589  // Insert the primitive into the set, but if we already have a primitive of
2590  // that type, reset the pointer to that one instead.
2591  PrimitiveUnifier pu(primitive);
2592  std::pair<UniquePrimitives::iterator, bool> result =
2593  unique_primitives.insert(UniquePrimitives::value_type(pu, primitive));
2594 
2595  if (result.second) {
2596  // This was the first primitive of this type. Store it.
2597  primitives.push_back(primitive);
2598 
2599  if (egg2pg_cat.is_debug()) {
2600  egg2pg_cat.debug()
2601  << "First primitive of type " << primitive->get_type()
2602  << ": " << primitive << "\n";
2603  }
2604  }
2605 
2606  GeomPrimitive *orig_prim = (*result.first).second;
2607 
2608  // Make sure we don't try to put more than egg_max_indices into any one
2609  // GeomPrimitive.
2610  if (orig_prim->get_num_vertices() + egg_prim->size() <= (unsigned int)egg_max_indices) {
2611  primitive = orig_prim;
2612 
2613  } else if (orig_prim != primitive) {
2614  // If the old primitive is full, keep the new primitive from now on.
2615  (*result.first).second = primitive;
2616 
2617  if (egg2pg_cat.is_debug()) {
2618  egg2pg_cat.debug()
2619  << "Next primitive of type " << primitive->get_type()
2620  << ": " << primitive << "\n";
2621  }
2622  primitives.push_back(primitive);
2623  }
2624 
2625  // Now add the vertices.
2626  EggPrimitive::const_iterator vi;
2627  for (vi = egg_prim->begin(); vi != egg_prim->end(); ++vi) {
2628  primitive->add_vertex((*vi)->get_index());
2629  }
2630  primitive->close_primitive();
2631 }
2632 
2633 /**
2634  * Defines the PortalNode from the first polygon found within this group.
2635  */
2636 void EggLoader::
2637 set_portal_polygon(EggGroup *egg_group, PortalNode *pnode) {
2638  pnode->clear_vertices();
2639 
2640  PT(EggPolygon) poly = find_first_polygon(egg_group);
2641  if (poly != nullptr) {
2642  LMatrix4d mat = poly->get_vertex_to_node();
2643 
2644  EggPolygon::const_iterator vi;
2645  for (vi = poly->begin(); vi != poly->end(); ++vi) {
2646  LVertexd vert = (*vi)->get_pos3() * mat;
2647  pnode->add_vertex(LCAST(PN_stdfloat, vert));
2648  }
2649  }
2650 }
2651 
2652 /**
2653  * Defines the OccluderNode from the first polygon found within this group.
2654  */
2655 void EggLoader::
2656 set_occluder_polygon(EggGroup *egg_group, OccluderNode *pnode) {
2657  PT(EggPolygon) poly = find_first_polygon(egg_group);
2658  if (poly != nullptr) {
2659  if (poly->size() != 4) {
2660  egg2pg_cat.error()
2661  << "Invalid number of vertices for " << egg_group->get_name() << "\n";
2662  } else {
2663  LMatrix4d mat = poly->get_vertex_to_node();
2664 
2665  LPoint3d v0 = (*poly)[0]->get_pos3() * mat;
2666  LPoint3d v1 = (*poly)[1]->get_pos3() * mat;
2667  LPoint3d v2 = (*poly)[2]->get_pos3() * mat;
2668  LPoint3d v3 = (*poly)[3]->get_pos3() * mat;
2669  pnode->set_vertices(LCAST(PN_stdfloat, v0),
2670  LCAST(PN_stdfloat, v1),
2671  LCAST(PN_stdfloat, v2),
2672  LCAST(PN_stdfloat, v3));
2673 
2674  if (poly->get_bface_flag()) {
2675  pnode->set_double_sided(true);
2676  }
2677  }
2678  }
2679 }
2680 
2681 /**
2682  * Returns the first EggPolygon found at or below the indicated node.
2683  */
2684 PT(EggPolygon) EggLoader::
2685 find_first_polygon(EggGroup *egg_group) {
2686  // Does this group have any polygons?
2687  EggGroup::const_iterator ci;
2688  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2689  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
2690  // Yes! Return the polygon.
2691  return DCAST(EggPolygon, (*ci));
2692  }
2693  }
2694 
2695  // Well, the group had no polygons; look for a child group that does.
2696  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2697  if ((*ci)->is_of_type(EggGroup::get_class_type())) {
2698  EggGroup *child_group = DCAST(EggGroup, *ci);
2699  PT(EggPolygon) found = find_first_polygon(child_group);
2700  if (found != nullptr) {
2701  return found;
2702  }
2703  }
2704  }
2705 
2706  // We got nothing.
2707  return nullptr;
2708 }
2709 
2710 /**
2711  * Creates a single generic Sphere corresponding to the polygons associated
2712  * with this group. This sphere is used by make_collision_sphere and
2713  * Polylight sphere. It could be used for other spheres.
2714  */
2715 bool EggLoader::
2716 make_sphere(EggGroup *egg_group, EggGroup::CollideFlags flags,
2717  LPoint3 &center, PN_stdfloat &radius, LColor &color) {
2718  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2719  if (geom_group != nullptr) {
2720  // Collect all of the vertices.
2721  pset<EggVertex *> vertices;
2722 
2723  EggGroup::const_iterator ci;
2724  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2725  if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
2726  EggPrimitive *prim = DCAST(EggPrimitive, *ci);
2727  EggPrimitive::const_iterator pi;
2728  for (pi = prim->begin(); pi != prim->end(); ++pi) {
2729  vertices.insert(*pi);
2730  }
2731  }
2732  }
2733 
2734  // Now average together all of the vertices to get a center.
2735  int num_vertices = 0;
2736  LPoint3d d_center(0.0, 0.0, 0.0);
2738 
2739  for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
2740  EggVertex *vtx = (*vi);
2741  d_center += vtx->get_pos3();
2742  num_vertices++;
2743  }
2744 
2745  if (num_vertices > 0) {
2746  d_center /= (double)num_vertices;
2747  // egg2pg_cat.debug() << "make_sphere d_center: " << d_center << "\n";
2748 
2749  // And the furthest vertex determines the radius.
2750  double radius2 = 0.0;
2751  for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
2752  EggVertex *vtx = (*vi);
2753  LPoint3d p3 = vtx->get_pos3();
2754  LVector3d v = p3 - d_center;
2755  radius2 = max(radius2, v.length_squared());
2756  }
2757 
2758  center = LCAST(PN_stdfloat, d_center);
2759  radius = sqrtf(radius2);
2760 
2761  // egg2pg_cat.debug() << "make_sphere radius: " << radius << "\n";
2762  vi = vertices.begin();
2763  EggVertex *clr_vtx = (*vi);
2764  color = clr_vtx->get_color();
2765  return true;
2766  }
2767  }
2768  return false;
2769 }
2770 
2771 /**
2772  * Creates a single generic Box corresponding to the polygons associated with
2773  * this group. This box is used by make_collision_box.
2774  */
2775 bool EggLoader::
2776 make_box(EggGroup *egg_group, EggGroup::CollideFlags flags,
2777  const LMatrix4 &xform, LPoint3 &min_p, LPoint3 &max_p) {
2778  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2779  if (geom_group != nullptr) {
2780  // Collect all of the vertices.
2781  pset<EggVertex *> vertices;
2782 
2783  EggGroup::const_iterator ci;
2784  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2785  if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
2786  EggPrimitive *prim = DCAST(EggPrimitive, *ci);
2787  EggPrimitive::const_iterator pi;
2788  for (pi = prim->begin(); pi != prim->end(); ++pi) {
2789  vertices.insert(*pi);
2790  }
2791  }
2792  }
2793 
2794  // Now find the minmax points
2796  vi = vertices.begin();
2797 
2798  if (vi == vertices.end()) {
2799  // No vertices, no bounding box.
2800  min_p.set(0, 0, 0);
2801  max_p.set(0, 0, 0);
2802  return false;
2803  }
2804 
2805  EggVertex *vertex = (*vi);
2806  LPoint3 min_pd = LCAST(PN_stdfloat, vertex->get_pos3()) * xform;
2807  LPoint3 max_pd = min_pd;
2808 
2809  for (++vi; vi != vertices.end(); ++vi) {
2810  vertex = (*vi);
2811  LPoint3 pos = LCAST(PN_stdfloat, vertex->get_pos3()) * xform;
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 = min_pd;
2821  max_p = max_pd;
2822  return (min_pd != max_pd);
2823  }
2824  return false;
2825 }
2826 
2827 /**
2828  * Creates a single generic Box corresponding to the polygons associated with
2829  * this group. This box is used by make_collision_box.
2830  */
2831 bool EggLoader::
2832 make_box(EggGroup *egg_group, EggGroup::CollideFlags flags,
2833  LPoint3 &min_p, LPoint3 &max_p, LColor &color) {
2834 
2835  color.set(1.0, 1.0, 1.0, 1.0);
2836  return make_box(egg_group, flags, LMatrix4::ident_mat(), min_p, max_p);
2837 }
2838 
2839 /**
2840  * Creates CollisionSolids corresponding to the collision geometry indicated
2841  * at the given node and below.
2842  */
2843 void EggLoader::
2844 make_collision_solids(EggGroup *start_group, EggGroup *egg_group,
2845  CollisionNode *cnode) {
2846  if (egg_group->get_cs_type() != EggGroup::CST_none) {
2847  start_group = egg_group;
2848  }
2849 
2850  switch (start_group->get_cs_type()) {
2851  case EggGroup::CST_none:
2852  // No collision flags; do nothing. Don't even traverse further.
2853  return;
2854 
2855  case EggGroup::CST_plane:
2856  make_collision_plane(egg_group, cnode, start_group->get_collide_flags());
2857  break;
2858 
2859  case EggGroup::CST_polygon:
2860  make_collision_polygon(egg_group, cnode, start_group->get_collide_flags());
2861  break;
2862 
2863  case EggGroup::CST_polyset:
2864  make_collision_polyset(egg_group, cnode, start_group->get_collide_flags());
2865  break;
2866 
2867  case EggGroup::CST_sphere:
2868  make_collision_sphere(egg_group, cnode, start_group->get_collide_flags());
2869  break;
2870 
2871  case EggGroup::CST_box:
2872  make_collision_box(egg_group, cnode, start_group->get_collide_flags());
2873  break;
2874 
2875  case EggGroup::CST_inv_sphere:
2876  make_collision_inv_sphere(egg_group, cnode, start_group->get_collide_flags());
2877  break;
2878 
2879  case EggGroup::CST_tube:
2880  make_collision_capsule(egg_group, cnode, start_group->get_collide_flags());
2881  break;
2882 
2883  case EggGroup::CST_floor_mesh:
2884  make_collision_floor_mesh(egg_group, cnode, start_group->get_collide_flags());
2885  break;
2886  }
2887 
2888  if ((start_group->get_collide_flags() & EggGroup::CF_descend) != 0) {
2889  // Now pick up everything below.
2890  EggGroup::const_iterator ci;
2891  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
2892  if ((*ci)->is_of_type(EggGroup::get_class_type())) {
2893  make_collision_solids(start_group, DCAST(EggGroup, *ci), cnode);
2894  }
2895  }
2896  } else {
2897  egg2pg_cat.warning()
2898  << "Using <Collide> without 'descend' is deprecated. 'descend' "
2899  << "will become the default in a future version of Panda3D.\n";
2900  }
2901 }
2902 
2903 /**
2904  * Creates a single CollisionPlane corresponding to the first polygon
2905  * associated with this group.
2906  */
2907 void EggLoader::
2908 make_collision_plane(EggGroup *egg_group, CollisionNode *cnode,
2909  EggGroup::CollideFlags flags) {
2910  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2911  if (geom_group != nullptr) {
2912  EggGroup::const_iterator ci;
2913  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2914  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
2915  CollisionPlane *csplane =
2916  create_collision_plane(DCAST(EggPolygon, *ci), egg_group);
2917  if (csplane != nullptr) {
2918  apply_collision_flags(csplane, flags);
2919  csplane->xform(cnode->get_transform()->get_mat());
2920  cnode->add_solid(csplane);
2921  return;
2922  }
2923  } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
2924  EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
2925  PT(EggGroup) temp_group = new EggGroup;
2926  if (comp->triangulate_into(temp_group)) {
2927  make_collision_plane(temp_group, cnode, flags);
2928  return;
2929  }
2930  }
2931  }
2932  }
2933 }
2934 
2935 
2936 
2937 /**
2938  * Creates a single CollisionPolygon corresponding to the first polygon
2939  * associated with this group.
2940  */
2941 void EggLoader::
2942 make_collision_floor_mesh(EggGroup *egg_group, CollisionNode *cnode,
2943  EggGroup::CollideFlags flags) {
2944 
2945  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2946 
2947 
2948  if (geom_group != nullptr) {
2949  create_collision_floor_mesh(cnode, geom_group,flags);
2950  }
2951 }
2952 
2953 /**
2954  * Creates a single CollisionPolygon corresponding to the first polygon
2955  * associated with this group.
2956  */
2957 void EggLoader::
2958 make_collision_polygon(EggGroup *egg_group, CollisionNode *cnode,
2959  EggGroup::CollideFlags flags) {
2960 
2961  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2962  if (geom_group != nullptr) {
2963  EggGroup::const_iterator ci;
2964  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2965  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
2966  create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
2967  egg_group, flags);
2968  } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
2969  EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
2970  PT(EggGroup) temp_group = new EggGroup;
2971  if (comp->triangulate_into(temp_group)) {
2972  make_collision_polygon(temp_group, cnode, flags);
2973  return;
2974  }
2975  }
2976  }
2977  }
2978 }
2979 
2980 
2981 /**
2982  * Creates a series of CollisionPolygons corresponding to the polygons
2983  * associated with this group.
2984  */
2985 void EggLoader::
2986 make_collision_polyset(EggGroup *egg_group, CollisionNode *cnode,
2987  EggGroup::CollideFlags flags) {
2988  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
2989  if (geom_group != nullptr) {
2990  EggGroup::const_iterator ci;
2991  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
2992  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
2993  create_collision_polygons(cnode, DCAST(EggPolygon, *ci),
2994  egg_group, flags);
2995  } else if ((*ci)->is_of_type(EggCompositePrimitive::get_class_type())) {
2996  EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, *ci);
2997  PT(EggGroup) temp_group = new EggGroup;
2998  if (comp->triangulate_into(temp_group)) {
2999  make_collision_polyset(temp_group, cnode, flags);
3000  }
3001  }
3002  }
3003  }
3004 }
3005 
3006 /**
3007  * Creates a single CollisionSphere corresponding to the polygons associated
3008  * with this group.
3009  */
3010 void EggLoader::
3011 make_collision_sphere(EggGroup *egg_group, CollisionNode *cnode,
3012  EggGroup::CollideFlags flags) {
3013  LPoint3 center;
3014  PN_stdfloat radius;
3015  LColor dummycolor;
3016  if (make_sphere(egg_group, flags, center, radius, dummycolor)) {
3017  CollisionSphere *cssphere =
3018  new CollisionSphere(center, radius);
3019  apply_collision_flags(cssphere, flags);
3020  cssphere->xform(cnode->get_transform()->get_mat());
3021  cnode->add_solid(cssphere);
3022  }
3023 }
3024 
3025 /**
3026  * Creates a single CollisionBox corresponding to the polygons associated with
3027  * this group.
3028  */
3029 void EggLoader::
3030 make_collision_box(EggGroup *egg_group, CollisionNode *cnode,
3031  EggGroup::CollideFlags flags) {
3032  LPoint3 min_p;
3033  LPoint3 max_p;
3034  CPT(TransformState) transform = cnode->get_transform();
3035  if (make_box(egg_group, flags, transform->get_mat(), min_p, max_p)) {
3036  CollisionBox *csbox =
3037  new CollisionBox(min_p, max_p);
3038  apply_collision_flags(csbox, flags);
3039  cnode->add_solid(csbox);
3040  }
3041 }
3042 
3043 /**
3044  * Creates a single CollisionInvSphere corresponding to the polygons
3045  * associated with this group.
3046  */
3047 void EggLoader::
3048 make_collision_inv_sphere(EggGroup *egg_group, CollisionNode *cnode,
3049  EggGroup::CollideFlags flags) {
3050  LPoint3 center;
3051  PN_stdfloat radius;
3052  LColor dummycolor;
3053  if (make_sphere(egg_group, flags, center, radius, dummycolor)) {
3054  CollisionInvSphere *cssphere =
3055  new CollisionInvSphere(center, radius);
3056  apply_collision_flags(cssphere, flags);
3057  cssphere->xform(cnode->get_transform()->get_mat());
3058  cnode->add_solid(cssphere);
3059  }
3060 }
3061 
3062 /**
3063  * Creates a single CollisionCapsule corresponding to the polygons associated
3064  * with this group.
3065  */
3066 void EggLoader::
3067 make_collision_capsule(EggGroup *egg_group, CollisionNode *cnode,
3068  EggGroup::CollideFlags flags) {
3069  EggGroup *geom_group = find_collision_geometry(egg_group, flags);
3070  if (geom_group != nullptr) {
3071  // Collect all of the vertices.
3072  pset<EggVertex *> vertices;
3073 
3074  EggGroup::const_iterator ci;
3075  for (ci = geom_group->begin(); ci != geom_group->end(); ++ci) {
3076  if ((*ci)->is_of_type(EggPrimitive::get_class_type())) {
3077  EggPrimitive *prim = DCAST(EggPrimitive, *ci);
3078  EggPrimitive::const_iterator pi;
3079  for (pi = prim->begin(); pi != prim->end(); ++pi) {
3080  vertices.insert(*pi);
3081  }
3082  }
3083  }
3084 
3085  // Now store the 3-d values in a vector for convenient access (and also
3086  // determine the centroid). We compute this in node space.
3087  size_t num_vertices = vertices.size();
3088  if (num_vertices != 0) {
3089  pvector<LPoint3d> vpos;
3090  vpos.reserve(num_vertices);
3091 
3092  LPoint3d center(0.0, 0.0, 0.0);
3094  for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
3095  EggVertex *vtx = (*vi);
3096  const LPoint3d &pos = vtx->get_pos3();
3097  vpos.push_back(pos);
3098  center += pos;
3099  }
3100  center /= (double)num_vertices;
3101 
3102  // Now that we have the centroid, we have to try to figure out the
3103  // cylinder's major axis. Start by finding a point farthest from the
3104  // centroid.
3105  size_t i;
3106  double radius2 = 0.0;
3107  LPoint3d far_a = center;
3108  for (i = 0; i < num_vertices; i++) {
3109  double dist2 = (vpos[i] - center).length_squared();
3110  if (dist2 > radius2) {
3111  radius2 = dist2;
3112  far_a = vpos[i];
3113  }
3114  }
3115 
3116  // The point we have found above, far_a, must be one one of the endcaps.
3117  // Now find another point, far_b, that is the farthest from far_a. This
3118  // will be a point on the other endcap.
3119  radius2 = 0.0;
3120  LPoint3d far_b = center;
3121  for (i = 0; i < num_vertices; i++) {
3122  double dist2 = (vpos[i] - far_a).length_squared();
3123  if (dist2 > radius2) {
3124  radius2 = dist2;
3125  far_b = vpos[i];
3126  }
3127  }
3128 
3129  // Now we have far_a and far_b, one point on each endcap. However,
3130  // these points are not necessarily centered on the endcaps, so we
3131  // haven't figured out the cylinder's axis yet (the line between far_a
3132  // and far_b will probably pass through the cylinder at an angle).
3133 
3134  // So we still need to determine the full set of points in each endcap.
3135  // To do this, we pass back through the set of points, categorizing each
3136  // point into either "endcap a" or "endcap b". We also leave a hefty
3137  // chunk of points in the middle uncategorized; this helps prevent us
3138  // from getting a little bit lopsided with points near the middle that
3139  // may appear to be closer to the wrong endcap.
3140  LPoint3d cap_a_center(0.0, 0.0, 0.0);
3141  LPoint3d cap_b_center(0.0, 0.0, 0.0);
3142  int num_a = 0;
3143  int num_b = 0;
3144 
3145  // This is the threshold length; points farther away from the center
3146  // than this are deemed to be in one endcap or the other.
3147  double center_length = (far_a - far_b).length() / 4.0;
3148  double center_length2 = center_length * center_length;
3149 
3150  for (i = 0; i < num_vertices; i++) {
3151  double dist2 = (vpos[i] - center).length_squared();
3152  if (dist2 > center_length2) {
3153  // This point is farther away from the center than center_length;
3154  // therefore it belongs in an endcap.
3155  double dist_a2 = (vpos[i] - far_a).length_squared();
3156  double dist_b2 = (vpos[i] - far_b).length_squared();
3157  if (dist_a2 < dist_b2) {
3158  // It's in endcap a.
3159  cap_a_center += vpos[i];
3160  num_a++;
3161  } else {
3162  // It's in endcap b.
3163  cap_b_center += vpos[i];
3164  num_b++;
3165  }
3166  }
3167  }
3168 
3169  if (num_a > 0 && num_b > 0) {
3170  cap_a_center /= (double)num_a;
3171  cap_b_center /= (double)num_b;
3172 
3173 
3174  // Now we finally have the major axis of the cylinder.
3175  LVector3d axis = cap_b_center - cap_a_center;
3176  axis.normalize();
3177 
3178  // If the axis is *almost* parallel with a major axis, assume it is
3179  // meant to be exactly parallel.
3180  if (IS_THRESHOLD_ZERO(axis[0], 0.01)) {
3181  axis[0] = 0.0;
3182  }
3183  if (IS_THRESHOLD_ZERO(axis[1], 0.01)) {
3184  axis[1] = 0.0;
3185  }
3186  if (IS_THRESHOLD_ZERO(axis[2], 0.01)) {
3187  axis[2] = 0.0;
3188  }
3189  axis.normalize();
3190 
3191  // Transform all of the points so that the major axis is along the Y
3192  // axis, and the origin is the center. This is very similar to the
3193  // CollisionCapsule's idea of its canonical orientation (although not
3194  // exactly the same, since it is centered on the origin instead of
3195  // having point_a on the origin). It makes it easier to determine the
3196  // length and radius of the cylinder.
3197  LMatrix4d mat;
3198  look_at(mat, axis, LVector3d(0.0, 0.0, 1.0), CS_zup_right);
3199  mat.set_row(3, center);
3200  LMatrix4d inv_mat;
3201  inv_mat.invert_from(mat);
3202 
3203  for (i = 0; i < num_vertices; i++) {
3204  vpos[i] = vpos[i] * inv_mat;
3205  }
3206 
3207  double max_radius2 = 0.0;
3208 
3209  // Now determine the radius.
3210  for (i = 0; i < num_vertices; i++) {
3211  LVector2d v(vpos[i][0], vpos[i][2]);
3212  double radius2 = v.length_squared();
3213  if (radius2 > max_radius2) {
3214  max_radius2 = radius2;
3215  }
3216  }
3217 
3218  // And with the radius, we can determine the length. We need to know
3219  // the radius first because we want the round endcaps to enclose all
3220  // points.
3221  double min_y = 0.0;
3222  double max_y = 0.0;
3223 
3224  for (i = 0; i < num_vertices; i++) {
3225  LVector2d v(vpos[i][0], vpos[i][2]);
3226  double radius2 = v.length_squared();
3227 
3228  if (vpos[i][1] < min_y) {
3229  // Adjust the Y pos to account for the point's distance from the
3230  // axis.
3231  double factor = sqrt(max_radius2 - radius2);
3232  min_y = min(min_y, vpos[i][1] + factor);
3233 
3234  } else if (vpos[i][1] > max_y) {
3235  double factor = sqrt(max_radius2 - radius2);
3236  max_y = max(max_y, vpos[i][1] - factor);
3237  }
3238  }
3239 
3240  double length = max_y - min_y;
3241  double radius = sqrt(max_radius2);
3242 
3243  // Finally, we have everything we need to define the cylinder.
3244  LVector3d half = axis * (length / 2.0);
3245  LPoint3d point_a = center - half;
3246  LPoint3d point_b = center + half;
3247 
3248  CollisionCapsule *cscapsule =
3249  new CollisionCapsule(LCAST(PN_stdfloat, point_a), LCAST(PN_stdfloat, point_b),
3250  radius);
3251  apply_collision_flags(cscapsule, flags);
3252  cscapsule->xform(cnode->get_transform()->get_mat());
3253  cnode->add_solid(cscapsule);
3254  }
3255  }
3256  }
3257 }
3258 
3259 /**
3260  * Does funny stuff to the CollisionSolid as appropriate, based on the
3261  * settings of the given CollideFlags.
3262  */
3263 void EggLoader::
3264 apply_collision_flags(CollisionSolid *solid, EggGroup::CollideFlags flags) {
3265  if ((flags & EggGroup::CF_intangible) != 0) {
3266  solid->set_tangible(false);
3267  }
3268  if ((flags & EggGroup::CF_level) != 0) {
3269  solid->set_effective_normal(LVector3::up());
3270  }
3271 }
3272 
3273 /**
3274  * Looks for the node, at or below the indicated node, that contains the
3275  * associated collision geometry.
3276  */
3277 EggGroup *EggLoader::
3278 find_collision_geometry(EggGroup *egg_group, EggGroup::CollideFlags flags) {
3279  if ((flags & EggGroup::CF_descend) != 0) {
3280  // If we have the "descend" instruction, we'll get to it when we get to
3281  // it. Don't worry about it now.
3282  return egg_group;
3283  }
3284 
3285  // Does this group have any polygons?
3286  EggGroup::const_iterator ci;
3287  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
3288  if ((*ci)->is_of_type(EggPolygon::get_class_type())) {
3289  // Yes! Use this group.
3290  return egg_group;
3291  }
3292  }
3293 
3294  // Well, the group had no polygons; look for a child group that has the same
3295  // collision type.
3296  for (ci = egg_group->begin(); ci != egg_group->end(); ++ci) {
3297  if ((*ci)->is_of_type(EggGroup::get_class_type())) {
3298  EggGroup *child_group = DCAST(EggGroup, *ci);
3299  if (child_group->get_cs_type() == egg_group->get_cs_type()) {
3300  return child_group;
3301  }
3302  }
3303  }
3304 
3305  // We got nothing.
3306  return nullptr;
3307 }
3308 
3309 /**
3310  * Creates a single CollisionPlane from the indicated EggPolygon.
3311  */
3312 CollisionPlane *EggLoader::
3313 create_collision_plane(EggPolygon *egg_poly, EggGroup *parent_group) {
3314  if (!egg_poly->cleanup()) {
3315  egg2pg_cat.info()
3316  << "Ignoring degenerate collision plane in " << parent_group->get_name()
3317  << "\n";
3318  return nullptr;
3319  }
3320 
3321  if (!egg_poly->is_planar()) {
3322  egg2pg_cat.warning()
3323  << "Non-planar polygon defining collision plane in "
3324  << parent_group->get_name()
3325  << "\n";
3326  }
3327 
3328  pvector<LVertex> vertices;
3329  if (!egg_poly->empty()) {
3330  EggPolygon::const_iterator vi;
3331  vi = egg_poly->begin();
3332 
3333  LVertexd vert = (*vi)->get_pos3();
3334  vertices.push_back(LCAST(PN_stdfloat, vert));
3335 
3336  LVertexd last_vert = vert;
3337  ++vi;
3338  while (vi != egg_poly->end()) {
3339  vert = (*vi)->get_pos3();
3340  if (!vert.almost_equal(last_vert)) {
3341  vertices.push_back(LCAST(PN_stdfloat, vert));
3342  }
3343 
3344  last_vert = vert;
3345  ++vi;
3346  }
3347  }
3348 
3349  if (vertices.size() < 3) {
3350  return nullptr;
3351  }
3352  LPlane plane(vertices[0], vertices[1], vertices[2]);
3353  return new CollisionPlane(plane);
3354 }
3355 
3356 /**
3357  * Creates one or more CollisionPolygons from the indicated EggPolygon, and
3358  * adds them to the indicated CollisionNode.
3359  */
3360 void EggLoader::
3361 create_collision_polygons(CollisionNode *cnode, EggPolygon *egg_poly,
3362  EggGroup *parent_group,
3363  EggGroup::CollideFlags flags) {
3364 
3365  PT(EggGroup) group = new EggGroup;
3366 
3367  if (!egg_poly->triangulate_into(group, false)) {
3368  egg2pg_cat.info()
3369  << "Ignoring degenerate collision polygon in "
3370  << parent_group->get_name()
3371  << "\n";
3372  return;
3373  }
3374 
3375  if (group->size() != 1) {
3376  egg2pg_cat.info()
3377  << "Triangulating concave or non-planar collision polygon in "
3378  << parent_group->get_name()
3379  << "\n";
3380  }
3381 
3382  EggGroup::iterator ci;
3383  for (ci = group->begin(); ci != group->end(); ++ci) {
3384  EggPolygon *poly = DCAST(EggPolygon, *ci);
3385 
3386  pvector<LVertex> vertices;
3387  if (!poly->empty()) {
3388  EggPolygon::const_iterator vi;
3389  vi = poly->begin();
3390 
3391  LVertexd vert = (*vi)->get_pos3();
3392  vertices.push_back(LCAST(PN_stdfloat, vert));
3393 
3394  LVertexd last_vert = vert;
3395  ++vi;
3396  while (vi != poly->end()) {
3397  vert = (*vi)->get_pos3();
3398  if (!vert.almost_equal(last_vert)) {
3399  vertices.push_back(LCAST(PN_stdfloat, vert));
3400  }
3401 
3402  last_vert = vert;
3403  ++vi;
3404  }
3405  }
3406 
3407  if (vertices.size() >= 3) {
3408  const LVertex *vertices_begin = &vertices[0];
3409  const LVertex *vertices_end = vertices_begin + vertices.size();
3410  PT(CollisionPolygon) cspoly =
3411  new CollisionPolygon(vertices_begin, vertices_end);
3412  if (cspoly->is_valid()) {
3413  apply_collision_flags(cspoly, flags);
3414  cspoly->xform(cnode->get_transform()->get_mat());
3415  cnode->add_solid(cspoly);
3416  }
3417  }
3418  }
3419 }
3420 
3421 /**
3422  * Creates a CollisionFloorMesh from the indicated EggPolygons, and adds it to
3423  * the indicated CollisionNode.
3424  */
3425 void EggLoader::
3426 create_collision_floor_mesh(CollisionNode *cnode,
3427  EggGroup *parent_group,
3428  EggGroup::CollideFlags flags) {
3429 
3430  PT(EggGroup) group = new EggGroup;
3431  EggVertexPool pool("floorMesh");
3432  pool.local_object();
3433  EggGroup::const_iterator egi;
3434  for (egi = parent_group->begin(); egi != parent_group->end(); ++egi) {
3435  if ((*egi)->is_of_type(EggPolygon::get_class_type())) {
3436  EggPolygon * poly = DCAST(EggPolygon, *egi);
3437  if (!poly->triangulate_into(group, false)) {
3438  egg2pg_cat.info()
3439  << "Ignoring degenerate collision polygon in "
3440  << parent_group->get_name()
3441  << "\n";
3442  return;
3443  }
3444 
3445  }
3446  }
3447  if(group->size() == 0) {
3448  egg2pg_cat.info()
3449  << "empty collision solid\n";
3450  return;
3451  }
3454 
3455  EggGroup::iterator ci;
3456  for (ci = group->begin(); ci != group->end(); ++ci) {
3457  EggPolygon *poly = DCAST(EggPolygon, *ci);
3458  if (poly->get_num_vertices() == 3) {
3459  CollisionFloorMesh::TriangleIndices tri;
3460 
3461  // generate a shared vertex triangle from the vertex pool
3462  tri.p1=pool.create_unique_vertex(*poly->get_vertex(0))->get_index();
3463  tri.p2=pool.create_unique_vertex(*poly->get_vertex(1))->get_index();
3464  tri.p3=pool.create_unique_vertex(*poly->get_vertex(2))->get_index();
3465 
3466  triangles.push_back(tri);
3467  } else if (poly->get_num_vertices() == 4) {
3468  // this is a case that really shouldn't happen, but appears to be
3469  // required -split up the quad int 2 tris.
3470  CollisionFloorMesh::TriangleIndices tri;
3471  CollisionFloorMesh::TriangleIndices tri2;
3472 
3473  // generate a shared vertex triangle from the vertex pool
3474  tri.p1=pool.create_unique_vertex(*poly->get_vertex(0))->get_index();
3475  tri.p2=pool.create_unique_vertex(*poly->get_vertex(1))->get_index();
3476  tri.p3=pool.create_unique_vertex(*poly->get_vertex(2))->get_index();
3477 
3478  triangles.push_back(tri);
3479 
3480  // generate a shared vertex triangle from the vertex pool
3481  tri2.p1=tri.p1;
3482  tri2.p2=tri.p3;
3483  tri2.p3=pool.create_unique_vertex(*poly->get_vertex(3))->get_index();
3484 
3485 
3486  triangles.push_back(tri2);
3487  }
3488  }
3489 
3490  // Now we have a set of triangles, and a pool
3491  PT(CollisionFloorMesh) csfloor = new CollisionFloorMesh;
3492 
3493 
3495  for (vi = pool.begin(); vi != pool.end(); vi++) {
3496  csfloor->add_vertex(LCAST(PN_stdfloat,(*vi)->get_pos3()));
3497  }
3498 
3500 
3501  for (ti = triangles.begin(); ti != triangles.end(); ti++) {
3502  CollisionFloorMesh::TriangleIndices triangle = *ti;
3503  csfloor->add_triangle(triangle.p1, triangle.p2, triangle.p3);
3504  }
3505  csfloor->xform(cnode->get_transform()->get_mat());
3506  cnode->add_solid(csfloor);
3507 }
3508 
3509 
3510 /**
3511  * Walks back over the tree and applies the DeferredNodeProperties that were
3512  * saved up along the way.
3513  */
3514 void EggLoader::
3515 apply_deferred_nodes(PandaNode *node, const DeferredNodeProperty &prop) {
3516  DeferredNodeProperty next_prop(prop);
3517 
3518  // Do we have a DeferredNodeProperty associated with this node?
3519  DeferredNodes::const_iterator dni;
3520  dni = _deferred_nodes.find(node);
3521 
3522  if (dni != _deferred_nodes.end()) {
3523  const DeferredNodeProperty &def = (*dni).second;
3524  next_prop.compose(def);
3525  }
3526 
3527  // Now apply the accumulated state to the node.
3528  next_prop.apply_to_node(node);
3529 
3530  int num_children = node->get_num_children();
3531  for (int i = 0; i < num_children; i++) {
3532  apply_deferred_nodes(node->get_child(i), next_prop);
3533  }
3534 }
3535 
3536 /**
3537  * Walks the hierarchy and calls expand_object_types() on each node, to expand
3538  * all of the ObjectType definitions in the file at once. Also prunes any
3539  * nodes that are flagged "backstage".
3540  *
3541  * The return value is true if this node should be kept, false if it should be
3542  * pruned.
3543  */
3544 bool EggLoader::
3545 expand_all_object_types(EggNode *egg_node) {
3546  if (egg_node->is_of_type(EggGroup::get_class_type())) {
3547  EggGroup *egg_group = DCAST(EggGroup, egg_node);
3548 
3549  if (egg_group->get_num_object_types() != 0) {
3550  pset<string> expanded;
3551  pvector<string> expanded_history;
3552  if (!expand_object_types(egg_group, expanded, expanded_history)) {
3553  return false;
3554  }
3555  }
3556  }
3557 
3558  // Now recurse on children, and we might prune children from this list as we
3559  // go.
3560  if (egg_node->is_of_type(EggGroupNode::get_class_type())) {
3561  EggGroupNode *egg_group_node = DCAST(EggGroupNode, egg_node);
3562  EggGroupNode::const_iterator ci;
3563  ci = egg_group_node->begin();
3564  while (ci != egg_group_node->end()) {
3565  EggGroupNode::const_iterator cnext = ci;
3566  ++cnext;
3567 
3568  if (!expand_all_object_types(*ci)) {
3569  // Prune this child.
3570  egg_group_node->erase(ci);
3571  }
3572  ci = cnext;
3573  }
3574  }
3575 
3576  return true;
3577 }
3578 
3579 /**
3580  * Recursively expands the group's ObjectType string(s). It's recursive
3581  * because an ObjectType string might itself expand to another ObjectType
3582  * string, which is allowed; but we don't want to get caught in a cycle.
3583  *
3584  * The return value is true if the object type is expanded and the node is
3585  * valid, or false if the node should be ignored (e.g. ObjectType
3586  * "backstage").
3587  */
3588 bool EggLoader::
3589 expand_object_types(EggGroup *egg_group, const pset<string> &expanded,
3590  const pvector<string> &expanded_history) {
3591  int num_object_types = egg_group->get_num_object_types();
3592 
3593  // First, copy out the object types so we can recursively modify the list.
3594  vector_string object_types;
3595  int i;
3596  for (i = 0; i < num_object_types; i++) {
3597  object_types.push_back(egg_group->get_object_type(i));
3598  }
3599  egg_group->clear_object_types();
3600 
3601  for (i = 0; i < num_object_types; i++) {
3602  string object_type = object_types[i];
3603  pset<string> new_expanded(expanded);
3604 
3605  // Check for a cycle.
3606  if (!new_expanded.insert(object_type).second) {
3607  egg2pg_cat.error()
3608  << "Cycle in ObjectType expansions:\n";
3610  for (pi = expanded_history.begin();
3611  pi != expanded_history.end();
3612  ++pi) {
3613  egg2pg_cat.error(false)
3614  << (*pi) << " -> ";
3615  }
3616  egg2pg_cat.error(false) << object_type << "\n";
3617  _error = true;
3618 
3619  } else {
3620  // No cycle; continue.
3621  pvector<string> new_expanded_history(expanded_history);
3622  new_expanded_history.push_back(object_type);
3623 
3624  if (!do_expand_object_type(egg_group, new_expanded,
3625  new_expanded_history, object_type)) {
3626  // Ignorable group; stop here.
3627  return false;
3628  }
3629  }
3630  }
3631 
3632  return true;
3633 }
3634 
3635 /**
3636  * Further implementation of expand_object_types().
3637  */
3638 bool EggLoader::
3639 do_expand_object_type(EggGroup *egg_group, const pset<string> &expanded,
3640  const pvector<string> &expanded_history,
3641  const string &object_type) {
3642  // Try to find the egg syntax that the given objecttype is shorthand for.
3643  // First, look in the config file.
3644 
3645  ConfigVariableString egg_object_type
3646  ("egg-object-type-" + downcase(object_type), "");
3647  string egg_syntax = egg_object_type;
3648 
3649  if (!egg_object_type.has_value()) {
3650  // It wasn't defined in a config file. Maybe it's built in?
3651 
3652  if (cmp_nocase_uh(object_type, "barrier") == 0) {
3653  egg_syntax = "<Collide> { Polyset descend }";
3654 
3655  } else if (cmp_nocase_uh(object_type, "solidpoly") == 0) {
3656  egg_syntax = "<Collide> { Polyset descend solid }";
3657 
3658  } else if (cmp_nocase_uh(object_type, "turnstile") == 0) {
3659  egg_syntax = "<Collide> { Polyset descend turnstile }";
3660 
3661  } else if (cmp_nocase_uh(object_type, "sphere") == 0) {
3662  egg_syntax = "<Collide> { Sphere descend }";
3663 
3664  } else if (cmp_nocase_uh(object_type, "tube") == 0) {
3665  egg_syntax = "<Collide> { Tube descend }";
3666 
3667  } else if (cmp_nocase_uh(object_type, "trigger") == 0) {
3668  egg_syntax = "<Collide> { Polyset descend intangible }";
3669 
3670  } else if (cmp_nocase_uh(object_type, "trigger_sphere") == 0) {
3671  egg_syntax = "<Collide> { Sphere descend intangible }";
3672 
3673  } else if (cmp_nocase_uh(object_type, "eye_trigger") == 0) {
3674  egg_syntax = "<Collide> { Polyset descend intangible center }";
3675 
3676  } else if (cmp_nocase_uh(object_type, "bubble") == 0) {
3677  egg_syntax = "<Collide> { Sphere keep descend }";
3678 
3679  } else if (cmp_nocase_uh(object_type, "ghost") == 0) {
3680  egg_syntax = "<Scalar> collide-mask { 0 }";
3681 
3682  } else if (cmp_nocase_uh(object_type, "dcs") == 0) {
3683  egg_syntax = "<DCS> { 1 }";
3684 
3685  } else if (cmp_nocase_uh(object_type, "model") == 0) {
3686  egg_syntax = "<Model> { 1 }";
3687 
3688  } else if (cmp_nocase_uh(object_type, "none") == 0) {
3689  // ObjectType "none" is a special case, meaning nothing in particular.
3690  return true;
3691 
3692  } else if (cmp_nocase_uh(object_type, "backstage") == 0) {
3693  // Ignore "backstage" geometry.
3694  return false;
3695 
3696  } else {
3697  egg2pg_cat.error()
3698  << "Unknown ObjectType " << object_type << "\n";
3699  _error = true;
3700  egg2pg_cat.debug() << "returning true\n";
3701  return true;
3702  }
3703  }
3704 
3705  if (!egg_syntax.empty()) {
3706  if (!egg_group->parse_egg(egg_syntax)) {
3707  egg2pg_cat.error()
3708  << "Error while parsing definition for ObjectType "
3709  << object_type << "\n";
3710  _error = true;
3711 
3712  } else {
3713  // Now we've parsed the object type syntax, which might have added more
3714  // object types. Recurse if necessary.
3715  if (egg_group->get_num_object_types() != 0) {
3716  if (!expand_object_types(egg_group, expanded, expanded_history)) {
3717  return false;
3718  }
3719  }
3720  }
3721  }
3722 
3723  return true;
3724 }
3725 
3726 /**
3727  * Extracts the combine_mode from the given egg texture, and returns its
3728  * corresponding TextureStage value.
3729  */
3730 TextureStage::CombineMode EggLoader::
3731 get_combine_mode(const EggTexture *egg_tex,
3732  EggTexture::CombineChannel channel) {
3733  switch (egg_tex->get_combine_mode(channel)) {
3734  case EggTexture::CM_unspecified:
3735  // fall through
3736 
3737  case EggTexture::CM_modulate:
3738  return TextureStage::CM_modulate;
3739 
3740  case EggTexture::CM_replace:
3741  return TextureStage::CM_replace;
3742 
3743  case EggTexture::CM_add:
3744  return TextureStage::CM_add;
3745 
3746  case EggTexture::CM_add_signed:
3747  return TextureStage::CM_add_signed;
3748 
3749  case EggTexture::CM_interpolate:
3750  return TextureStage::CM_interpolate;
3751 
3752  case EggTexture::CM_subtract:
3753  return TextureStage::CM_subtract;
3754 
3755  case EggTexture::CM_dot3_rgb:
3756  return TextureStage::CM_dot3_rgb;
3757 
3758  case EggTexture::CM_dot3_rgba:
3759  return TextureStage::CM_dot3_rgba;
3760  };
3761 
3762  return TextureStage::CM_undefined;
3763 }
3764 
3765 /**
3766  * Extracts the combine_source from the given egg texture, and returns its
3767  * corresponding TextureStage value.
3768  */
3769 TextureStage::CombineSource EggLoader::
3770 get_combine_source(const EggTexture *egg_tex,
3771  EggTexture::CombineChannel channel, int n) {
3772  switch (egg_tex->get_combine_source(channel, n)) {
3773  case EggTexture::CS_unspecified:
3774  // The default source if it is unspecified is based on the parameter
3775  // index.
3776  switch (n) {
3777  case 0:
3778  return TextureStage::CS_previous;
3779  case 1:
3780  return TextureStage::CS_texture;
3781  case 2:
3782  return TextureStage::CS_constant;
3783  }
3784  // Otherwise, fall through
3785 
3786  case EggTexture::CS_texture:
3787  return TextureStage::CS_texture;
3788 
3789  case EggTexture::CS_constant:
3790  return TextureStage::CS_constant;
3791 
3792  case EggTexture::CS_primary_color:
3793  return TextureStage::CS_primary_color;
3794 
3795  case EggTexture::CS_previous:
3796  return TextureStage::CS_previous;
3797 
3798  case EggTexture::CS_constant_color_scale:
3799  return TextureStage::CS_constant_color_scale;
3800 
3801  case EggTexture::CS_last_saved_result:
3802  return TextureStage::CS_last_saved_result;
3803  };
3804 
3805  return TextureStage::CS_undefined;
3806 }
3807 
3808 /**
3809  * Extracts the combine_operand from the given egg texture, and returns its
3810  * corresponding TextureStage value.
3811  */
3812 TextureStage::CombineOperand EggLoader::
3813 get_combine_operand(const EggTexture *egg_tex,
3814  EggTexture::CombineChannel channel, int n) {
3815  switch (egg_tex->get_combine_operand(channel, n)) {
3816  case EggTexture::CO_unspecified:
3817  if (channel == EggTexture::CC_rgb) {
3818  // The default operand for RGB is src_color, except for the third
3819  // parameter, which defaults to src_alpha.
3820  return n < 2 ? TextureStage::CO_src_color : TextureStage::CO_src_alpha;
3821  } else {
3822  // The default operand for alpha is always src_alpha.
3823  return TextureStage::CO_src_alpha;
3824  }
3825 
3826  case EggTexture::CO_src_color:
3827  return TextureStage::CO_src_color;
3828 
3829  case EggTexture::CO_one_minus_src_color:
3830  return TextureStage::CO_one_minus_src_color;
3831 
3832  case EggTexture::CO_src_alpha:
3833  return TextureStage::CO_src_alpha;
3834 
3835  case EggTexture::CO_one_minus_src_alpha:
3836  return TextureStage::CO_one_minus_src_alpha;
3837  };
3838 
3839  return TextureStage::CO_undefined;
3840 }
3841 
3842 /**
3843  * Converts the EggGroup's BlendMode to the corresponding
3844  * ColorBlendAttrib::Mode value.
3845  */
3846 ColorBlendAttrib::Mode EggLoader::
3847 get_color_blend_mode(EggGroup::BlendMode mode) {
3848  switch (mode) {
3849  case EggGroup::BM_unspecified:
3850  case EggGroup::BM_none:
3851  return ColorBlendAttrib::M_none;
3852  case EggGroup::BM_add:
3853  return ColorBlendAttrib::M_add;
3854  case EggGroup::BM_subtract:
3855  return ColorBlendAttrib::M_subtract;
3856  case EggGroup::BM_inv_subtract:
3857  return ColorBlendAttrib::M_inv_subtract;
3858  case EggGroup::BM_min:
3859  return ColorBlendAttrib::M_min;
3860  case EggGroup::BM_max:
3861  return ColorBlendAttrib::M_max;
3862  }
3863 
3864  return ColorBlendAttrib::M_none;
3865 }
3866 
3867 /**
3868  * Converts the EggGroup's BlendOperand to the corresponding
3869  * ColorBlendAttrib::Operand value.
3870  */
3871 ColorBlendAttrib::Operand EggLoader::
3872 get_color_blend_operand(EggGroup::BlendOperand operand) {
3873  switch (operand) {
3874  case EggGroup::BO_zero:
3875  return ColorBlendAttrib::O_zero;
3876  case EggGroup::BO_unspecified:
3877  case EggGroup::BO_one:
3878  return ColorBlendAttrib::O_one;
3879  case EggGroup::BO_incoming_color:
3880  return ColorBlendAttrib::O_incoming_color;
3881  case EggGroup::BO_one_minus_incoming_color:
3882  return ColorBlendAttrib::O_one_minus_incoming_color;
3883  case EggGroup::BO_fbuffer_color:
3884  return ColorBlendAttrib::O_fbuffer_color;
3885  case EggGroup::BO_one_minus_fbuffer_color:
3886  return ColorBlendAttrib::O_one_minus_fbuffer_color;
3887  case EggGroup::BO_incoming_alpha:
3888  return ColorBlendAttrib::O_incoming_alpha;
3889  case EggGroup::BO_one_minus_incoming_alpha:
3890  return ColorBlendAttrib::O_one_minus_incoming_alpha;
3891  case EggGroup::BO_fbuffer_alpha:
3892  return ColorBlendAttrib::O_fbuffer_alpha;
3893  case EggGroup::BO_one_minus_fbuffer_alpha:
3894  return ColorBlendAttrib::O_one_minus_fbuffer_alpha;
3895  case EggGroup::BO_constant_color:
3896  return ColorBlendAttrib::O_constant_color;
3897  case EggGroup::BO_one_minus_constant_color:
3898  return ColorBlendAttrib::O_one_minus_constant_color;
3899  case EggGroup::BO_constant_alpha:
3900  return ColorBlendAttrib::O_constant_alpha;
3901  case EggGroup::BO_one_minus_constant_alpha:
3902  return ColorBlendAttrib::O_one_minus_constant_alpha;
3903  case EggGroup::BO_incoming_color_saturate:
3904  return ColorBlendAttrib::O_incoming_color_saturate;
3905  case EggGroup::BO_color_scale:
3906  return ColorBlendAttrib::O_color_scale;
3907  case EggGroup::BO_one_minus_color_scale:
3908  return ColorBlendAttrib::O_one_minus_color_scale;
3909  case EggGroup::BO_alpha_scale:
3910  return ColorBlendAttrib::O_alpha_scale;
3911  case EggGroup::BO_one_minus_alpha_scale:
3912  return ColorBlendAttrib::O_one_minus_alpha_scale;
3913  }
3914 
3915  return ColorBlendAttrib::O_zero;
3916 }
3917 
3918 /**
3919  *
3920  */
3921 bool EggLoader::VertexPoolTransform::
3922 operator < (const EggLoader::VertexPoolTransform &other) const {
3923  if (_vertex_pool != other._vertex_pool) {
3924  return _vertex_pool < other._vertex_pool;
3925  }
3926  int compare = _transform.compare_to(other._transform, 0.001);
3927  if (compare != 0) {
3928  return compare < 0;
3929  }
3930 
3931  if (_bake_in_uvs.size() != other._bake_in_uvs.size()) {
3932  return _bake_in_uvs.size() < other._bake_in_uvs.size();
3933  }
3934 
3935  BakeInUVs::const_iterator ai, bi;
3936  ai = _bake_in_uvs.begin();
3937  bi = other._bake_in_uvs.begin();
3938  while (ai != _bake_in_uvs.end()) {
3939  nassertr(bi != other._bake_in_uvs.end(), false);
3940  if ((*ai) != (*bi)) {
3941  return (*ai) < (*bi);
3942  }
3943  ++ai;
3944  ++bi;
3945  }
3946  nassertr(bi == other._bake_in_uvs.end(), false);
3947 
3948  return false;
3949 }
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
virtual void xform(const LMatrix4 &mat)
Transforms the solid by the indicated matrix.
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.