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