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