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