Panda3D
Loading...
Searching...
No Matches
eggGroupNode.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 eggGroupNode.cxx
10 * @author drose
11 * @date 1999-01-16
12 */
13
14#include "eggGroupNode.h"
15#include "eggGroup.h"
16#include "eggCoordinateSystem.h"
17#include "eggData.h"
18#include "eggFilenameNode.h"
20#include "eggPrimitive.h"
21#include "eggPoint.h"
22#include "eggPolygon.h"
24#include "eggMesher.h"
25#include "eggVertexPool.h"
26#include "eggVertex.h"
29#include "epvector.h"
30#include "pt_EggTexture.h"
31#include "pt_EggMaterial.h"
32#include "config_egg.h"
33
34#include "dSearchPath.h"
35#include "virtualFileSystem.h"
36#include "deg_2_rad.h"
37#include "dcast.h"
38#include "bamCacheRecord.h"
39
40#include <algorithm>
41
42using std::string;
43
44TypeHandle EggGroupNode::_type_handle;
45
46
47/**
48 *
49 */
50EggGroupNode::
51EggGroupNode(const EggGroupNode &copy) : EggNode(copy) {
52 if (!copy.empty()) {
53 egg_cat.warning()
54 << "The EggGroupNode copy constructor does not copy children!\n";
55 }
56}
57
58/**
59 *
60 */
61EggGroupNode &EggGroupNode::
62operator =(const EggGroupNode &copy) {
63 if (!copy.empty()) {
64 egg_cat.warning()
65 << "The EggGroupNode copy assignment does not copy children!\n";
66 }
67 EggNode::operator =(copy);
68 return *this;
69}
70
71/**
72 *
73 */
74EggGroupNode::
75~EggGroupNode() {
76}
77
78
79/**
80 * Writes the group and all of its children to the indicated output stream in
81 * Egg format.
82 */
84write(std::ostream &out, int indent_level) const {
85 iterator i;
86
87 // Since joints tend to reference vertex pools, which sometimes appear later
88 // in the file, and since generally non-joints don't reference joints, we
89 // try to maximize our chance of writing out a one-pass readable egg file by
90 // writing joints at the end of the list of children of a particular node.
91
92 for (i = begin(); i != end(); ++i) {
93 PT(EggNode) child = (*i);
94 if (!child->is_joint()) {
95 child->write(out, indent_level);
96 }
97 }
98
99 for (i = begin(); i != end(); ++i) {
100 PT(EggNode) child = (*i);
101 if (child->is_joint()) {
102 child->write(out, indent_level);
103 }
104 }
105}
106
107/**
108 *
109 */
110EggGroupNode::iterator EggGroupNode::
111begin() const {
112 return _children.begin();
113}
114
115/**
116 *
117 */
118EggGroupNode::iterator EggGroupNode::
119end() const {
120 return _children.end();
121}
122
123/**
124 *
125 */
126EggGroupNode::reverse_iterator EggGroupNode::
127rbegin() const {
128 return _children.rbegin();
129}
130
131/**
132 *
133 */
134EggGroupNode::reverse_iterator EggGroupNode::
135rend() const {
136 return _children.rend();
137}
138
139/**
140 *
141 */
142EggGroupNode::iterator EggGroupNode::
143insert(iterator position, PT(EggNode) x) {
144 prepare_add_child(x);
145 return _children.insert((Children::iterator &)position, x);
146}
147
148/**
149 *
150 */
151EggGroupNode::iterator EggGroupNode::
152erase(iterator position) {
153 prepare_remove_child(*position);
154 return _children.erase((Children::iterator &)position);
155}
156
157/**
158 *
159 */
160EggGroupNode::iterator EggGroupNode::
161erase(iterator first, iterator last) {
162 iterator i;
163 for (i = first; i != last; ++i) {
164 prepare_remove_child(*i);
165 }
166 return _children.erase((Children::iterator &)first,
167 (Children::iterator &)last);
168}
169
170/**
171 * Replaces the node at the indicated position with the indicated node. It is
172 * an error to call this with an invalid position iterator (e.g. end()).
173 */
175replace(iterator position, PT(EggNode) x) {
176 nassertv(position != end());
177
178 prepare_remove_child(*position);
179 prepare_add_child(x);
180 *(Children::iterator &)position = x;
181}
182
183/**
184 *
185 */
186bool EggGroupNode::
187empty() const {
188 return _children.empty();
189}
190
191/**
192 *
193 */
194EggGroupNode::size_type EggGroupNode::
195size() const {
196 return _children.size();
197}
198
199/**
200 *
201 */
202void EggGroupNode::
203clear() {
204 erase(begin(), end());
205}
206
207/**
208 * Returns the first child in the group's list of children, or NULL if the
209 * list of children is empty. Can be used with get_next_child() to return the
210 * complete list of children without using the iterator class; however, this
211 * is non-thread-safe, and so is not recommended except for languages other
212 * than C++ which cannot use the iterators.
213 */
216 _gnc_iterator = begin();
217 return get_next_child();
218}
219
220/**
221 * Returns the next child in the group's list of children since the last call
222 * to get_first_child() or get_next_child(), or NULL if the last child has
223 * been returned. Can be used with get_first_child() to return the complete
224 * list of children without using the iterator class; however, this is non-
225 * thread-safe, and so is not recommended except for languages other than C++
226 * which cannot use the iterators.
227 *
228 * It is an error to call this without previously calling get_first_child().
229 */
232 if (_gnc_iterator != end()) {
233 return *_gnc_iterator++;
234 }
235 return nullptr;
236}
237
238/**
239 * Adds the indicated child to the group and returns it. If the child node is
240 * already a child of some other node, removes it first.
241 */
243add_child(EggNode *node) {
245 PT(EggNode) ptnode = node;
246 if (node->_parent != nullptr) {
247 node->_parent->remove_child(node);
248 }
249 prepare_add_child(node);
250 _children.push_back(node);
251 return node;
252}
253
254/**
255 * Removes the indicated child node from the group and returns it. If the
256 * child was not already in the group, does nothing and returns NULL.
257 */
258PT(EggNode) EggGroupNode::
259remove_child(EggNode *node) {
260 PT(EggNode) ptnode = node;
261 iterator i = find(begin(), end(), ptnode);
262 if (i == end()) {
263 return PT(EggNode)();
264 } else {
265 // erase() calls prepare_remove_child().
266 erase(i);
267 return ptnode;
268 }
269}
270
271
272/**
273 * Moves all the children from the other node to this one. This is especially
274 * useful because the group node copy assignment operator does not copy
275 * children.
276 */
279 Children::iterator ci;
280 for (ci = other._children.begin();
281 ci != other._children.end();
282 ++ci) {
283 other.prepare_remove_child(*ci);
284 prepare_add_child(*ci);
285 }
286
287 _children.splice(_children.end(), other._children);
288}
289
290/**
291 * Returns the child of this node whose name is the indicated string, or NULL
292 * if there is no child of this node by that name. Does not search
293 * recursively.
294 */
296find_child(const string &name) const {
297 Children::const_iterator ci;
298 for (ci = _children.begin(); ci != _children.end(); ++ci) {
299 EggNode *child = (*ci);
300 if (child->get_name() == name) {
301 return child;
302 }
303 }
304
305 return nullptr;
306}
307
308/**
309 * Returns true if any nodes at this level and below include a reference to a
310 * file via an absolute pathname, or false if all references are relative.
311 */
314 Children::const_iterator ci;
315 for (ci = _children.begin();
316 ci != _children.end();
317 ++ci) {
318 EggNode *child = *ci;
319 if (child->is_of_type(EggTexture::get_class_type())) {
320 EggTexture *tex = DCAST(EggTexture, child);
321 if (!tex->get_filename().is_local()) {
322 if (egg_cat.is_debug()) {
323 egg_cat.debug()
324 << "Absolute pathname: " << tex->get_filename()
325 << "\n";
326 }
327 return true;
328 }
329
330 if (tex->has_alpha_filename()) {
331 if (!tex->get_alpha_filename().is_local()) {
332 if (egg_cat.is_debug()) {
333 egg_cat.debug()
334 << "Absolute pathname: " << tex->get_alpha_filename()
335 << "\n";
336 }
337 return true;
338 }
339 }
340
341 } else if (child->is_of_type(EggFilenameNode::get_class_type())) {
342 EggFilenameNode *fnode = DCAST(EggFilenameNode, child);
343 if (!fnode->get_filename().is_local()) {
344 if (egg_cat.is_debug()) {
345 egg_cat.debug()
346 << "Absolute pathname: " << fnode->get_filename()
347 << "\n";
348 }
349 return true;
350 }
351
352 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
353 if (DCAST(EggGroupNode, child)->has_absolute_pathnames()) {
354 return true;
355 }
356 }
357 }
358
359 return false;
360}
361
362/**
363 * Walks the tree and attempts to resolve any filenames encountered. This
364 * looks up filenames along the specified search path; it does not
365 * automatically search the model_path for missing files.
366 */
368resolve_filenames(const DSearchPath &searchpath) {
370
371 Children::iterator ci;
372 for (ci = _children.begin();
373 ci != _children.end();
374 ++ci) {
375 EggNode *child = *ci;
376 if (child->is_of_type(EggTexture::get_class_type())) {
377 EggTexture *tex = DCAST(EggTexture, child);
378 Filename tex_filename = tex->get_filename();
379 vfs->resolve_filename(tex_filename, searchpath);
380 tex->set_filename(tex_filename);
381
382 if (tex->has_alpha_filename()) {
383 Filename alpha_filename = tex->get_alpha_filename();
384 vfs->resolve_filename(alpha_filename, searchpath);
385 tex->set_alpha_filename(alpha_filename);
386 }
387
388 } else if (child->is_of_type(EggFilenameNode::get_class_type())) {
389 EggFilenameNode *fnode = DCAST(EggFilenameNode, child);
390 Filename filename = fnode->get_filename();
391 vfs->resolve_filename(filename, searchpath, fnode->get_default_extension());
392 fnode->set_filename(filename);
393
394 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
395 DCAST(EggGroupNode, child)->resolve_filenames(searchpath);
396 }
397 }
398}
399
400/**
401 * Similar to resolve_filenames, but each non-absolute filename encountered is
402 * arbitrarily taken to be in the indicated directory, whether or not the so-
403 * named filename exists.
404 */
406force_filenames(const Filename &directory) {
407 Children::iterator ci;
408 for (ci = _children.begin();
409 ci != _children.end();
410 ++ci) {
411 EggNode *child = *ci;
412 if (child->is_of_type(EggTexture::get_class_type())) {
413 EggTexture *tex = DCAST(EggTexture, child);
414 Filename tex_filename = tex->get_filename();
415 if (tex_filename.is_local()) {
416 tex->set_filename(Filename(directory, tex_filename));
417 }
418
419 if (tex->has_alpha_filename()) {
420 Filename alpha_filename = tex->get_alpha_filename();
421 if (alpha_filename.is_local()) {
422 tex->set_alpha_filename(Filename(directory, alpha_filename));
423 }
424 }
425
426 } else if (child->is_of_type(EggFilenameNode::get_class_type())) {
427 EggFilenameNode *fnode = DCAST(EggFilenameNode, child);
428 Filename filename = fnode->get_filename();
429 if (filename.is_local()) {
430 fnode->set_filename(Filename(directory, filename));
431 }
432
433 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
434 DCAST(EggGroupNode, child)->force_filenames(directory);
435 }
436 }
437}
438
439/**
440 * Reverses the vertex ordering of all polygons defined at this node and
441 * below. Does not change the surface normals, if any.
442 */
445 Children::iterator ci;
446 for (ci = _children.begin();
447 ci != _children.end();
448 ++ci) {
449 EggNode *child = *ci;
450 if (child->is_of_type(EggPrimitive::get_class_type())) {
451 EggPrimitive *prim = DCAST(EggPrimitive, child);
453
454 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
455 DCAST(EggGroupNode, child)->reverse_vertex_ordering();
456 }
457 }
458}
459
460/**
461 * Recomputes all the vertex normals for polygon geometry at this group node
462 * and below so that they accurately reflect the vertex positions. A shared
463 * edge between two polygons (even in different groups) is considered smooth
464 * if the angle between the two edges is less than threshold degrees.
465 *
466 * This function also removes degenerate polygons that do not have enough
467 * vertices to define a normal. It does not affect normals for other kinds of
468 * primitives like Nurbs or Points.
469 *
470 * This function does not remove or adjust vertices in the vertex pool; it
471 * only adds new vertices with the correct normals. Thus, it is a good idea
472 * to call remove_unused_vertices() after calling this.
473 */
475recompute_vertex_normals(double threshold, CoordinateSystem cs) {
476 // First, collect all the vertices together with their shared polygons.
477 NVertexCollection collection;
478 r_collect_vertex_normals(collection, threshold, cs);
479
480 // Now bust them into separate groups according to the edge threshold. Two
481 // polygons that share a vertex belong in the same group only if the angle
482 // between their normals is within the threshold.
483
484 double cos_angle = cos(deg_2_rad(threshold));
485
486 NVertexCollection::iterator ci;
487 for (ci = collection.begin(); ci != collection.end(); ++ci) {
488 NVertexGroup &group = (*ci).second;
489
490 // Here's a group of polygons that share a vertex. Build up a new group
491 // that consists of just the first polygon and all the ones that are
492 // within threshold degrees from it.
493 NVertexGroup::iterator gi;
494 gi = group.begin();
495 while (gi != group.end()) {
496 const NVertexReference &base_ref = (*gi);
497 NVertexGroup new_group;
498 NVertexGroup leftover_group;
499 new_group.push_back(base_ref);
500 ++gi;
501
502 while (gi != group.end()) {
503 const NVertexReference &ref = (*gi);
504 double dot = base_ref._normal.dot(ref._normal);
505 if (dot > cos_angle) {
506 // These polygons are close enough to the same angle.
507 new_group.push_back(ref);
508 } else {
509 // These polygons are not.
510 leftover_group.push_back(ref);
511 }
512 ++gi;
513 }
514
515 // Now new_group is a collection of connected polygons and the vertices
516 // that connect them. Smooth these vertices.
517 do_compute_vertex_normals(new_group);
518
519 // And reset the group of remaining polygons.
520 group.swap(leftover_group);
521 gi = group.begin();
522 }
523 }
524}
525
526/**
527 * Recomputes all the polygon normals for polygon geometry at this group node
528 * and below so that they accurately reflect the vertex positions. Normals
529 * are removed from the vertices and defined only on polygons, giving the
530 * geometry a faceted appearance.
531 *
532 * This function also removes degenerate polygons that do not have enough
533 * vertices to define a normal. It does not affect normals for other kinds of
534 * primitives like Nurbs or Points.
535 *
536 * This function does not remove or adjust vertices in the vertex pool; it
537 * only adds new vertices with the normals removed. Thus, it is a good idea
538 * to call remove_unused_vertices() after calling this.
539 */
541recompute_polygon_normals(CoordinateSystem cs) {
542 Children::iterator ci, cnext;
543 ci = _children.begin();
544 while (ci != _children.end()) {
545 cnext = ci;
546 ++cnext;
547 EggNode *child = *ci;
548
549 if (child->is_of_type(EggPolygon::get_class_type())) {
550 EggPolygon *polygon = DCAST(EggPolygon, child);
551
552 if (!polygon->recompute_polygon_normal(cs)) {
553 // The polygon is degenerate. Remove it.
554 prepare_remove_child(child);
555 _children.erase(ci);
556
557 } else {
558 // Remove the normal from each polygon vertex.
559 size_t num_vertices = polygon->size();
560 for (size_t i = 0; i < num_vertices; i++) {
561 EggVertex *vertex = polygon->get_vertex(i);
562 EggVertexPool *pool = vertex->get_pool();
563
564 if (vertex->has_normal()) {
565 EggVertex new_vertex(*vertex);
566 new_vertex.clear_normal();
567 EggVertex *unique = pool->create_unique_vertex(new_vertex);
568 unique->copy_grefs_from(*vertex);
569
570 polygon->set_vertex(i, unique);
571 }
572 }
573 }
574
575 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
576 DCAST(EggGroupNode, child)->recompute_polygon_normals(cs);
577 }
578
579 ci = cnext;
580 }
581}
582
583/**
584 * Removes all normals from primitives, and the vertices they reference, at
585 * this node and below.
586 *
587 * This function does not remove or adjust vertices in the vertex pool; it
588 * only adds new vertices with the normal removed. Thus, it is a good idea to
589 * call remove_unused_vertices() after calling this.
590 */
593 Children::iterator ci;
594 for (ci = _children.begin(); ci != _children.end(); ++ci) {
595 EggNode *child = *ci;
596
597 if (child->is_of_type(EggPrimitive::get_class_type())) {
598 EggPrimitive *prim = DCAST(EggPrimitive, child);
599 prim->clear_normal();
600
601 // Remove the normal from each prim vertex.
602 size_t num_vertices = prim->size();
603 for (size_t i = 0; i < num_vertices; i++) {
604 EggVertex *vertex = prim->get_vertex(i);
605 EggVertexPool *pool = vertex->get_pool();
606
607 if (vertex->has_normal()) {
608 EggVertex new_vertex(*vertex);
609 new_vertex.clear_normal();
610 EggVertex *unique = pool->create_unique_vertex(new_vertex);
611 unique->copy_grefs_from(*vertex);
612
613 prim->set_vertex(i, unique);
614 }
615 }
616
617 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
618 DCAST(EggGroupNode, child)->strip_normals();
619 }
620 }
621}
622
623/**
624 * This function recomputes the tangent and binormal for the named texture
625 * coordinate set for all vertices at this level and below. Use the empty
626 * string for the default texture coordinate set.
627 *
628 * It is necessary for each vertex to already have a normal (or at least a
629 * polygon normal), as well as a texture coordinate in the named texture
630 * coordinate set, before calling this function. You might precede this with
631 * recompute_vertex_normals() to ensure that the normals exist.
632 *
633 * Like recompute_vertex_normals(), this function does not remove or adjust
634 * vertices in the vertex pool; it only adds new vertices with the new
635 * tangents and binormals computed. Thus, it is a good idea to call
636 * remove_unused_vertices() after calling this.
637 */
640 // First, collect all the vertices together with their shared polygons.
641 TBNVertexCollection collection;
642 r_collect_tangent_binormal(uv_name, collection);
643
644 // Now compute the tangent and binormal separately for each common group of
645 // vertices.
646 TBNVertexCollection::const_iterator ci;
647 for (ci = collection.begin(); ci != collection.end(); ++ci) {
648 const TBNVertexValue &value = (*ci).first;
649 const TBNVertexGroup &group = (*ci).second;
650
651 do_compute_tangent_binormal(value, group);
652 }
653
654 return true;
655}
656
657/**
658 * This function recomputes the tangent and binormal for the named texture
659 * coordinate sets. Returns true if anything was done.
660 */
662recompute_tangent_binormal(const vector_string &names) {
663 bool changed = false;
664
665 for (vector_string::const_iterator si = names.begin();
666 si != names.end();
667 ++si) {
668 GlobPattern uv_name(*si);
669 nout << "Computing tangent and binormal for \"" << uv_name << "\"\n";
671 changed = true;
672 }
673
674 return changed;
675}
676
677/**
678 * This function recomputes the tangent and binormal for any texture
679 * coordinate set that affects a normal map. Returns true if anything was
680 * done.
681 */
684 vector_string names;
686 EggTextureCollection::iterator eti;
687 texs.find_used_textures(this);
688 for (eti = texs.begin(); eti != texs.end(); eti++) {
689 EggTexture *eggtex = (*eti);
690 if ((eggtex->get_env_type() == EggTexture::ET_normal)||
691 (eggtex->get_env_type() == EggTexture::ET_normal_height)||
692 (eggtex->get_env_type() == EggTexture::ET_normal_gloss)) {
693 string uv = eggtex->get_uv_name();
694 vector_string::iterator it = find(names.begin(), names.end(), uv);
695 if (it == names.end()) {
696 names.push_back(uv);
697 }
698 }
699 }
700 return recompute_tangent_binormal(names);
701}
702
703/**
704 * Replace all higher-order polygons at this point in the scene graph and
705 * below with triangles. Returns the total number of new triangles produced,
706 * less degenerate polygons removed.
707 *
708 * If flags contains T_polygon and T_convex, both concave and convex polygons
709 * will be subdivided into triangles; with only T_polygon, only concave
710 * polygons will be subdivided, and convex polygons will be largely unchanged.
711 */
713triangulate_polygons(int flags) {
714 int num_produced = 0;
715
716 Children children_copy = _children;
717
718 Children::iterator ci;
719 for (ci = children_copy.begin();
720 ci != children_copy.end();
721 ++ci) {
722 EggNode *child = (*ci);
723
724 if (child->is_of_type(EggPolygon::get_class_type())) {
725 if ((flags & T_polygon) != 0) {
726 EggPolygon *poly = DCAST(EggPolygon, child);
727 poly->triangulate_in_place((flags & T_convex) != 0);
728 }
729
730 } else if (child->is_of_type(EggCompositePrimitive::get_class_type())) {
731 if ((flags & T_composite) != 0) {
732 EggCompositePrimitive *comp = DCAST(EggCompositePrimitive, child);
733 comp->triangulate_in_place();
734 }
735
736 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
737 if ((flags & T_recurse) != 0) {
738 num_produced += DCAST(EggGroupNode, child)->triangulate_polygons(flags);
739 }
740 }
741 }
742
743 num_produced += std::max(0, (int)(_children.size() - children_copy.size()));
744 return num_produced;
745}
746
747/**
748 * Combine triangles together into triangle strips, at this group and below.
749 */
751mesh_triangles(int flags) {
752 EggMesher mesher;
753 mesher.mesh(this, (flags & T_flat_shaded) != 0);
754
755 if ((flags & T_recurse) != 0) {
756 EggGroupNode::iterator ci;
757 for (ci = begin(); ci != end(); ++ci) {
758 if ((*ci)->is_of_type(EggGroupNode::get_class_type())) {
759 EggGroupNode *group_child = DCAST(EggGroupNode, *ci);
760 group_child->mesh_triangles(flags);
761 }
762 }
763 }
764}
765
766/**
767 * Creates PointLight primitives to reference any otherwise unreferences
768 * vertices discovered in this group or below.
769 */
772 // Create a temporary node to hold the EggPoint objects we might create
773 // while we iterate. (We don't add them during the iteration to avoid
774 // invalidating the iterator.)
775 PT(EggGroupNode) temp = new EggGroup("temp");
776
777 EggGroupNode::iterator ci;
778 for (ci = begin(); ci != end(); ++ci) {
779 if ((*ci)->is_of_type(EggGroupNode::get_class_type())) {
780 EggGroupNode *group_child = DCAST(EggGroupNode, *ci);
781 group_child->make_point_primitives();
782
783 } else if ((*ci)->is_of_type(EggVertexPool::get_class_type())) {
784 EggVertexPool *vpool = DCAST(EggVertexPool, *ci);
785 PT(EggPrimitive) prim = new EggPoint;
786 vpool->add_unused_vertices_to_prim(prim);
787 if (!prim->empty()) {
788 temp->add_child(prim);
789 }
790 }
791 }
792
793 steal_children(*temp);
794}
795
796/**
797 * Rename by stripping out the prefix
798 */
800rename_nodes(vector_string strip_prefix, bool recurse) {
801 int num_renamed = 0;
802 for (unsigned int ni = 0; ni < strip_prefix.size(); ++ni) {
803 string axe_name = strip_prefix[ni];
804 if (this->get_name().substr(0, axe_name.size()) == axe_name) {
805 string new_name = this->get_name().substr(axe_name.size());
806 // cout << "renaming " << this->get_name() << "->" << new_name << endl;
807 this->set_name(new_name);
808 num_renamed += 1;
809 }
810 }
811 if (recurse) {
812 EggGroupNode::iterator ci;
813 for (ci = begin(); ci != end(); ++ci) {
814 if ((*ci)->is_of_type(EggGroupNode::get_class_type())) {
815 EggGroupNode *group_child = DCAST(EggGroupNode, *ci);
816 num_renamed += group_child->rename_nodes(strip_prefix, recurse);
817 }
818 else if ((*ci)->is_of_type(EggNode::get_class_type())) {
819 EggNode *node_child = DCAST(EggNode, *ci);
820 num_renamed += node_child->rename_node(strip_prefix);
821 }
822 }
823 }
824 return num_renamed;
825}
826
827/**
828 * Removes all vertices from VertexPools within this group or below that are
829 * not referenced by at least one primitive. Also collapses together
830 * equivalent vertices, and renumbers all vertices after the operation so
831 * their indices are consecutive, beginning at zero. Returns the total number
832 * of vertices removed.
833 *
834 * Note that this operates on the VertexPools within this group level, without
835 * respect to primitives that reference these vertices (unlike other functions
836 * like strip_normals()). It is therefore most useful to call this on the
837 * EggData root, rather than on a subgroup within the hierarchy, since a
838 * VertexPool may appear anywhere in the hierarchy.
839 */
841remove_unused_vertices(bool recurse) {
842 int num_removed = 0;
843
844 Children::iterator ci, cnext;
845 ci = _children.begin();
846 while (ci != _children.end()) {
847 cnext = ci;
848 ++cnext;
849 EggNode *child = *ci;
850
851 if (child->is_of_type(EggVertexPool::get_class_type())) {
852 EggVertexPool *vpool = DCAST(EggVertexPool, child);
853 num_removed += vpool->remove_unused_vertices();
854
855 if (vpool->empty()) {
856 // If, after removing all the vertices, there's nothing left in the
857 // vertex pool, then remove the whole vertex pool.
858 _children.erase(ci);
859 }
860
861 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
862 if (recurse) {
863 num_removed += DCAST(EggGroupNode, child)->remove_unused_vertices(recurse);
864 }
865 }
866
867 ci = cnext;
868 }
869
870 return num_removed;
871}
872
873/**
874 * Removes primitives at this level and below which appear to be degenerate;
875 * e.g. polygons with fewer than 3 vertices, etc. Returns the number of
876 * primitives removed.
877 */
879remove_invalid_primitives(bool recurse) {
880 int num_removed = 0;
881
882 Children::iterator ci, cnext;
883 ci = _children.begin();
884 while (ci != _children.end()) {
885 cnext = ci;
886 ++cnext;
887 EggNode *child = *ci;
888
889 if (child->is_of_type(EggPrimitive::get_class_type())) {
890 EggPrimitive *prim = DCAST(EggPrimitive, child);
891 if (!prim->cleanup()) {
892 _children.erase(ci);
893 num_removed++;
894 }
895
896 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
897 if (recurse) {
898 num_removed += DCAST(EggGroupNode, child)->remove_invalid_primitives(recurse);
899 }
900 }
901
902 ci = cnext;
903 }
904
905 return num_removed;
906}
907
908/**
909 * Resets the connected_shading information on all primitives at this node and
910 * below, so that it may be accurately rederived by the next call to
911 * get_connected_shading().
912 *
913 * It may be a good idea to call remove_unused_vertices() as well, to
914 * establish the correct connectivity between common vertices.
915 */
918 Children::iterator ci;
919 for (ci = _children.begin(); ci != _children.end(); ++ci) {
920 EggNode *child = *ci;
921
922 if (child->is_of_type(EggPrimitive::get_class_type())) {
923 EggPrimitive *prim = DCAST(EggPrimitive, child);
925 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
926 DCAST(EggGroupNode, child)->clear_connected_shading();
927 }
928 }
929}
930
931/**
932 * Queries the connected_shading information on all primitives at this node
933 * and below, to ensure that it has been completely filled in before we start
934 * mucking around with vertices.
935 */
938 Children::iterator ci;
939 for (ci = _children.begin(); ci != _children.end(); ++ci) {
940 EggNode *child = *ci;
941
942 if (child->is_of_type(EggPrimitive::get_class_type())) {
943 EggPrimitive *prim = DCAST(EggPrimitive, child);
944 prim->get_connected_shading();
945 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
946 DCAST(EggGroupNode, child)->get_connected_shading();
947 }
948 }
949}
950
951/**
952 * Applies per-vertex normal and color to all vertices, if they are in fact
953 * per-vertex (and different for each vertex), or moves them to the primitive
954 * if they are all the same.
955 *
956 * After this call, either the primitive will have normals or its vertices
957 * will, but not both. Ditto for colors.
958 *
959 * If use_connected_shading is true, each polygon is considered in conjunction
960 * with all connected polygons; otherwise, each polygon is considered
961 * individually.
962 *
963 * If allow_per_primitive is false, S_per_face or S_overall will treated like
964 * S_per_vertex: normals and colors will always be assigned to the vertices.
965 * In this case, there will never be per-primitive colors or normals after
966 * this call returns. On the other hand, if allow_per_primitive is true, then
967 * S_per_face means that normals and colors should be assigned to the
968 * primitives, and removed from the vertices, as described above.
969 *
970 * This may create redundant vertices in the vertex pool, so it may be a good
971 * idea to follow this up with remove_unused_vertices().
972 */
974unify_attributes(bool use_connected_shading, bool allow_per_primitive,
975 bool recurse) {
976 Children::iterator ci;
977 for (ci = _children.begin(); ci != _children.end(); ++ci) {
978 EggNode *child = *ci;
979
980 if (child->is_of_type(EggPrimitive::get_class_type())) {
981 EggPrimitive *prim = DCAST(EggPrimitive, child);
982
983 EggPrimitive::Shading shading = EggPrimitive::S_per_vertex;
984
985 if (allow_per_primitive) {
986 shading = prim->get_shading();
987 if (use_connected_shading) {
988 shading = prim->get_connected_shading();
989 }
990 }
991
992 prim->unify_attributes(shading);
993
994 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
995 if (recurse) {
996 DCAST(EggGroupNode, child)->unify_attributes
997 (use_connected_shading, allow_per_primitive, recurse);
998 }
999 }
1000 }
1001}
1002
1003/**
1004 * Sets the last vertex of the triangle (or each component) to the primitive
1005 * normal and/or color, if the primitive is flat-shaded. This reflects the
1006 * OpenGL convention of storing flat-shaded properties on the last vertex,
1007 * although it is not usually a convention in Egg.
1008 *
1009 * This may create redundant vertices in the vertex pool, so it may be a good
1010 * idea to follow this up with remove_unused_vertices().
1011 */
1013apply_last_attribute(bool recurse) {
1014 Children::iterator ci;
1015 for (ci = _children.begin(); ci != _children.end(); ++ci) {
1016 EggNode *child = *ci;
1017
1018 if (child->is_of_type(EggPrimitive::get_class_type())) {
1019 EggPrimitive *prim = DCAST(EggPrimitive, child);
1020 prim->apply_last_attribute();
1021 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1022 if (recurse) {
1023 DCAST(EggGroupNode, child)->apply_last_attribute(recurse);
1024 }
1025 }
1026 }
1027}
1028
1029/**
1030 * Sets the first vertex of the triangle (or each component) to the primitive
1031 * normal and/or color, if the primitive is flat-shaded. This reflects the
1032 * DirectX convention of storing flat-shaded properties on the first vertex,
1033 * although it is not usually a convention in Egg.
1034 *
1035 * This may create redundant vertices in the vertex pool, so it may be a good
1036 * idea to follow this up with remove_unused_vertices().
1037 */
1039apply_first_attribute(bool recurse) {
1040 Children::iterator ci;
1041 for (ci = _children.begin(); ci != _children.end(); ++ci) {
1042 EggNode *child = *ci;
1043
1044 if (child->is_of_type(EggPrimitive::get_class_type())) {
1045 EggPrimitive *prim = DCAST(EggPrimitive, child);
1046 prim->apply_first_attribute();
1047 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1048 if (recurse) {
1049 DCAST(EggGroupNode, child)->apply_first_attribute(recurse);
1050 }
1051 }
1052 }
1053}
1054
1055/**
1056 * Intended as a followup to apply_last_attribute(), this also sets an
1057 * attribute on the first vertices of the primitive, if they don't already
1058 * have an attribute set, just so they end up with *something*.
1059 */
1061post_apply_flat_attribute(bool recurse) {
1062 Children::iterator ci;
1063 for (ci = _children.begin(); ci != _children.end(); ++ci) {
1064 EggNode *child = *ci;
1065
1066 if (child->is_of_type(EggPrimitive::get_class_type())) {
1067 EggPrimitive *prim = DCAST(EggPrimitive, child);
1069 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1070 if (recurse) {
1071 DCAST(EggGroupNode, child)->post_apply_flat_attribute(recurse);
1072 }
1073 }
1074 }
1075}
1076
1077/**
1078 * Returns true if there are any primitives (e.g. polygons) defined within
1079 * this group or below, false otherwise.
1080 */
1082has_primitives() const {
1083 Children::const_iterator ci;
1084 for (ci = _children.begin();
1085 ci != _children.end();
1086 ++ci) {
1087 if ((*ci)->has_primitives()) {
1088 return true;
1089 }
1090 }
1091
1092 return false;
1093}
1094
1095/**
1096 * Returns true if there are any primitives (e.g. polygons) defined within
1097 * this group or below, but the search does not include nested joints.
1098 */
1100joint_has_primitives() const {
1101 Children::const_iterator ci;
1102 for (ci = _children.begin();
1103 ci != _children.end();
1104 ++ci) {
1105 EggNode *child = (*ci);
1106
1107 if (!child->is_joint()) {
1108 if (child->joint_has_primitives()) {
1109 return true;
1110 }
1111 }
1112 }
1113
1114 return false;
1115}
1116
1117/**
1118 * Returns true if any of the primitives (e.g. polygons) defined within this
1119 * group or below have either face or vertex normals defined, false otherwise.
1120 */
1122has_normals() const {
1123 Children::const_iterator ci;
1124 for (ci = _children.begin();
1125 ci != _children.end();
1126 ++ci) {
1127 if ((*ci)->has_normals()) {
1128 return true;
1129 }
1130 }
1131
1132 return false;
1133}
1134
1135/**
1136 * Copies vertices used by the primitives at this group node (and below, if
1137 * recurse is true) into one or more new vertex pools, and updates the
1138 * primitives to reference these pools. It is up to the caller to parent the
1139 * newly-created vertex pools somewhere appropriate in the egg hierarchy.
1140 *
1141 * No more than max_vertices will be placed into any one vertex pool. This is
1142 * the sole criteria for splitting vertex pools.
1143 */
1145rebuild_vertex_pools(EggVertexPools &vertex_pools, unsigned int max_vertices,
1146 bool recurse) {
1147 Children::iterator ci;
1148 for (ci = _children.begin(); ci != _children.end(); ++ci) {
1149 EggNode *child = *ci;
1150
1151 if (child->is_of_type(EggPrimitive::get_class_type())) {
1152 typedef pvector< PT(EggVertex) > Vertices;
1153 Vertices vertices;
1154 EggPrimitive *prim = DCAST(EggPrimitive, child);
1155
1156 // Copy all of the vertices out.
1157 EggPrimitive::const_iterator pi;
1158 for (pi = prim->begin(); pi != prim->end(); ++pi) {
1159 vertices.push_back(*pi);
1160 }
1161
1162 typedef epvector<EggAttributes> Attributes;
1163 Attributes attributes;
1164
1165 if (prim->is_of_type(EggCompositePrimitive::get_class_type())) {
1166 // A compositive primitive has the additional complication of dealing
1167 // with its attributes.
1168 EggCompositePrimitive *cprim = DCAST(EggCompositePrimitive, prim);
1169 int i;
1170 int num_components = cprim->get_num_components();
1171 for (i = 0; i < num_components; i++) {
1172 attributes.push_back(*cprim->get_component(i));
1173 }
1174 }
1175
1176 prim->clear();
1177
1178 // Now look for a new home for the vertices. First, see if any of the
1179 // vertex pools we've already created already have a copy of each one of
1180 // the vertices.
1181 bool found_pool = false;
1182 EggVertexPool *best_pool = nullptr;
1183 int best_new_vertices = 0;
1184
1185 Vertices new_vertices;
1186 EggVertexPools::iterator vpi;
1187 for (vpi = vertex_pools.begin();
1188 vpi != vertex_pools.end() && !found_pool;
1189 ++vpi) {
1190 EggVertexPool *vertex_pool = (*vpi);
1191 int num_new_vertices = 0;
1192
1193 new_vertices.clear();
1194 new_vertices.reserve(vertices.size());
1195
1196 Vertices::const_iterator vi;
1197 for (vi = vertices.begin();
1198 vi != vertices.end() && !found_pool;
1199 ++vi) {
1200 EggVertex *vertex = (*vi);
1201 EggVertex *new_vertex = vertex_pool->find_matching_vertex(*vertex);
1202 new_vertices.push_back(new_vertex);
1203 if (new_vertex == nullptr) {
1204 ++num_new_vertices;
1205 }
1206 }
1207
1208 if (num_new_vertices == 0) {
1209 // Great, we found a vertex pool that already shares all these
1210 // vertices. No need to look any further.
1211 found_pool = true;
1212
1213 } else if (vertex_pool->size() + num_new_vertices <= max_vertices) {
1214 // We would have to add some vertices to this pool, so this vertex
1215 // pool qualifies only if the number of vertices we have to add
1216 // would still keep it within our limit.
1217 if (best_pool == nullptr ||
1218 num_new_vertices < best_new_vertices) {
1219 // This is currently our most favorable vertex pool.
1220 best_pool = vertex_pool;
1221 best_new_vertices = num_new_vertices;
1222 }
1223 }
1224 }
1225
1226 if (!found_pool) {
1227 if (best_pool == nullptr) {
1228 // There was no vertex pool that qualified. We will have to create
1229 // a new vertex pool.
1230 best_pool = new EggVertexPool("");
1231 vertex_pools.push_back(best_pool);
1232 }
1233
1234 new_vertices.clear();
1235 new_vertices.reserve(vertices.size());
1236
1237 Vertices::const_iterator vi;
1238 for (vi = vertices.begin(); vi != vertices.end(); ++vi) {
1239 EggVertex *vertex = (*vi);
1240 EggVertex *new_vertex = best_pool->create_unique_vertex(*vertex);
1241 new_vertex->copy_grefs_from(*vertex);
1242 new_vertices.push_back(new_vertex);
1243 }
1244 }
1245
1246 Vertices::const_iterator vi;
1247 nassertv(new_vertices.size() == vertices.size());
1248 for (vi = new_vertices.begin(); vi != new_vertices.end(); ++vi) {
1249 EggVertex *new_vertex = (*vi);
1250 nassertv(new_vertex != nullptr);
1251 prim->add_vertex(new_vertex);
1252 }
1253
1254 if (prim->is_of_type(EggCompositePrimitive::get_class_type())) {
1255 // Now restore the composite attributes.
1256 EggCompositePrimitive *cprim = DCAST(EggCompositePrimitive, prim);
1257 int i;
1258 int num_components = cprim->get_num_components();
1259 nassertv(num_components == (int)attributes.size());
1260 for (i = 0; i < num_components; i++) {
1261 cprim->set_component(i, &attributes[i]);
1262 }
1263 }
1264
1265 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1266 if (recurse) {
1267 DCAST(EggGroupNode, child)->rebuild_vertex_pools(vertex_pools, max_vertices, recurse);
1268 }
1269 }
1270 }
1271}
1272
1273/**
1274 * This function is called from within EggGroupNode whenever the parentage of
1275 * the node has changed. It should update the depth and under_instance flags
1276 * accordingly.
1277 *
1278 * Offset is the difference between the old depth value and the new value. It
1279 * should be consistent with the supplied depth value. If it is not, we have
1280 * some error.
1281 */
1282void EggGroupNode::
1283update_under(int depth_offset) {
1284 EggNode::update_under(depth_offset);
1285
1286 Children::iterator ci;
1287 for (ci = _children.begin();
1288 ci != _children.end();
1289 ++ci) {
1290 nassertv((*ci)->get_parent() == this);
1291 (*ci)->update_under(depth_offset);
1292 }
1293}
1294
1295/**
1296 * This is called from within the egg code by transform(). It applies a
1297 * transformation matrix to the current node in some sensible way, then
1298 * continues down the tree.
1299 *
1300 * The first matrix is the transformation to apply; the second is its inverse.
1301 * The third parameter is the coordinate system we are changing to, or
1302 * CS_default if we are not changing coordinate systems.
1303 */
1304void EggGroupNode::
1305r_transform(const LMatrix4d &mat, const LMatrix4d &inv,
1306 CoordinateSystem to_cs) {
1307 Children::iterator ci;
1308 for (ci = _children.begin();
1309 ci != _children.end();
1310 ++ci) {
1311 (*ci)->r_transform(mat, inv, to_cs);
1312 }
1313}
1314
1315/**
1316 * This is called from within the egg code by transform_vertices_only()(). It
1317 * applies a transformation matrix to the current node in some sensible way
1318 * (if the current node is a vertex pool with vertices), then continues down
1319 * the tree.
1320 */
1321void EggGroupNode::
1322r_transform_vertices(const LMatrix4d &mat) {
1323 Children::iterator ci;
1324 for (ci = _children.begin();
1325 ci != _children.end();
1326 ++ci) {
1327 (*ci)->r_transform_vertices(mat);
1328 }
1329}
1330
1331/**
1332 * This is only called immediately after loading an egg file from disk, to
1333 * propagate the value found in the CoordinateSystem entry (or the default
1334 * Y-up coordinate system) to all nodes that care about what the coordinate
1335 * system is.
1336 */
1337void EggGroupNode::
1338r_mark_coordsys(CoordinateSystem cs) {
1339 Children::iterator ci;
1340 for (ci = _children.begin();
1341 ci != _children.end();
1342 ++ci) {
1343 (*ci)->r_mark_coordsys(cs);
1344 }
1345}
1346
1347/**
1348 * The recursive implementation of flatten_transforms().
1349 */
1350void EggGroupNode::
1351r_flatten_transforms() {
1352 Children::iterator ci;
1353 for (ci = _children.begin();
1354 ci != _children.end();
1355 ++ci) {
1356 (*ci)->r_flatten_transforms();
1357 }
1358}
1359
1360/**
1361 * The recursive implementation of apply_texmats().
1362 */
1363void EggGroupNode::
1364r_apply_texmats(EggTextureCollection &textures) {
1365 Children::iterator ci;
1366 for (ci = _children.begin();
1367 ci != _children.end();
1368 ++ci) {
1369 (*ci)->r_apply_texmats(textures);
1370 }
1371}
1372
1373/**
1374 * Walks the tree, looking for an EggCoordinateSystem entry. If one is found,
1375 * extracts it and returns its value. If multiple entries are found, extracts
1376 * all of them and returns CS_invalid if they disagree.
1377 */
1378CoordinateSystem EggGroupNode::
1379find_coordsys_entry() {
1380 CoordinateSystem coordsys = CS_default;
1381
1382 // We can do this cicnext iteration through the list as we modify it, only
1383 // because we know this works with an STL list type container. If this were
1384 // a vector or a set, this wouldn't necessarily work.
1385
1386 Children::iterator ci, cnext;
1387 ci = _children.begin();
1388 while (ci != _children.end()) {
1389 cnext = ci;
1390 ++cnext;
1391 EggNode *child = *ci;
1392
1393 if (child->is_of_type(EggCoordinateSystem::get_class_type())) {
1394 CoordinateSystem new_cs =
1395 DCAST(EggCoordinateSystem, child)->get_value();
1396
1397 // Now remove the CoordinateSystem entry from our child list.
1398 prepare_remove_child(child);
1399 _children.erase(ci);
1400
1401 if (new_cs != CS_default) {
1402 if (coordsys != CS_default && coordsys != new_cs) {
1403 coordsys = CS_invalid;
1404 } else {
1405 coordsys = new_cs;
1406 }
1407 }
1408
1409 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1410 CoordinateSystem new_cs =
1411 DCAST(EggGroupNode, child)->find_coordsys_entry();
1412 if (new_cs != CS_default) {
1413 if (coordsys != CS_default && coordsys != new_cs) {
1414 coordsys = CS_invalid;
1415 } else {
1416 coordsys = new_cs;
1417 }
1418 }
1419 }
1420
1421 ci = cnext;
1422 }
1423
1424 return coordsys;
1425}
1426
1427/**
1428 * Walks the tree, looking for EggTextures. Each EggTexture that is found is
1429 * removed from the hierarchy and added to the EggTextureCollection. Returns
1430 * the number of EggTextures found.
1431 */
1432int EggGroupNode::
1433find_textures(EggTextureCollection *collection) {
1434 int num_found = 0;
1435
1436 // We can do this cicnext iteration through the list as we modify it, only
1437 // because we know this works with an STL list type container. If this were
1438 // a vector or a set, this wouldn't necessarily work.
1439
1440 Children::iterator ci, cnext;
1441 ci = _children.begin();
1442 while (ci != _children.end()) {
1443 cnext = ci;
1444 ++cnext;
1445 EggNode *child = *ci;
1446
1447 if (child->is_of_type(EggTexture::get_class_type())) {
1448 PT_EggTexture tex = DCAST(EggTexture, child);
1449
1450 // Now remove the EggTexture entry from our child list.
1451 prepare_remove_child(tex);
1452 _children.erase(ci);
1453
1454 // And add it to the collection.
1455 collection->add_texture(tex);
1456 num_found++;
1457
1458 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1459 num_found +=
1460 DCAST(EggGroupNode, child)->find_textures(collection);
1461 }
1462
1463 ci = cnext;
1464 }
1465
1466 return num_found;
1467}
1468
1469/**
1470 * Walks the tree, looking for EggMaterials. Each EggMaterial that is found
1471 * is removed from the hierarchy and added to the EggMaterialCollection.
1472 * Returns the number of EggMaterials found.
1473 */
1474int EggGroupNode::
1475find_materials(EggMaterialCollection *collection) {
1476 int num_found = 0;
1477
1478 // We can do this cicnext iteration through the list as we modify it, only
1479 // because we know this works with an STL list type container. If this were
1480 // a vector or a set, this wouldn't necessarily work.
1481
1482 Children::iterator ci, cnext;
1483 ci = _children.begin();
1484 while (ci != _children.end()) {
1485 cnext = ci;
1486 ++cnext;
1487 EggNode *child = *ci;
1488
1489 if (child->is_of_type(EggMaterial::get_class_type())) {
1490 PT_EggMaterial tex = DCAST(EggMaterial, child);
1491
1492 // Now remove the EggMaterial entry from our child list.
1493 prepare_remove_child(tex);
1494 _children.erase(ci);
1495
1496 // And add it to the collection.
1497 collection->add_material(tex);
1498 num_found++;
1499
1500 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1501 num_found +=
1502 DCAST(EggGroupNode, child)->find_materials(collection);
1503 }
1504
1505 ci = cnext;
1506 }
1507
1508 return num_found;
1509}
1510
1511/**
1512 * Walks the tree and locates unloaded external reference nodes, which it
1513 * attempts to locate and load in. The reference node is replaced with the
1514 * entire subtree loaded. This is intended to be called from
1515 * EggData::load_externals().
1516 */
1517bool EggGroupNode::
1518r_load_externals(const DSearchPath &searchpath, CoordinateSystem coordsys,
1519 BamCacheRecord *record) {
1520 bool success = true;
1521
1522 Children::iterator ci;
1523 for (ci = _children.begin();
1524 ci != _children.end();
1525 ++ci) {
1526 EggNode *child = *ci;
1527 if (child->is_of_type(EggExternalReference::get_class_type())) {
1528 PT(EggExternalReference) ref = DCAST(EggExternalReference, child);
1529
1530 // Replace the reference with an empty group node. When we load the
1531 // external file successfully, we'll put its contents here.
1532 Filename filename = ref->get_filename();
1533 EggGroupNode *new_node =
1535 replace(ci, new_node);
1536
1537 if (!EggData::resolve_egg_filename(filename, searchpath)) {
1538 egg_cat.error()
1539 << "Could not locate " << filename << " in "
1540 << searchpath << "\n";
1541 } else {
1542 // Now define a new EggData structure to hold the external reference,
1543 // and load it.
1544 EggData ext_data;
1545 ext_data.set_coordinate_system(coordsys);
1546 ext_data.set_auto_resolve_externals(true);
1547 if (ext_data.read(filename)) {
1548 // The external file was read correctly. Add its contents into the
1549 // tree at this point.
1550 if (record != nullptr) {
1551 record->add_dependent_file(filename);
1552 }
1553
1554 success =
1555 ext_data.load_externals(searchpath, record)
1556 && success;
1557 new_node->steal_children(ext_data);
1558 }
1559 }
1560
1561 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1562 EggGroupNode *group_child = DCAST(EggGroupNode, child);
1563 success =
1564 group_child->r_load_externals(searchpath, coordsys, record)
1565 && success;
1566 }
1567 }
1568 return success;
1569}
1570
1571
1572/**
1573 * Marks the node as a child of the group. This is an internal function
1574 * called by the STL-like functions push_back() and insert(), in preparation
1575 * for actually adding the child.
1576 *
1577 * It is an error to add a node that is already a child of this group or some
1578 * other group.
1579 */
1580void EggGroupNode::
1581prepare_add_child(EggNode *node) {
1582 nassertv(node != nullptr);
1585 // Make sure the node is not already a child of some other group.
1586 nassertv(node->get_parent() == nullptr);
1587 nassertv(node->get_depth() == 0);
1588 node->_parent = this;
1589
1590 node->update_under(get_depth() + 1);
1591}
1592
1593
1594/**
1595 * Marks the node as removed from the group. This is an internal function
1596 * called by the STL-like functions pop_back() and erase(), in preparation for
1597 * actually doing the removal.
1598 *
1599 * It is an error to attempt to remove a node that is not already a child of
1600 * this group.
1601 */
1602void EggGroupNode::
1603prepare_remove_child(EggNode *node) {
1604 nassertv(node != nullptr);
1605 // Make sure the node is in fact a child of this group.
1606 nassertv(node->get_parent() == this);
1607 nassertv(node->get_depth() == get_depth() + 1);
1608 node->_parent = nullptr;
1609
1610 node->update_under(-(get_depth() + 1));
1611}
1612
1613
1614
1615/**
1616 * This is part of the implementation of recompute_vertex_normals(). It walks
1617 * the scene graph at this group node and below, identifying all the polygons
1618 * and the vertices they have in common.
1619 */
1620void EggGroupNode::
1621r_collect_vertex_normals(EggGroupNode::NVertexCollection &collection,
1622 double threshold, CoordinateSystem cs) {
1623 // We can do this cicnext iteration through the list as we modify it, only
1624 // because we know this works with an STL list type container. If this were
1625 // a vector or a set, this wouldn't necessarily work.
1626
1627 Children::iterator ci, cnext;
1628 ci = _children.begin();
1629 while (ci != _children.end()) {
1630 cnext = ci;
1631 ++cnext;
1632 EggNode *child = *ci;
1633
1634 if (child->is_of_type(EggPolygon::get_class_type())) {
1635 EggPolygon *polygon = DCAST(EggPolygon, child);
1636 polygon->clear_normal();
1637
1638 NVertexReference ref;
1639 ref._polygon = polygon;
1640 if (!polygon->calculate_normal(ref._normal, cs)) {
1641 // The polygon is degenerate. Remove it.
1642
1643 prepare_remove_child(child);
1644 _children.erase(ci);
1645
1646 } else {
1647 // Now add each vertex from the polygon separately to our collection.
1648 size_t num_vertices = polygon->size();
1649 for (size_t i = 0; i < num_vertices; i++) {
1650 EggVertex *vertex = polygon->get_vertex(i);
1651 ref._vertex = i;
1652 collection[vertex->get_pos3()].push_back(ref);
1653 }
1654 }
1655
1656 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1657 EggGroupNode *group = DCAST(EggGroupNode, child);
1658
1659 // We can't share vertices across an Instance node. Don't even bother
1660 // trying. Instead, just restart.
1661 if (group->is_under_instance()) {
1662 group->recompute_vertex_normals(threshold, cs);
1663 } else {
1664 group->r_collect_vertex_normals(collection, threshold, cs);
1665 }
1666 }
1667
1668 ci = cnext;
1669 }
1670}
1671
1672/**
1673 * This is part of the implementation of recompute_vertex_normals(). It
1674 * accepts a group of polygons and their common normals, and computes the same
1675 * normal for all their shared vertices.
1676 */
1677void EggGroupNode::
1678do_compute_vertex_normals(const NVertexGroup &group) {
1679 nassertv(!group.empty());
1680
1681 // Determine the common normal. This is simply the average of all the
1682 // polygon normals that share this vertex.
1683 LNormald normal(0.0, 0.0, 0.0);
1684 NVertexGroup::const_iterator gi;
1685 for (gi = group.begin(); gi != group.end(); ++gi) {
1686 const NVertexReference &ref = (*gi);
1687 normal += ref._normal;
1688 }
1689
1690 normal /= (double)group.size();
1691 normal.normalize();
1692
1693 // Now we have the common normal; apply it to all the vertices.
1694
1695 for (gi = group.begin(); gi != group.end(); ++gi) {
1696 const NVertexReference &ref = (*gi);
1697 EggVertex *vertex = ref._polygon->get_vertex(ref._vertex);
1698 EggVertexPool *pool = vertex->get_pool();
1699
1700 EggVertex new_vertex(*vertex);
1701 new_vertex.set_normal(normal);
1702 EggVertex *unique = pool->create_unique_vertex(new_vertex);
1703 unique->copy_grefs_from(*vertex);
1704
1705 ref._polygon->set_vertex(ref._vertex, unique);
1706 }
1707}
1708
1709/**
1710 * This is part of the implementation of recompute_tangent_binormal(). It
1711 * walks the scene graph at this group node and below, identifying all the
1712 * polygons and the vertices they have in common.
1713 */
1714void EggGroupNode::
1715r_collect_tangent_binormal(const GlobPattern &uv_name,
1717 Children::iterator ci;
1718 for (ci = _children.begin(); ci != _children.end(); ++ci) {
1719 EggNode *child = *ci;
1720
1721 if (child->is_of_type(EggPolygon::get_class_type())) {
1722 EggPolygon *polygon = DCAST(EggPolygon, child);
1723
1724 TBNVertexReference ref;
1725 ref._polygon = polygon;
1726
1727 // Now add each vertex from the polygon separately to our collection.
1728 size_t num_vertices = polygon->size();
1729 for (size_t i = 0; i < num_vertices; i++) {
1730 // We look at the triangle formed by each three consecutive vertices
1731 // to determine the s direction and t direction at each vertex. v1 is
1732 // the key vertex, the one at position i; v2 is vertex i + 1, and v3
1733 // is vertex i - 1.
1734 EggVertex *v1 = polygon->get_vertex(i);
1735 EggVertex *v2 = polygon->get_vertex((i + 1) % num_vertices);
1736 EggVertex *v3 = polygon->get_vertex((i + num_vertices - 1) % num_vertices);
1737 if (v1->has_normal() || polygon->has_normal()) {
1738 // Go through all of the UV names on the vertex, looking for one
1739 // that matches the glob pattern.
1741 for (uvi = v1->uv_begin(); uvi != v1->uv_end(); ++uvi) {
1742 EggVertexUV *uv_obj = (*uvi);
1743 string name = uv_obj->get_name();
1744 if (uv_name.matches(name) &&
1745 v2->has_uv(name) && v3->has_uv(name)) {
1746 TBNVertexValue value;
1747 value._uv_name = name;
1748 value._pos = v1->get_pos3();
1749 if (v1->has_normal()) {
1750 value._normal = v1->get_normal();
1751 } else {
1752 value._normal = polygon->get_normal();
1753 }
1754 value._uv = v1->get_uv(name);
1755
1756 // Compute the s direction and t direction for this vertex.
1757 LPoint3d p1 = v1->get_pos3();
1758 LPoint3d p2 = v2->get_pos3();
1759 LPoint3d p3 = v3->get_pos3();
1760
1761 LTexCoordd w1 = v1->get_uv(name);
1762 LTexCoordd w2 = v2->get_uv(name);
1763 LTexCoordd w3 = v3->get_uv(name);
1764
1765 // Check the facing of the texture; we will have to split
1766 // vertices whose UV's are mirrored along a seam. The facing is
1767 // determined by the winding order of the texcoords on the
1768 // polygon. A front-facing polygon should not contribute to the
1769 // tangent and binormal of a back-facing polygon, and vice-
1770 // versa.
1771 value._facing = is_right(w1 - w2, w3 - w1);
1772
1773 double x1 = p2[0] - p1[0];
1774 double x2 = p3[0] - p1[0];
1775 double y1 = p2[1] - p1[1];
1776 double y2 = p3[1] - p1[1];
1777 double z1 = p2[2] - p1[2];
1778 double z2 = p3[2] - p1[2];
1779
1780 double s1 = w2[0] - w1[0];
1781 double s2 = w3[0] - w1[0];
1782 double t1 = w2[1] - w1[1];
1783 double t2 = w3[1] - w1[1];
1784
1785 double denom = (s1 * t2 - s2 * t1);
1786 if (denom == 0.0) {
1787 ref._sdir.set(0.0, 0.0, 0.0);
1788 ref._tdir.set(0.0, 0.0, 0.0);
1789 } else {
1790 double r = 1.0 / denom;
1791 ref._sdir.set((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
1792 (t2 * z1 - t1 * z2) * r);
1793 ref._tdir.set((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
1794 (s1 * z2 - s2 * z1) * r);
1795 }
1796
1797 // Store the vertex referenced to the polygon.
1798 ref._vertex = i;
1799 collection[value].push_back(ref);
1800 }
1801 }
1802 }
1803 }
1804
1805 } else if (child->is_of_type(EggGroupNode::get_class_type())) {
1806 EggGroupNode *group = DCAST(EggGroupNode, child);
1807
1808 // We can't share vertices across an Instance node. Don't even bother
1809 // trying. Instead, just restart.
1810 if (group->is_under_instance()) {
1811 group->recompute_tangent_binormal(uv_name);
1812 } else {
1813 group->r_collect_tangent_binormal(uv_name, collection);
1814 }
1815 }
1816 }
1817}
1818
1819/**
1820 * This is part of the implementation of recompute_tangent_binormal(). It
1821 * accepts a group of polygons and their common normals and UV's, and computes
1822 * the tangent and binormal for all their shared vertices.
1823 */
1824void EggGroupNode::
1825do_compute_tangent_binormal(const TBNVertexValue &value,
1826 const TBNVertexGroup &group) {
1827 nassertv(!group.empty());
1828
1829 // Accumulate together all of the s vectors and t vectors computed for the
1830 // different vertices that are together here.
1831 LNormald sdir(0.0, 0.0, 0.0);
1832 LNormald tdir(0.0, 0.0, 0.0);
1833
1834 TBNVertexGroup::const_iterator gi;
1835 for (gi = group.begin(); gi != group.end(); ++gi) {
1836 const TBNVertexReference &ref = (*gi);
1837 sdir += ref._sdir;
1838 tdir += ref._tdir;
1839 }
1840
1841 // If sdir andor tdir are zero, choose an arbitrary vector instead. (This
1842 // is really the only reason we normalize sdir and tdir, though it also
1843 // helps stabilize the math below in case the vectors are very small but not
1844 // quite zero.)
1845 if (!sdir.normalize()) {
1846 sdir.set(1.0, 0.0, 0.0);
1847 }
1848 if (!tdir.normalize()) {
1849 tdir = sdir.cross(LNormald(0.0, 0.0, -1.0));
1850 }
1851
1852 LNormald tangent = (sdir - value._normal * value._normal.dot(sdir));
1853 tangent.normalize();
1854
1855 LNormald binormal = cross(value._normal, tangent);
1856 if (dot(binormal, tdir) < 0.0f) {
1857 binormal = -binormal;
1858 }
1859 // Shouldn't need to normalize this, but we do just for good measure.
1860 binormal.normalize();
1861
1862 // Now we have the common tangent and binormal; apply them to all the
1863 // vertices.
1864
1865 for (gi = group.begin(); gi != group.end(); ++gi) {
1866 const TBNVertexReference &ref = (*gi);
1867 EggVertex *vertex = ref._polygon->get_vertex(ref._vertex);
1868 EggVertexPool *pool = vertex->get_pool();
1869
1870 EggVertex new_vertex(*vertex);
1871 EggVertexUV *uv_obj = new_vertex.modify_uv_obj(value._uv_name);
1872 nassertv(uv_obj != nullptr);
1873 uv_obj->set_tangent(tangent);
1874 uv_obj->set_binormal(binormal);
1875
1876 EggVertex *unique = pool->create_unique_vertex(new_vertex);
1877 unique->copy_grefs_from(*vertex);
1878
1879 ref._polygon->set_vertex(ref._vertex, unique);
1880 }
1881}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record.
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition dSearchPath.h:28
The base class for primitives such as triangle strips and triangle fans, which include several compon...
get_num_components
Returns the number of individual component triangles within the composite.
set_component
Changes the attributes for the nth component triangle.
get_component
Returns the attributes for the nth component triangle.
The <CoordinateSystem> entry at the top of an egg file.
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition eggData.h:37
static bool resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath=DSearchPath())
Looks for the indicated filename, first along the indicated searchpath, and then along the model_path...
Definition eggData.cxx:44
set_auto_resolve_externals
Indicates whether the EggData object will automatically resolve any external references when read() i...
Definition eggData.h:72
bool load_externals(const DSearchPath &searchpath=DSearchPath())
Loads up all the egg files referenced by <File> entries within the egg structure, and inserts their c...
Definition eggData.cxx:162
set_coordinate_system
Changes the coordinate system of the EggData.
Definition eggData.h:73
bool read(Filename filename, std::string display_name=std::string())
Opens the indicated filename and reads the egg data contents from it.
Definition eggData.cxx:65
Defines a reference to another egg file which should be inserted at this point.
This is an egg node that contains a filename.
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
virtual std::string get_default_extension() const
Returns the default extension for this filename type.
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.
int remove_unused_vertices(bool recurse)
Removes all vertices from VertexPools within this group or below that are not referenced by at least ...
virtual void write(std::ostream &out, int indent_level) const
Writes the group and all of its children to the indicated output stream in Egg format.
int remove_invalid_primitives(bool recurse)
Removes primitives at this level and below which appear to be degenerate; e.g.
void strip_normals()
Removes all normals from primitives, and the vertices they reference, at this node and below.
EggNode * find_child(const std::string &name) const
Returns the child of this node whose name is the indicated string, or NULL if there is no child of th...
void get_connected_shading()
Queries the connected_shading information on all primitives at this node and below,...
void resolve_filenames(const DSearchPath &searchpath)
Walks the tree and attempts to resolve any filenames encountered.
bool has_absolute_pathnames() const
Returns true if any nodes at this level and below include a reference to a file via an absolute pathn...
void apply_first_attribute(bool recurse)
Sets the first vertex of the triangle (or each component) to the primitive normal and/or color,...
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
int rename_nodes(vector_string strip_prefix, bool recurse)
Rename by stripping out the prefix.
bool recompute_tangent_binormal_auto()
This function recomputes the tangent and binormal for any texture coordinate set that affects a norma...
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...
EggNode * get_next_child()
Returns the next child in the group's list of children since the last call to get_first_child() or ge...
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...
bool recompute_tangent_binormal(const GlobPattern &uv_name)
This function recomputes the tangent and binormal for the named texture coordinate set for all vertic...
void apply_last_attribute(bool recurse)
Sets the last vertex of the triangle (or each component) to the primitive normal and/or color,...
void make_point_primitives()
Creates PointLight primitives to reference any otherwise unreferences vertices discovered in this gro...
void unify_attributes(bool use_connected_shading, bool allow_per_primitive, bool recurse)
Applies per-vertex normal and color to all vertices, if they are in fact per-vertex (and different fo...
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 recompute_polygon_normals(CoordinateSystem cs=CS_default)
Recomputes all the polygon normals for polygon geometry at this group node and below so that they acc...
void recompute_vertex_normals(double threshold, CoordinateSystem cs=CS_default)
Recomputes all the vertex normals for polygon geometry at this group node and below so that they accu...
static bool is_right(const LVector2d &v1, const LVector2d &v2)
Returns true if the 2-d v1 is to the right of v2.
virtual bool joint_has_primitives() const
Returns true if there are any primitives (e.g.
void force_filenames(const Filename &directory)
Similar to resolve_filenames, but each non-absolute filename encountered is arbitrarily taken to be i...
void clear_connected_shading()
Resets the connected_shading information on all primitives at this node and below,...
void replace(iterator position, PT(EggNode) x)
Replaces the node at the indicated position with the indicated node.
virtual bool has_primitives() const
Returns true if there are any primitives (e.g.
void steal_children(EggGroupNode &other)
Moves all the children from the other node to this one.
virtual bool has_normals() const
Returns true if any of the primitives (e.g.
void reverse_vertex_ordering()
Reverses the vertex ordering of all polygons defined at this node and below.
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition eggGroup.h:34
This is a collection of materials by MRef name.
bool add_material(EggMaterial *material)
Explicitly adds a new material to the collection.
Collects together unrelated EggPrimitives, determines their edge connectivity, and generates a set of...
Definition eggMesher.h:33
void mesh(EggGroupNode *group, bool flat_shaded)
Accepts an EggGroupNode, which contains a set of EggPrimitives–typically, triangles and quads–as chil...
Definition eggMesher.cxx:49
A base class for things that may be directly added into the egg hierarchy.
Definition eggNode.h:36
virtual bool is_joint() const
Returns true if this particular node represents a <Joint> entry or not.
Definition eggNode.cxx:65
bool is_under_instance() const
Returns true if there is an <Instance> node somewhere in the egg tree at or above this node,...
Definition eggNode.I:68
int rename_node(vector_string strip_prefix)
Rename by stripping out the prefix.
Definition eggNode.cxx:34
get_depth
Returns the number of nodes above this node in the egg hierarchy.
Definition eggNode.h:49
A single point, or a collection of points as defined by a single <PointLight> entry.
Definition eggPoint.h:25
A single polygon.
Definition eggPolygon.h:24
bool calculate_normal(LNormald &result, CoordinateSystem cs=CS_default) const
Calculates the true polygon normal–the vector pointing out of the front of the polygon–based on the v...
bool recompute_polygon_normal(CoordinateSystem cs=CS_default)
Recalculates the normal according to the order of the vertices, and sets it.
Definition eggPolygon.I:43
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
virtual void unify_attributes(Shading shading)
If the shading property is S_per_vertex, ensures that all vertices have a normal and a color,...
get_shading
Returns the shading properties apparent on this particular primitive.
get_vertex
Returns a particular index based on its index number.
virtual void apply_first_attribute()
Sets the first vertex of the triangle (or each component) to the primitive normal and/or color,...
get_connected_shading
Determines what sort of shading properties this primitive's connected neighbors have.
set_vertex
Replaces a particular vertex based on its index number in the list of vertices.
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
virtual void post_apply_flat_attribute()
Intended as a followup to apply_last_attribute(), this also sets an attribute on the first vertices o...
virtual void reverse_vertex_ordering()
Reverses the ordering of the vertices in this primitive, if appropriate, in order to change the direc...
void clear()
Removes all of the vertices from the primitive.
void clear_connected_shading()
Resets the connected_shading member in this primitive, so that get_connected_shading() will recompute...
virtual void apply_last_attribute()
Sets the last vertex of the triangle (or each component) to the primitive normal and/or color,...
virtual bool cleanup()
Cleans up modeling errors in whatever context this makes sense.
This is a collection of textures by TRef name.
bool add_texture(EggTexture *texture)
Explicitly adds a new texture to the collection.
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_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_alpha_filename
Returns the separate file assigned for the alpha channel.
Definition eggTexture.h:347
set_alpha_filename
Specifies a separate file that will be loaded in with the 1- or 3-component texture and applied as th...
Definition eggTexture.h:347
has_alpha_filename
Returns true if a separate file for the alpha component has been applied, false otherwise.
Definition eggTexture.h:347
A collection of vertices.
int remove_unused_vertices()
Removes all vertices from the pool that are not referenced by at least one primitive.
EggVertex * find_matching_vertex(const EggVertex &copy)
If the EggVertexPool already has a vertex matching the indicated vertex, returns it; otherwise,...
void add_unused_vertices_to_prim(EggPrimitive *prim)
Adds all of the unused vertices in this vertex pool to the indicated primitive, in ascending order.
size_type size() const
Returns the number of 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.
bool empty() const
Returns true if the pool is empty.
The set of UV's that may or may not be assigned to a vertex.
Definition eggVertexUV.h:29
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition eggVertex.h:39
bool has_uv() const
Returns true if the vertex has an unnamed UV coordinate pair, false otherwise.
Definition eggVertex.I:158
EggVertexPool * get_pool() const
Returns the vertex pool this vertex belongs in.
Definition eggVertex.I:19
LTexCoordd get_uv() const
Returns the unnamed UV coordinate pair on the vertex.
Definition eggVertex.I:179
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
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition eggVertex.I:131
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
bool is_local() const
Returns true if the filename is local, e.g.
Definition filename.I:549
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition filename.I:386
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition globPattern.h:32
bool matches(const std::string &candidate) const
Returns true if the candidate string matches the pattern, false otherwise.
void ref() const
Explicitly increments the reference count.
bool test_ref_count_integrity() const
Does some easy checks to make sure that the reference count isn't completely bogus.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
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 hierarchy of directories and files that appears to be one continuous file system,...
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
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 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.