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