Panda3D
 All Classes Functions Variables Enumerations
eggGroup.cxx
1 // Filename: eggGroup.cxx
2 // Created by: drose (16Jan99)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "eggGroup.h"
16 #include "eggMiscFuncs.h"
17 #include "eggVertexPool.h"
18 #include "eggBin.h"
19 #include "lexerDefs.h"
20 
21 #include "indent.h"
22 #include "string_utils.h"
23 #include "lmatrix.h"
24 #include "dcast.h"
25 
26 
27 TypeHandle EggGroup::_type_handle;
28 
29 ////////////////////////////////////////////////////////////////////
30 // Function: EggGroup::Constructor
31 // Access: Published
32 // Description:
33 ////////////////////////////////////////////////////////////////////
34 EggGroup::
35 EggGroup(const string &name) : EggGroupNode(name) {
36  _flags = 0;
37  _flags2 = 0;
38  _fps = 0.0;
39  _blend_mode = BM_unspecified;
40  _blend_operand_a = BO_unspecified;
41  _blend_operand_b = BO_unspecified;
42  _blend_color = LColor::zero();
43  _u_speed = 0;
44  _v_speed = 0;
45  _w_speed = 0;
46  _r_speed = 0;
47 }
48 
49 ////////////////////////////////////////////////////////////////////
50 // Function: EggGroup::Copy Constructor
51 // Access: Published
52 // Description:
53 ////////////////////////////////////////////////////////////////////
54 EggGroup::
55 EggGroup(const EggGroup &copy) {
56  (*this) = copy;
57 }
58 
59 ////////////////////////////////////////////////////////////////////
60 // Function: EggGroup::Copy assignment operator
61 // Access: Published
62 // Description:
63 ////////////////////////////////////////////////////////////////////
64 EggGroup &EggGroup::
65 operator = (const EggGroup &copy) {
66  EggTransform::operator = (copy);
67  _flags = copy._flags;
68  _flags2 = copy._flags2;
69  _collide_mask = copy._collide_mask;
70  _from_collide_mask = copy._from_collide_mask;
71  _into_collide_mask = copy._into_collide_mask;
72  _billboard_center = copy._billboard_center;
73  _object_types = copy._object_types;
74  _collision_name = copy._collision_name;
75  _fps = copy._fps;
76  _lod = copy._lod;
77  _blend_mode = copy._blend_mode;
78  _blend_operand_a = copy._blend_operand_a;
79  _blend_operand_b = copy._blend_operand_b;
80  _blend_color = copy._blend_color;
81  _tag_data = copy._tag_data;
82  _u_speed = copy._u_speed;
83  _v_speed = copy._v_speed;
84  _w_speed = copy._w_speed;
85  _r_speed = copy._r_speed;
86  _default_pose = copy._default_pose;
87 
89  _vref = copy._vref;
90 
91  // We must walk through the vertex ref list, and flag each vertex as
92  // now reffed by this group.
93  VertexRef::iterator vri;
94  for (vri = _vref.begin(); vri != _vref.end(); ++vri) {
95  EggVertex *vert = (*vri).first;
96 
97  bool inserted = vert->_gref.insert(this).second;
98  // Did the group not exist previously in the vertex's gref list?
99  // If it was there already, we must be out of sync between
100  // vertices and groups.
101  nassertr(inserted, *this);
102  }
103 
104  // These must be down here, because the EggNode assignment operator
105  // will force an update_under(). Therefore, we can't call it until
106  // all the attributes that affect adjust_under() are in place.
107  EggGroupNode::operator = (copy);
108  EggRenderMode::operator = (copy);
109 
110  return *this;
111 }
112 
113 
114 ////////////////////////////////////////////////////////////////////
115 // Function: EggGroup::Destructor
116 // Access: Published
117 // Description:
118 ////////////////////////////////////////////////////////////////////
119 EggGroup::
120 ~EggGroup() {
122 }
123 
124 ////////////////////////////////////////////////////////////////////
125 // Function: EggGroup::set_group_type
126 // Access: Published
127 // Description:
128 ////////////////////////////////////////////////////////////////////
129 void EggGroup::
130 set_group_type(GroupType type) {
131  if (type != get_group_type()) {
132 #ifndef NDEBUG
133  if (type != GT_instance) {
134  // Only valid to change to a non-instance type if we have no
135  // group refs.
136  nassertv(_group_refs.empty());
137  }
138 #endif
139 
140  // Make sure the user didn't give us any stray bits.
141  nassertv((type & ~F_group_type)==0);
142  _flags = (_flags & ~F_group_type) | type;
143 
144  // Now we might have changed the type to or from an instance node,
145  // so we have to recompute the under_flags.
146  update_under(0);
147  }
148 }
149 
150 ////////////////////////////////////////////////////////////////////
151 // Function: EggGroup::has_object_type
152 // Access: Published
153 // Description: Returns true if the indicated object type has been
154 // added to the group, or false otherwise.
155 ////////////////////////////////////////////////////////////////////
156 bool EggGroup::
157 has_object_type(const string &object_type) const {
158  vector_string::const_iterator oi;
159  for (oi = _object_types.begin(); oi != _object_types.end(); ++oi) {
160  if (cmp_nocase_uh((*oi), object_type) == 0) {
161  return true;
162  }
163  }
164  return false;
165 }
166 
167 ////////////////////////////////////////////////////////////////////
168 // Function: EggGroup::remove_object_type
169 // Access: Published
170 // Description: Removes the first instance of the indicated object
171 // type from the group if it is present. Returns true
172 // if the object type was found and removed, false
173 // otherwise.
174 ////////////////////////////////////////////////////////////////////
175 bool EggGroup::
176 remove_object_type(const string &object_type) {
177  vector_string::iterator oi;
178  for (oi = _object_types.begin(); oi != _object_types.end(); ++oi) {
179  if (cmp_nocase_uh((*oi), object_type) == 0) {
180  _object_types.erase(oi);
181  return true;
182  }
183  }
184  return false;
185 }
186 
187 ////////////////////////////////////////////////////////////////////
188 // Function: EggGroup::write
189 // Access: Published, Virtual
190 // Description: Writes the group and all of its children to the
191 // indicated output stream in Egg format.
192 ////////////////////////////////////////////////////////////////////
193 void EggGroup::
194 write(ostream &out, int indent_level) const {
195  test_under_integrity();
196 
197  switch (get_group_type()) {
198  case GT_group:
199  write_header(out, indent_level, "<Group>");
200  break;
201 
202  case GT_instance:
203  write_header(out, indent_level, "<Instance>");
204  break;
205 
206  case GT_joint:
207  write_header(out, indent_level, "<Joint>");
208  break;
209 
210  default:
211  // invalid group type
212  nassertv(false);
213  }
214 
215  if (is_of_type(EggBin::get_class_type())) {
216  indent(out, indent_level + 2)
217  << "// Bin " << DCAST(EggBin, this)->get_bin_number() << "\n";
218  }
219 
220  if (has_lod()) {
221  get_lod().write(out, indent_level + 2);
222  }
223 
224  write_billboard_flags(out, indent_level + 2);
225  write_collide_flags(out, indent_level + 2);
226  write_model_flags(out, indent_level + 2);
227  write_switch_flags(out, indent_level + 2);
228 
229  if (has_transform()) {
230  EggTransform::write(out, indent_level + 2, "<Transform>");
231  }
232 
233  if (get_group_type() == GT_joint && _default_pose.has_transform()) {
234  _default_pose.write(out, indent_level + 2, "<DefaultPose>");
235  }
236 
237  if (get_scroll_u() != 0) {
238  indent(out, indent_level + 2)
239  << "<Scalar> scroll_u { " << get_scroll_u() << " }\n";
240 
241  }
242 
243  if (get_scroll_v() != 0) {
244  indent(out, indent_level + 2)
245  << "<Scalar> scroll_v { " << get_scroll_v() << " }\n";
246 
247  }
248 
249  if (get_scroll_w() != 0) {
250  indent(out, indent_level + 2)
251  << "<Scalar> scroll_w { " << get_scroll_w() << " }\n";
252 
253  }
254 
255  if (get_scroll_r() != 0) {
256  indent(out, indent_level + 2)
257  << "<Scalar> scroll_r { " << get_scroll_r() << " }\n";
258 
259  }
260 
261  write_object_types(out, indent_level + 2);
262  write_decal_flags(out, indent_level + 2);
263  write_tags(out, indent_level + 2);
264  write_render_mode(out, indent_level + 2);
265 
266  if (get_portal_flag()) {
267  indent(out, indent_level + 2) << "<Scalar> portal { 1 }\n";
268  }
269 
270  if (get_occluder_flag()) {
271  indent(out, indent_level + 2) << "<Scalar> occluder { 1 }\n";
272  }
273 
274  if (get_polylight_flag()) {
275  indent(out, indent_level + 2) << "<Scalar> polylight { 1 }\n";
276  }
277 
278  if (has_indexed_flag()) {
279  indent(out, indent_level + 2)
280  << "<Scalar> indexed { " << get_indexed_flag() << " }\n";
281  }
282 
283  if (get_blend_mode() != BM_unspecified) {
284  indent(out, indent_level + 2)
285  << "<Scalar> blend { " << get_blend_mode() << " }\n";
286  }
287 
288  if (get_blend_operand_a() != BO_unspecified) {
289  indent(out, indent_level + 2)
290  << "<Scalar> blendop-a { " << get_blend_operand_a() << " }\n";
291  }
292 
293  if (get_blend_operand_b() != BO_unspecified) {
294  indent(out, indent_level + 2)
295  << "<Scalar> blendop-b { " << get_blend_operand_b() << " }\n";
296  }
297 
298  if (has_blend_color()) {
299  const LColor &c = get_blend_color();
300  indent(out, indent_level + 2)
301  << "<Scalar> blendr { " << c[0] << " }\n";
302  indent(out, indent_level + 2)
303  << "<Scalar> blendg { " << c[1] << " }\n";
304  indent(out, indent_level + 2)
305  << "<Scalar> blendb { " << c[2] << " }\n";
306  indent(out, indent_level + 2)
307  << "<Scalar> blenda { " << c[3] << " }\n";
308  }
309 
310  GroupRefs::const_iterator gri;
311  for (gri = _group_refs.begin(); gri != _group_refs.end(); ++gri) {
312  EggGroup *group_ref = (*gri);
313  indent(out, indent_level + 2)
314  << "<Ref> { " << group_ref->get_name() << " }\n";
315  }
316 
317  // We have to write the children nodes before we write the vertex
318  // references, since we might be referencing a vertex that's defined
319  // in one of those children nodes!
320  EggGroupNode::write(out, indent_level + 2);
321  write_vertex_ref(out, indent_level + 2);
322 
323  indent(out, indent_level) << "}\n";
324 }
325 
326 ////////////////////////////////////////////////////////////////////
327 // Function: EggGroup::write_billboard_flags
328 // Access: Published
329 // Description: Writes just the <Billboard> entry and related fields to
330 // the indicated ostream.
331 ////////////////////////////////////////////////////////////////////
332 void EggGroup::
333 write_billboard_flags(ostream &out, int indent_level) const {
334  if (get_billboard_type() != BT_none) {
335  indent(out, indent_level)
336  << "<Billboard> { " << get_billboard_type() << " }\n";
337  }
338 
339  if (has_billboard_center()) {
340  indent(out, indent_level)
341  << "<BillboardCenter> { " << get_billboard_center() << " }\n";
342  }
343 }
344 
345 ////////////////////////////////////////////////////////////////////
346 // Function: EggGroup::write_collide_flags
347 // Access: Published
348 // Description: Writes just the <Collide> entry and related fields to
349 // the indicated ostream.
350 ////////////////////////////////////////////////////////////////////
351 void EggGroup::
352 write_collide_flags(ostream &out, int indent_level) const {
353  if (get_cs_type() != CST_none) {
354  indent(out, indent_level) << "<Collide> ";
355  if (has_collision_name()) {
356  enquote_string(out, get_collision_name()) << " ";
357  }
358  out << "{ " << get_cs_type();
359  if (get_collide_flags() != CF_none) {
360  out << " " << get_collide_flags();
361  }
362  out << " }\n";
363  }
364 
365  if (has_collide_mask()) {
366  indent(out, indent_level)
367  << "<Scalar> collide-mask { 0x";
368  get_collide_mask().output_hex(out, 0);
369  out << " }\n";
370  }
371 
372  if (has_from_collide_mask()) {
373  indent(out, indent_level)
374  << "<Scalar> from-collide-mask { 0x";
375  get_from_collide_mask().output_hex(out, 0);
376  out << " }\n";
377  }
378 
379  if (has_into_collide_mask()) {
380  indent(out, indent_level)
381  << "<Scalar> into-collide-mask { 0x";
382  get_into_collide_mask().output_hex(out, 0);
383  out << " }\n";
384  }
385 }
386 
387 ////////////////////////////////////////////////////////////////////
388 // Function: EggGroup::write_model_flags
389 // Access: Published
390 // Description: Writes the <Model> flag and related flags to the
391 // indicated ostream.
392 ////////////////////////////////////////////////////////////////////
393 void EggGroup::
394 write_model_flags(ostream &out, int indent_level) const {
395  if (get_dcs_type() != DC_unspecified) {
396  indent(out, indent_level)
397  << "<DCS> { " << get_dcs_type() << " }\n";
398  }
399 
400  if (get_dart_type() != DT_none) {
401  indent(out, indent_level)
402  << "<Dart> { " << get_dart_type() << " }\n";
403  }
404 
405  if (get_model_flag()) {
406  indent(out, indent_level) << "<Model> { 1 }\n";
407  }
408 
409  if (get_texlist_flag()) {
410  indent(out, indent_level) << "<TexList> { 1 }\n";
411  }
412 
413  if (get_direct_flag()) {
414  indent(out, indent_level) << "<Scalar> direct { 1 }\n";
415  }
416 }
417 
418 ////////////////////////////////////////////////////////////////////
419 // Function: EggGroup::write_switch_flags
420 // Access: Published
421 // Description: Writes the <Switch> flag and related flags to the
422 // indicated ostream.
423 ////////////////////////////////////////////////////////////////////
424 void EggGroup::
425 write_switch_flags(ostream &out, int indent_level) const {
426  if (get_switch_flag()) {
427  indent(out, indent_level) << "<Switch> { 1 }\n";
428  if (get_switch_fps() != 0.0) {
429  indent(out, indent_level)
430  << "<Scalar> fps { " << get_switch_fps() << " }\n";
431  }
432  }
433 }
434 
435 ////////////////////////////////////////////////////////////////////
436 // Function: EggGroup::write_object_types
437 // Access: Published
438 // Description: Writes just the <ObjectTypes> entries, if any, to the
439 // indicated ostream.
440 ////////////////////////////////////////////////////////////////////
441 void EggGroup::
442 write_object_types(ostream &out, int indent_level) const {
443  vector_string::const_iterator oi;
444  for (oi = _object_types.begin(); oi != _object_types.end(); ++oi) {
445  indent(out, indent_level)
446  << "<ObjectType> { ";
447  enquote_string(out, (*oi)) << " }\n";
448  }
449 }
450 
451 ////////////////////////////////////////////////////////////////////
452 // Function: EggGroup::write_decal_flags
453 // Access: Published
454 // Description: Writes the flags related to decaling, if any.
455 ////////////////////////////////////////////////////////////////////
456 void EggGroup::
457 write_decal_flags(ostream &out, int indent_level) const {
458  if (get_decal_flag()) {
459  indent(out, indent_level) << "<Scalar> decal { 1 }\n";
460  }
461 }
462 
463 ////////////////////////////////////////////////////////////////////
464 // Function: EggGroup::write_tags
465 // Access: Published
466 // Description: Writes just the <Tag> entries, if any, to the
467 // indicated ostream.
468 ////////////////////////////////////////////////////////////////////
469 void EggGroup::
470 write_tags(ostream &out, int indent_level) const {
471  TagData::const_iterator ti;
472  for (ti = _tag_data.begin(); ti != _tag_data.end(); ++ti) {
473  const string &key = (*ti).first;
474  const string &value = (*ti).second;
475 
476  indent(out, indent_level) << "<Tag> ";
477  enquote_string(out, key) << " {\n";
478  enquote_string(out, value, indent_level + 2) << "\n";
479  indent(out, indent_level) << "}\n";
480  }
481 }
482 
483 ////////////////////////////////////////////////////////////////////
484 // Function: EggGroup::write_render_mode
485 // Access: Published
486 // Description: Writes the flags inherited from EggRenderMode and
487 // similar flags that control obscure render effects.
488 ////////////////////////////////////////////////////////////////////
489 void EggGroup::
490 write_render_mode(ostream &out, int indent_level) const {
491  EggRenderMode::write(out, indent_level);
492 
493  if (get_nofog_flag()) {
494  indent(out, indent_level) << "<Scalar> no-fog { 1 }\n";
495  }
496 }
497 
498 ////////////////////////////////////////////////////////////////////
499 // Function: EggGroup::is_joint
500 // Access: Published, Virtual
501 // Description: Returns true if this particular node represents a
502 // <Joint> entry or not. This is a handy thing to know
503 // since Joints are sorted to the end of their sibling
504 // list when writing an egg file. See
505 // EggGroupNode::write().
506 ////////////////////////////////////////////////////////////////////
507 bool EggGroup::
508 is_joint() const {
509  return (get_group_type() == GT_joint);
510 }
511 
512 ////////////////////////////////////////////////////////////////////
513 // Function: EggGroup::determine_alpha_mode
514 // Access: Published, Virtual
515 // Description: Walks back up the hierarchy, looking for an EggGroup
516 // or EggPrimitive or some such object at this level or
517 // above this group that has an alpha_mode other than
518 // AM_unspecified. Returns a valid EggRenderMode pointer
519 // if one is found, or NULL otherwise.
520 ////////////////////////////////////////////////////////////////////
523  if (get_alpha_mode() != AM_unspecified) {
524  return this;
525  }
527 }
528 
529 ////////////////////////////////////////////////////////////////////
530 // Function: EggGroup::determine_depth_write_mode
531 // Access: Published, Virtual
532 // Description: Walks back up the hierarchy, looking for an EggGroup
533 // or EggPrimitive or some such object at this level or
534 // above this group that has a depth_write_mode other
535 // than DWM_unspecified. Returns a valid EggRenderMode
536 // pointer if one is found, or NULL otherwise.
537 ////////////////////////////////////////////////////////////////////
540  if (get_depth_write_mode() != DWM_unspecified) {
541  return this;
542  }
544 }
545 
546 ////////////////////////////////////////////////////////////////////
547 // Function: EggGroup::determine_depth_test_mode
548 // Access: Published, Virtual
549 // Description: Walks back up the hierarchy, looking for an EggGroup
550 // or EggPrimitive or some such object at this level or
551 // above this group that has a depth_test_mode other
552 // than DTM_unspecified. Returns a valid EggRenderMode
553 // pointer if one is found, or NULL otherwise.
554 ////////////////////////////////////////////////////////////////////
557  if (get_depth_test_mode() != DTM_unspecified) {
558  return this;
559  }
561 }
562 
563 ////////////////////////////////////////////////////////////////////
564 // Function: EggGroup::determine_visibility_mode
565 // Access: Published, Virtual
566 // Description: Walks back up the hierarchy, looking for an EggGroup
567 // or EggPrimitive or some such object at this level or
568 // above this group that has a visibility_mode other
569 // than VM_unspecified. Returns a valid EggRenderMode
570 // pointer if one is found, or NULL otherwise.
571 ////////////////////////////////////////////////////////////////////
574  if (get_visibility_mode() != VM_unspecified) {
575  return this;
576  }
578 }
579 
580 ////////////////////////////////////////////////////////////////////
581 // Function: EggGroup::determine_depth_offset
582 // Access: Published, Virtual
583 // Description: Walks back up the hierarchy, looking for an EggGroup
584 // or EggPrimitive or some such object at this level or
585 // above this group that has a depth_offset specified.
586 // Returns a valid EggRenderMode pointer if one is found,
587 // or NULL otherwise.
588 ////////////////////////////////////////////////////////////////////
591  if (has_depth_offset()) {
592  return this;
593  }
595 }
596 
597 ////////////////////////////////////////////////////////////////////
598 // Function: EggGroup::determine_draw_order
599 // Access: Published, Virtual
600 // Description: Walks back up the hierarchy, looking for an EggGroup
601 // or EggPrimitive or some such object at this level or
602 // above this group that has a draw_order specified.
603 // Returns a valid EggRenderMode pointer if one is found,
604 // or NULL otherwise.
605 ////////////////////////////////////////////////////////////////////
608  if (has_draw_order()) {
609  return this;
610  }
612 }
613 
614 ////////////////////////////////////////////////////////////////////
615 // Function: EggGroup::determine_bin
616 // Access: Published, Virtual
617 // Description: Walks back up the hierarchy, looking for an EggGroup
618 // or EggPrimitive or some such object at this level or
619 // above this group that has a bin specified. Returns a
620 // valid EggRenderMode pointer if one is found, or NULL
621 // otherwise.
622 ////////////////////////////////////////////////////////////////////
625  if (has_bin()) {
626  return this;
627  }
629 }
630 
631 ////////////////////////////////////////////////////////////////////
632 // Function: EggGroup::determine_indexed
633 // Access: Published, Virtual
634 // Description: Walks back up the hierarchy, looking for an EggGroup
635 // at this level or above that has the "indexed" scalar
636 // set. Returns the value of the indexed scalar if it
637 // is found, or false if it is not.
638 //
639 // In other words, returns true if the "indexed" flag is
640 // in effect for the indicated node, false otherwise.
641 ////////////////////////////////////////////////////////////////////
642 bool EggGroup::
644  if (has_indexed_flag()) {
645  return get_indexed_flag();
646  }
648 }
649 
650 ////////////////////////////////////////////////////////////////////
651 // Function: EggGroup::determine_decal
652 // Access: Published, Virtual
653 // Description: Walks back up the hierarchy, looking for an EggGroup
654 // at this level or above that has the "decal" flag
655 // set. Returns the value of the decal flag if it
656 // is found, or false if it is not.
657 //
658 // In other words, returns true if the "decal" flag is
659 // in effect for the indicated node, false otherwise.
660 ////////////////////////////////////////////////////////////////////
661 bool EggGroup::
663  if (get_decal_flag()) {
664  return true;
665  }
667 }
668 
669 ////////////////////////////////////////////////////////////////////
670 // Function: EggGroup::ref_vertex
671 // Access: Published
672 // Description: Adds the vertex to the set of those referenced by the
673 // group, at the indicated membership level. If the
674 // vertex is already being referenced, increases the
675 // membership amount by the indicated amount.
676 ////////////////////////////////////////////////////////////////////
677 void EggGroup::
678 ref_vertex(EggVertex *vert, double membership) {
679  VertexRef::iterator vri = _vref.find(vert);
680 
681  if (vri != _vref.end()) {
682  // The vertex was already being reffed; increment its membership
683  // amount.
684  (*vri).second += membership;
685 
686  // If that takes us down to zero, go ahead and unref the vertex.
687  if ((*vri).second == 0.0) {
688  unref_vertex(vert);
689  }
690 
691  } else {
692  // The vertex was not already reffed; ref it.
693  if (membership != 0.0) {
694  _vref[vert] = membership;
695 
696  bool inserted = vert->_gref.insert(this).second;
697  // Did the group not exist previously in the vertex's gref list?
698  // If it was there already, we must be out of sync between
699  // vertices and groups.
700  nassertv(inserted);
701  }
702  }
703 }
704 
705 
706 ////////////////////////////////////////////////////////////////////
707 // Function: EggGroup::unref_vertex
708 // Access: Published
709 // Description: Removes the vertex from the set of those referenced
710 // by the group. Does nothing if the vertex is not
711 // already reffed.
712 ////////////////////////////////////////////////////////////////////
713 void EggGroup::
715  VertexRef::iterator vri = _vref.find(vert);
716 
717  if (vri != _vref.end()) {
718  _vref.erase(vri);
719  int count = vert->_gref.erase(this);
720  // Did the group exist in the vertex's gref list? If it didn't,
721  // we must be out of sync between vertices and groups.
722  nassertv(count == 1);
723  }
724 }
725 
726 ////////////////////////////////////////////////////////////////////
727 // Function: EggGroup::unref_all_vertices
728 // Access: Published
729 // Description: Removes all vertices from the reference list.
730 ////////////////////////////////////////////////////////////////////
731 void EggGroup::
733  // We must walk through the vertex ref list, and flag each vertex as
734  // unreffed in its own structure.
735  VertexRef::iterator vri;
736  for (vri = _vref.begin(); vri != _vref.end(); ++vri) {
737  EggVertex *vert = (*vri).first;
738  int count = vert->_gref.erase(this);
739  // Did the group exist in the vertex's gref list? If it didn't,
740  // we must be out of sync between vertices and groups.
741  nassertv(count == 1);
742  }
743 
744  _vref.clear();
745 }
746 
747 
748 ////////////////////////////////////////////////////////////////////
749 // Function: EggGroup::get_vertex_membership
750 // Access: Published
751 // Description: Returns the amount of membership of the indicated
752 // vertex in this group. If the vertex is not reffed by
753 // the group, returns 0.
754 ////////////////////////////////////////////////////////////////////
755 double EggGroup::
756 get_vertex_membership(const EggVertex *vert) const {
757  VertexRef::const_iterator vri = _vref.find((EggVertex *)vert);
758 
759  if (vri != _vref.end()) {
760  return (*vri).second;
761  } else {
762  return 0.0;
763  }
764 }
765 
766 ////////////////////////////////////////////////////////////////////
767 // Function: EggGroup::set_vertex_membership
768 // Access: Published
769 // Description: Explicitly sets the net membership of the indicated
770 // vertex in this group to the given value.
771 ////////////////////////////////////////////////////////////////////
772 void EggGroup::
773 set_vertex_membership(EggVertex *vert, double membership) {
774  if (membership == 0.0) {
775  unref_vertex(vert);
776  return;
777  }
778 
779  VertexRef::iterator vri = _vref.find(vert);
780 
781  if (vri != _vref.end()) {
782  // The vertex was already being reffed; just change its membership
783  // amount.
784  (*vri).second = membership;
785 
786  } else {
787  // The vertex was not already reffed; ref it.
788  _vref[vert] = membership;
789 
790  bool inserted = vert->_gref.insert(this).second;
791  // Did the group not exist previously in the vertex's gref list?
792  // If it was there already, we must be out of sync between
793  // vertices and groups.
794  nassertv(inserted);
795  }
796 }
797 
798 ////////////////////////////////////////////////////////////////////
799 // Function: EggGroup::steal_vrefs
800 // Access: Published
801 // Description: Moves all of the vertex references from the indicated
802 // other group into this one. If a given vertex was
803 // previously shared by both groups, the relative
804 // memberships will be summed.
805 ////////////////////////////////////////////////////////////////////
806 void EggGroup::
808  nassertv(other != this);
809  VertexRef::const_iterator vri;
810  for (vri = other->vref_begin(); vri != other->vref_end(); ++vri) {
811  EggVertex *vert = (*vri).first;
812  double membership = (*vri).second;
813  ref_vertex(vert, membership);
814  }
815  other->unref_all_vertices();
816 }
817 
818 
819 #ifdef _DEBUG
820 
821 ////////////////////////////////////////////////////////////////////
822 // Function: EggGroup::test_vref_integrity
823 // Access: Published
824 // Description: Verifies that each vertex in the group exists and
825 // that it knows it is referenced by the group.
826 ////////////////////////////////////////////////////////////////////
827 void EggGroup::
828 test_vref_integrity() const {
830 
831  VertexRef::const_iterator vri;
832  for (vri = vref_begin(); vri != vref_end(); ++vri) {
833  const EggVertex *vert = (*vri).first;
834  vert->test_ref_count_integrity();
835 
836  nassertv(vert->has_gref(this));
837  }
838 }
839 
840 #endif // _DEBUG
841 
842 ////////////////////////////////////////////////////////////////////
843 // Function: EggGroup::add_group_ref
844 // Access: Published
845 // Description: Adds a new <Ref> entry to the group. This declares
846 // an internal reference to another node, and is used to
847 // implement scene-graph instancing; it is only valid if
848 // the group_type is GT_instance.
849 ////////////////////////////////////////////////////////////////////
850 void EggGroup::
852  nassertv(get_group_type() == GT_instance);
853  _group_refs.push_back(group);
854 }
855 
856 ////////////////////////////////////////////////////////////////////
857 // Function: EggGroup::get_num_group_refs
858 // Access: Published
859 // Description: Returns the number of <Ref> entries within this
860 // group. See add_group_ref().
861 ////////////////////////////////////////////////////////////////////
862 int EggGroup::
864  return _group_refs.size();
865 }
866 
867 ////////////////////////////////////////////////////////////////////
868 // Function: EggGroup::get_group_ref
869 // Access: Published
870 // Description: Returns the nth <Ref> entry within this group. See
871 // add_group_ref().
872 ////////////////////////////////////////////////////////////////////
874 get_group_ref(int n) const {
875  nassertr(n >= 0 && n < (int)_group_refs.size(), NULL);
876  return _group_refs[n];
877 }
878 
879 ////////////////////////////////////////////////////////////////////
880 // Function: EggGroup::remove_group_ref
881 // Access: Published
882 // Description: Removes the nth <Ref> entry within this group. See
883 // add_group_ref().
884 ////////////////////////////////////////////////////////////////////
885 void EggGroup::
887  nassertv(n >= 0 && n < (int)_group_refs.size());
888  _group_refs.erase(_group_refs.begin() + n);
889 }
890 
891 ////////////////////////////////////////////////////////////////////
892 // Function: EggGroup::clear_group_refs
893 // Access: Published
894 // Description: Removes all of the <Ref> entries within this group.
895 // See add_group_ref().
896 ////////////////////////////////////////////////////////////////////
897 void EggGroup::
899  _group_refs.clear();
900 }
901 
902 
903 
904 ////////////////////////////////////////////////////////////////////
905 // Function: EggGroup::string_group_type
906 // Access: Published, Static
907 // Description: Returns the GroupType value associated with the given
908 // string representation, or GT_invalid if the string
909 // does not match any known GroupType value.
910 ////////////////////////////////////////////////////////////////////
911 EggGroup::GroupType EggGroup::
912 string_group_type(const string &strval) {
913  if (cmp_nocase_uh(strval, "group") == 0) {
914  return GT_group;
915  } else if (cmp_nocase_uh(strval, "instance") == 0) {
916  return GT_instance;
917  } else if (cmp_nocase_uh(strval, "joint") == 0) {
918  return GT_joint;
919  } else {
920  return GT_invalid;
921  }
922 }
923 
924 ////////////////////////////////////////////////////////////////////
925 // Function: EggGroup::string_dart_type
926 // Access: Published, Static
927 // Description: Returns the DartType value associated with the given
928 // string representation, or DT_none if the string
929 // does not match any known DartType value.
930 ////////////////////////////////////////////////////////////////////
931 EggGroup::DartType EggGroup::
932 string_dart_type(const string &strval) {
933  if (cmp_nocase_uh(strval, "sync") == 0) {
934  return DT_sync;
935  } else if (cmp_nocase_uh(strval, "nosync") == 0) {
936  return DT_nosync;
937  } else if (cmp_nocase_uh(strval, "default") == 0) {
938  return DT_default;
939  } else if (cmp_nocase_uh(strval, "structured") == 0) {
940  return DT_structured;
941  } else {
942  return DT_none;
943  }
944 }
945 
946 ////////////////////////////////////////////////////////////////////
947 // Function: EggGroup::string_dcs_type
948 // Access: Published, Static
949 // Description: Returns the DCSType value associated with the given
950 // string representation, or DC_unspecified if the
951 // string does not match any known DCSType value.
952 ////////////////////////////////////////////////////////////////////
953 EggGroup::DCSType EggGroup::
954 string_dcs_type(const string &strval) {
955  if (cmp_nocase_uh(strval, "none") == 0) {
956  return DC_none;
957  } else if (cmp_nocase_uh(strval, "local") == 0) {
958  return DC_local;
959  } else if (cmp_nocase_uh(strval, "net") == 0) {
960  return DC_net;
961  } else if (cmp_nocase_uh(strval, "no_touch") == 0) {
962  return DC_no_touch;
963  } else if (cmp_nocase_uh(strval, "default") == 0) {
964  return DC_default;
965  } else {
966  return DC_unspecified;
967  }
968 }
969 
970 ////////////////////////////////////////////////////////////////////
971 // Function: EggGroup::string_billboard_type
972 // Access: Published, Static
973 // Description: Returns the BillboardType value associated with the
974 // given string representation, or BT_none if the string
975 // does not match any known BillboardType value.
976 ////////////////////////////////////////////////////////////////////
977 EggGroup::BillboardType EggGroup::
978 string_billboard_type(const string &strval) {
979  if (cmp_nocase_uh(strval, "axis") == 0) {
980  return BT_axis;
981  } else if (cmp_nocase_uh(strval, "point_eye") == 0) {
982  return BT_point_camera_relative;
983  } else if (cmp_nocase_uh(strval, "point_world") == 0) {
984  return BT_point_world_relative;
985  } else if (cmp_nocase_uh(strval, "point") == 0) {
986  return BT_point_world_relative;
987  } else {
988  return BT_none;
989  }
990 }
991 
992 ////////////////////////////////////////////////////////////////////
993 // Function: EggGroup::string_cs_type
994 // Access: Published, Static
995 // Description: Returns the CollisionSolidType value associated with the
996 // given string representation, or CST_none if the string
997 // does not match any known CollisionSolidType value.
998 ////////////////////////////////////////////////////////////////////
999 EggGroup::CollisionSolidType EggGroup::
1000 string_cs_type(const string &strval) {
1001  if (cmp_nocase_uh(strval, "plane") == 0) {
1002  return CST_plane;
1003  } else if (cmp_nocase_uh(strval, "polygon") == 0) {
1004  return CST_polygon;
1005  } else if (cmp_nocase_uh(strval, "polyset") == 0) {
1006  return CST_polyset;
1007  } else if (cmp_nocase_uh(strval, "sphere") == 0) {
1008  return CST_sphere;
1009  } else if (cmp_nocase_uh(strval, "box") == 0) {
1010  return CST_box;
1011  } else if (cmp_nocase_uh(strval, "inv-sphere") == 0 ||
1012  cmp_nocase_uh(strval, "invsphere") == 0) {
1013  return CST_inv_sphere;
1014  } else if (cmp_nocase_uh(strval, "tube") == 0) {
1015  return CST_tube;
1016  } else if (cmp_nocase_uh(strval, "floor-mesh") == 0 ||
1017  cmp_nocase_uh(strval, "floormesh") == 0) {
1018  return CST_floor_mesh;
1019  } else {
1020  return CST_none;
1021  }
1022 }
1023 
1024 ////////////////////////////////////////////////////////////////////
1025 // Function: EggGroup::string_collide_flags
1026 // Access: Published, Static
1027 // Description: Returns the CollideFlags value associated with the
1028 // given string representation, or CF_none if the string
1029 // does not match any known CollideFlags value. This
1030 // only recognizes a single keyword; it does not attempt
1031 // to parse a string of keywords.
1032 ////////////////////////////////////////////////////////////////////
1033 EggGroup::CollideFlags EggGroup::
1034 string_collide_flags(const string &strval) {
1035  if (cmp_nocase_uh(strval, "intangible") == 0) {
1036  return CF_intangible;
1037  } else if (cmp_nocase_uh(strval, "event") == 0) {
1038  return CF_event;
1039  } else if (cmp_nocase_uh(strval, "descend") == 0) {
1040  return CF_descend;
1041  } else if (cmp_nocase_uh(strval, "keep") == 0) {
1042  return CF_keep;
1043  } else if (cmp_nocase_uh(strval, "solid") == 0) {
1044  return CF_solid;
1045  } else if (cmp_nocase_uh(strval, "center") == 0) {
1046  return CF_center;
1047  } else if (cmp_nocase_uh(strval, "turnstile") == 0) {
1048  return CF_turnstile;
1049  } else if (cmp_nocase_uh(strval, "level") == 0) {
1050  return CF_level;
1051  } else {
1052  return CF_none;
1053  }
1054 }
1055 
1056 ////////////////////////////////////////////////////////////////////
1057 // Function: EggGroup::string_blend_mode
1058 // Access: Published, Static
1059 // Description: Returns the BlendMode value associated with the
1060 // given string representation, or BM_none if the string
1061 // does not match any known BlendMode.
1062 ////////////////////////////////////////////////////////////////////
1063 EggGroup::BlendMode EggGroup::
1064 string_blend_mode(const string &strval) {
1065  if (cmp_nocase_uh(strval, "none") == 0) {
1066  return BM_none;
1067  } else if (cmp_nocase_uh(strval, "add") == 0) {
1068  return BM_add;
1069  } else if (cmp_nocase_uh(strval, "subtract") == 0) {
1070  return BM_subtract;
1071  } else if (cmp_nocase_uh(strval, "inv_subtract") == 0) {
1072  return BM_inv_subtract;
1073  } else if (cmp_nocase_uh(strval, "min") == 0) {
1074  return BM_min;
1075  } else if (cmp_nocase_uh(strval, "max") == 0) {
1076  return BM_max;
1077  } else {
1078  return BM_unspecified;
1079  }
1080 }
1081 
1082 ////////////////////////////////////////////////////////////////////
1083 // Function: EggGroup::string_blend_operand
1084 // Access: Published, Static
1085 // Description: Returns the BlendOperand value associated with the
1086 // given string representation, or BO_none if the string
1087 // does not match any known BlendOperand.
1088 ////////////////////////////////////////////////////////////////////
1089 EggGroup::BlendOperand EggGroup::
1090 string_blend_operand(const string &strval) {
1091  if (cmp_nocase_uh(strval, "zero") == 0) {
1092  return BO_zero;
1093  } else if (cmp_nocase_uh(strval, "one") == 0) {
1094  return BO_one;
1095  } else if (cmp_nocase_uh(strval, "incoming_color") == 0) {
1096  return BO_incoming_color;
1097  } else if (cmp_nocase_uh(strval, "one_minus_incoming_color") == 0) {
1098  return BO_one_minus_incoming_color;
1099  } else if (cmp_nocase_uh(strval, "fbuffer_color") == 0) {
1100  return BO_fbuffer_color;
1101  } else if (cmp_nocase_uh(strval, "one_minus_fbuffer_color") == 0) {
1102  return BO_one_minus_fbuffer_color;
1103  } else if (cmp_nocase_uh(strval, "incoming_alpha") == 0) {
1104  return BO_incoming_alpha;
1105  } else if (cmp_nocase_uh(strval, "one_minus_incoming_alpha") == 0) {
1106  return BO_one_minus_incoming_alpha;
1107  } else if (cmp_nocase_uh(strval, "fbuffer_alpha") == 0) {
1108  return BO_fbuffer_alpha;
1109  } else if (cmp_nocase_uh(strval, "one_minus_fbuffer_alpha") == 0) {
1110  return BO_one_minus_fbuffer_alpha;
1111  } else if (cmp_nocase_uh(strval, "constant_color") == 0) {
1112  return BO_constant_color;
1113  } else if (cmp_nocase_uh(strval, "one_minus_constant_color") == 0) {
1114  return BO_one_minus_constant_color;
1115  } else if (cmp_nocase_uh(strval, "constant_alpha") == 0) {
1116  return BO_constant_alpha;
1117  } else if (cmp_nocase_uh(strval, "one_minus_constant_alpha") == 0) {
1118  return BO_one_minus_constant_alpha;
1119  } else if (cmp_nocase_uh(strval, "incoming_color_saturate") == 0) {
1120  return BO_incoming_color_saturate;
1121  } else if (cmp_nocase_uh(strval, "color_scale") == 0) {
1122  return BO_color_scale;
1123  } else if (cmp_nocase_uh(strval, "one_minus_color_scale") == 0) {
1124  return BO_one_minus_color_scale;
1125  } else if (cmp_nocase_uh(strval, "alpha_scale") == 0) {
1126  return BO_alpha_scale;
1127  } else if (cmp_nocase_uh(strval, "one_minus_alpha_scale") == 0) {
1128  return BO_one_minus_alpha_scale;
1129  } else {
1130  return BO_unspecified;
1131  }
1132 }
1133 
1134 ////////////////////////////////////////////////////////////////////
1135 // Function: EggGroup::as_transform
1136 // Access: Public, Virtual
1137 // Description: Returns this object cross-cast to an EggTransform
1138 // pointer, if it inherits from EggTransform, or NULL if
1139 // it does not.
1140 ////////////////////////////////////////////////////////////////////
1143  return this;
1144 }
1145 
1146 
1147 ////////////////////////////////////////////////////////////////////
1148 // Function: EggGroup::write_vertex_ref
1149 // Access: Protected
1150 // Description: Writes out the vertex ref component of the group body
1151 // only. This may consist of a number of <VertexRef>
1152 // entries, each with its own membership value.
1153 ////////////////////////////////////////////////////////////////////
1154 void EggGroup::
1155 write_vertex_ref(ostream &out, int indent_level) const {
1156  // We want to put the vertices together into groups first by vertex
1157  // pool, then by membership value. Each of these groups becomes a
1158  // separate VertexRef entry. Within each group, we'll sort the
1159  // vertices by index number.
1160 
1161  typedef pset<int> Indices;
1162  typedef pmap<double, Indices> Memberships;
1163  typedef pmap<EggVertexPool *, Memberships> Pools;
1164 
1165  Pools _entries;
1166  bool all_membership_one = true;
1167 
1168  VertexRef::const_iterator vri;
1169  for (vri = _vref.begin(); vri != _vref.end(); ++vri) {
1170  EggVertex *vert = (*vri).first;
1171  double membership = (*vri).second;
1172 
1173  if (membership != 1.0) {
1174  all_membership_one = false;
1175  }
1176 
1177  _entries[vert->get_pool()][membership].insert(vert->get_index());
1178  }
1179 
1180  // Now that we've reordered them, we can simply traverse the entries
1181  // and write them out.
1182  Pools::const_iterator pi;
1183  for (pi = _entries.begin(); pi != _entries.end(); ++pi) {
1184  EggVertexPool *pool = (*pi).first;
1185  const Memberships &memberships = (*pi).second;
1186  Memberships::const_iterator mi;
1187  for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
1188  double membership = (*mi).first;
1189  const Indices &indices = (*mi).second;
1190 
1191  indent(out, indent_level)
1192  << "<VertexRef> {\n";
1193  write_long_list(out, indent_level+2, indices.begin(), indices.end(),
1194  "", "", 72);
1195 
1196  // If all vrefs in this group have membership of 1, don't bother
1197  // to write out the membership scalar.
1198  if (!all_membership_one) {
1199  indent(out, indent_level + 2)
1200  << "<Scalar> membership { " << membership << " }\n";
1201  }
1202  if (pool == (EggVertexPool *)NULL) {
1203  indent(out, indent_level + 2)
1204  << "// Invalid NULL vertex pool.\n";
1205  } else {
1206  indent(out, indent_level + 2)
1207  << "<Ref> { " << pool->get_name() << " }\n";
1208  }
1209  indent(out, indent_level)
1210  << "}\n";
1211  }
1212  }
1213 }
1214 
1215 ////////////////////////////////////////////////////////////////////
1216 // Function: EggGroup::egg_start_parse_body
1217 // Access: Protected, Virtual
1218 // Description: This function is called within parse_egg(). It
1219 // should call the appropriate function on the lexer to
1220 // initialize the parser into the state associated with
1221 // this object. If the object cannot be parsed into
1222 // directly, it should return false.
1223 ////////////////////////////////////////////////////////////////////
1224 bool EggGroup::
1225 egg_start_parse_body() {
1226  egg_start_group_body();
1227  return true;
1228 }
1229 
1230 ////////////////////////////////////////////////////////////////////
1231 // Function: EggGroup::adjust_under
1232 // Access: Protected, Virtual
1233 // Description: This is called within update_under() after all the
1234 // various under settings have been inherited directly
1235 // from the parent node. It is responsible for
1236 // adjusting these settings to reflect states local to
1237 // the current node; for instance, an <Instance> node
1238 // will force the UF_under_instance bit on.
1239 ////////////////////////////////////////////////////////////////////
1240 void EggGroup::
1241 adjust_under() {
1242  // If we have our own transform, it carries forward.
1243 
1244  // As of 4/18/01, this now also affects the local_coord flag, below.
1245  // This means that a <Transform> entry within an <Instance> node
1246  // transforms the instance itself.
1247  if (has_transform()) {
1248  _under_flags |= UF_under_transform;
1249 
1250  // Our own transform also affects our node frame.
1251  _node_frame =
1252  new MatrixFrame(get_transform3d() * get_node_frame());
1253 
1254  // To prevent trying to invert a sigular matrix
1255  LMatrix4d mat;
1256  bool invert_ok = mat.invert_from(get_node_frame());
1257  if (invert_ok) {
1258  _node_frame_inv =
1259  new MatrixFrame(mat);
1260  } else {
1261  _node_frame_inv = NULL;
1262  }
1263 
1264  _vertex_to_node =
1265  new MatrixFrame(get_vertex_frame() * get_node_frame_inv());
1266  _node_to_vertex =
1267  new MatrixFrame(get_node_frame() * get_vertex_frame_inv());
1268 
1269  }
1270 
1271  if (is_instance_type()) {
1272  _under_flags |= UF_under_instance;
1273  if (_under_flags & UF_under_transform) {
1274  // If we've reached an instance node and we're under a
1275  // transform, that means we've just defined a local coordinate
1276  // system.
1277  _under_flags |= UF_local_coord;
1278  }
1279 
1280  // An instance node means that from this point and below, vertices
1281  // are defined relative to this node. Thus, the node frame
1282  // becomes the vertex frame.
1283  _vertex_frame = _node_frame;
1284  _vertex_frame_inv = _node_frame_inv;
1285  _vertex_to_node = NULL;
1286  _node_to_vertex = NULL;
1287  }
1288 }
1289 
1290 ////////////////////////////////////////////////////////////////////
1291 // Function: EggGroup::r_transform
1292 // Access: Protected, Virtual
1293 // Description: This is called from within the egg code by
1294 // transform(). It applies a transformation matrix
1295 // to the current node in some sensible way, then
1296 // continues down the tree.
1297 //
1298 // The first matrix is the transformation to apply; the
1299 // second is its inverse. The third parameter is the
1300 // coordinate system we are changing to, or CS_default
1301 // if we are not changing coordinate systems.
1302 ////////////////////////////////////////////////////////////////////
1303 void EggGroup::
1304 r_transform(const LMatrix4d &mat, const LMatrix4d &inv,
1305  CoordinateSystem to_cs) {
1306  if (has_transform() || get_group_type() == GT_joint) {
1307  // Since we want to apply this transform to all matrices,
1308  // including nested matrices, we can't simply premult it in and
1309  // leave it, because that would leave the rotational component in
1310  // the scene graph's matrix, and all nested matrices would inherit
1311  // the same rotational component. So we have to premult and then
1312  // postmult by the inverse to undo the rotational component each
1313  // time.
1314 
1315  LMatrix4d mat1 = mat;
1316  LMatrix4d inv1 = inv;
1317 
1318  // If we have a translation component, we should only apply
1319  // it to the top matrix. All subsequent matrices get just the
1320  // rotational component.
1321  mat1.set_row(3, LVector3d(0.0, 0.0, 0.0));
1322  inv1.set_row(3, LVector3d(0.0, 0.0, 0.0));
1323 
1324  internal_set_transform(inv1 * get_transform3d() * mat);
1325 
1326  if (_default_pose.has_transform()) {
1327  LMatrix4d t = _default_pose.get_transform3d();
1328  _default_pose.clear_transform();
1329  _default_pose.add_matrix4(inv1 * t * mat);
1330  }
1331 
1332  EggGroupNode::r_transform(mat1, inv1, to_cs);
1333  } else {
1334  EggGroupNode::r_transform(mat, inv, to_cs);
1335  }
1336 
1337  // Convert the LOD description too.
1338  if (has_lod()) {
1339  _lod->transform(mat);
1340  }
1341  if (has_billboard_center()) {
1342  _billboard_center = _billboard_center * mat;
1343  }
1344 }
1345 
1346 ////////////////////////////////////////////////////////////////////
1347 // Function: EggGroup::r_flatten_transforms
1348 // Access: Protected, Virtual
1349 // Description: The recursive implementation of flatten_transforms().
1350 ////////////////////////////////////////////////////////////////////
1351 void EggGroup::
1352 r_flatten_transforms() {
1353  EggGroupNode::r_flatten_transforms();
1354 
1355  if (is_local_coord()) {
1356  LMatrix4d mat = get_vertex_frame();
1357  if (has_lod()) {
1358  _lod->transform(mat);
1359  }
1360 
1361  if (get_billboard_type() != BT_none && !has_billboard_center()) {
1362  // If we had a billboard without an explicit center, it was an
1363  // implicit instance. Now it's not any more.
1364  set_billboard_center(LPoint3d(0.0, 0.0, 0.0) * mat);
1365 
1366  } else if (has_billboard_center()) {
1367  _billboard_center = _billboard_center * mat;
1368  }
1369  }
1370 
1371  if (get_group_type() == GT_instance) {
1372  set_group_type(GT_group);
1373  }
1374 
1375  if (get_group_type() != GT_joint) {
1376  internal_clear_transform();
1377  }
1378 }
1379 
1380 
1381 ////////////////////////////////////////////////////////////////////
1382 // Function: EggGroup::transform_changed
1383 // Access: Protected, Virtual
1384 // Description: This virtual method is inherited by EggTransform3d;
1385 // it is called whenever the transform is changed.
1386 ////////////////////////////////////////////////////////////////////
1387 void EggGroup::
1388 transform_changed() {
1389  // Recompute all of the cached transforms at this node and below.
1390  // We should probably make this smarter and do lazy evaluation of
1391  // these transforms, rather than having to recompute the whole tree
1392  // with every change to a parent node's transform.
1393  update_under(0);
1394 }
1395 
1396 
1397 
1398 ////////////////////////////////////////////////////////////////////
1399 // Function: GroupType output operator
1400 // Description:
1401 ////////////////////////////////////////////////////////////////////
1402 ostream &operator << (ostream &out, EggGroup::GroupType t) {
1403  switch (t) {
1404  case EggGroup::GT_invalid:
1405  return out << "invalid group";
1406  case EggGroup::GT_group:
1407  return out << "group";
1408  case EggGroup::GT_instance:
1409  return out << "instance";
1410  case EggGroup::GT_joint:
1411  return out << "joint";
1412  }
1413 
1414  nassertr(false, out);
1415  return out << "(**invalid**)";
1416 }
1417 
1418 ////////////////////////////////////////////////////////////////////
1419 // Function: DartType output operator
1420 // Description:
1421 ////////////////////////////////////////////////////////////////////
1422 ostream &operator << (ostream &out, EggGroup::DartType t) {
1423  switch (t) {
1424  case EggGroup::DT_none:
1425  return out << "none";
1426  case EggGroup::DT_sync:
1427  return out << "sync";
1428  case EggGroup::DT_nosync:
1429  return out << "nosync";
1430  case EggGroup::DT_structured:
1431  return out << "structured";
1432  case EggGroup::DT_default:
1433  return out << "1";
1434  }
1435 
1436  nassertr(false, out);
1437  return out << "(**invalid**)";
1438 }
1439 
1440 ////////////////////////////////////////////////////////////////////
1441 // Function: DCSType output operator
1442 // Description:
1443 ////////////////////////////////////////////////////////////////////
1444 ostream &operator << (ostream &out, EggGroup::DCSType t) {
1445  switch (t) {
1446  case EggGroup::DC_unspecified:
1447  return out << "unspecified";
1448  case EggGroup::DC_none:
1449  return out << "none";
1450  case EggGroup::DC_local:
1451  return out << "local";
1452  case EggGroup::DC_net:
1453  return out << "net";
1454  case EggGroup::DC_no_touch:
1455  return out << "no_touch";
1456  case EggGroup::DC_default:
1457  return out << "1";
1458  }
1459 
1460  nassertr(false, out);
1461  return out << "(**invalid**)";
1462 }
1463 
1464 ////////////////////////////////////////////////////////////////////
1465 // Function: BillboardType output operator
1466 // Description:
1467 ////////////////////////////////////////////////////////////////////
1468 ostream &operator << (ostream &out, EggGroup::BillboardType t) {
1469  switch (t) {
1470  case EggGroup::BT_none:
1471  return out << "none";
1472  case EggGroup::BT_axis:
1473  return out << "axis";
1474  case EggGroup::BT_point_camera_relative:
1475  return out << "point_eye";
1476  case EggGroup::BT_point_world_relative:
1477  return out << "point_world";
1478  }
1479 
1480  nassertr(false, out);
1481  return out << "(**invalid**)";
1482 }
1483 
1484 ////////////////////////////////////////////////////////////////////
1485 // Function: CollisionSolidType output operator
1486 // Description:
1487 ////////////////////////////////////////////////////////////////////
1488 ostream &operator << (ostream &out, EggGroup::CollisionSolidType t) {
1489  switch (t) {
1490  case EggGroup::CST_none:
1491  return out << "None";
1492  case EggGroup::CST_plane:
1493  return out << "Plane";
1494  case EggGroup::CST_polygon:
1495  return out << "Polygon";
1496  case EggGroup::CST_polyset:
1497  return out << "Polyset";
1498  case EggGroup::CST_sphere:
1499  return out << "Sphere";
1500  case EggGroup::CST_inv_sphere:
1501  return out << "InvSphere";
1502  case EggGroup::CST_tube:
1503  return out << "Tube";
1504  case EggGroup::CST_floor_mesh:
1505  return out << "FloorMesh";
1506  case EggGroup::CST_box:
1507  return out << "Box";
1508  }
1509 
1510  nassertr(false, out);
1511  return out << "(**invalid**)";
1512 }
1513 
1514 ////////////////////////////////////////////////////////////////////
1515 // Function: CollideFlags output operator
1516 // Description:
1517 ////////////////////////////////////////////////////////////////////
1518 ostream &operator << (ostream &out, EggGroup::CollideFlags t) {
1519  if (t == EggGroup::CF_none) {
1520  return out << "none";
1521  }
1522  int bits = (int)t;
1523  const char *space = "";
1524 
1525  if (bits & EggGroup::CF_intangible) {
1526  out << space << "intangible";
1527  space = " ";
1528  }
1529  if (bits & EggGroup::CF_event) {
1530  out << space << "event";
1531  space = " ";
1532  }
1533  if (bits & EggGroup::CF_descend) {
1534  out << space << "descend";
1535  space = " ";
1536  }
1537  if (bits & EggGroup::CF_keep) {
1538  out << space << "keep";
1539  space = " ";
1540  }
1541  if (bits & EggGroup::CF_solid) {
1542  out << space << "solid";
1543  space = " ";
1544  }
1545  if (bits & EggGroup::CF_center) {
1546  out << space << "center";
1547  space = " ";
1548  }
1549  if (bits & EggGroup::CF_turnstile) {
1550  out << space << "turnstile";
1551  space = " ";
1552  }
1553  if (bits & EggGroup::CF_level) {
1554  out << space << "level";
1555  space = " ";
1556  }
1557  return out;
1558 }
1559 
1560 ////////////////////////////////////////////////////////////////////
1561 // Function: ostream << EggGroup::BlendMode
1562 // Description:
1563 ////////////////////////////////////////////////////////////////////
1564 ostream &
1565 operator << (ostream &out, EggGroup::BlendMode t) {
1566  switch (t) {
1567  case EggGroup::BM_unspecified:
1568  return out << "unspecified";
1569 
1570  case EggGroup::BM_none:
1571  return out << "none";
1572 
1573  case EggGroup::BM_add:
1574  return out << "add";
1575 
1576  case EggGroup::BM_subtract:
1577  return out << "subtract";
1578 
1579  case EggGroup::BM_inv_subtract:
1580  return out << "inv_subtract";
1581 
1582  case EggGroup::BM_min:
1583  return out << "min";
1584 
1585  case EggGroup::BM_max:
1586  return out << "max";
1587  }
1588 
1589  return out << "**invalid EggGroup::BlendMode(" << (int)t << ")**";
1590 }
1591 
1592 ////////////////////////////////////////////////////////////////////
1593 // Function: ostream << EggGroup::BlendOperand
1594 // Description:
1595 ////////////////////////////////////////////////////////////////////
1596 ostream &
1597 operator << (ostream &out, EggGroup::BlendOperand t) {
1598  switch (t) {
1599  case EggGroup::BO_unspecified:
1600  return out << "unspecified";
1601 
1602  case EggGroup::BO_zero:
1603  return out << "zero";
1604 
1605  case EggGroup::BO_one:
1606  return out << "one";
1607 
1608  case EggGroup::BO_incoming_color:
1609  return out << "incomfing_color";
1610 
1611  case EggGroup::BO_one_minus_incoming_color:
1612  return out << "one_minus_incoming_color";
1613 
1614  case EggGroup::BO_fbuffer_color:
1615  return out << "fbuffer_color";
1616 
1617  case EggGroup::BO_one_minus_fbuffer_color:
1618  return out << "one_minus_fbuffer_color";
1619 
1620  case EggGroup::BO_incoming_alpha:
1621  return out << "incoming_alpha";
1622 
1623  case EggGroup::BO_one_minus_incoming_alpha:
1624  return out << "one_minus_incoming_alpha";
1625 
1626  case EggGroup::BO_fbuffer_alpha:
1627  return out << "fbuffer_alpha";
1628 
1629  case EggGroup::BO_one_minus_fbuffer_alpha:
1630  return out << "one_minus_fbuffer_alpha";
1631 
1632  case EggGroup::BO_constant_color:
1633  return out << "constant_color";
1634 
1635  case EggGroup::BO_one_minus_constant_color:
1636  return out << "one_minus_constant_color";
1637 
1638  case EggGroup::BO_constant_alpha:
1639  return out << "constant_alpha";
1640 
1641  case EggGroup::BO_one_minus_constant_alpha:
1642  return out << "one_minus_constant_alpha";
1643 
1644  case EggGroup::BO_incoming_color_saturate:
1645  return out << "incoming_color_saturate";
1646 
1647  case EggGroup::BO_color_scale:
1648  return out << "color_scale";
1649 
1650  case EggGroup::BO_one_minus_color_scale:
1651  return out << "one_minus_color_scale";
1652 
1653  case EggGroup::BO_alpha_scale:
1654  return out << "alpha_scale";
1655 
1656  case EggGroup::BO_one_minus_alpha_scale:
1657  return out << "one_minus_alpha_scale";
1658  }
1659 
1660  return out << "**invalid EggGroup::BlendOperand(" << (int)t << ")**";
1661 }
VisibilityMode get_visibility_mode() const
Returns the visibility mode that was set, or VM_unspecified if nothing was set.
Definition: eggRenderMode.I:99
static CollideFlags string_collide_flags(const string &strval)
Returns the CollideFlags value associated with the given string representation, or CF_none if the str...
Definition: eggGroup.cxx:1034
virtual bool determine_indexed()
Walks back up the hierarchy, looking for an EggGroup at this level or above that has the &quot;indexed&quot; sc...
Definition: eggNode.cxx:228
bool test_ref_count_integrity() const
Does some easy checks to make sure that the reference count isn&#39;t completely bogus.
static CollisionSolidType string_cs_type(const string &strval)
Returns the CollisionSolidType value associated with the given string representation, or CST_none if the string does not match any known CollisionSolidType value.
Definition: eggGroup.cxx:1000
void clear_transform()
Resets the transform to empty, identity.
Definition: eggTransform.I:125
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
virtual void write(ostream &out, int indent_level) const
Writes the group and all of its children to the indicated output stream in Egg format.
virtual EggRenderMode * determine_depth_write_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggGroup.cxx:539
const LMatrix4d & get_transform3d() const
Returns the overall transform as a 4x4 matrix.
Definition: eggTransform.I:251
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
virtual EggRenderMode * determine_bin()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggGroup.cxx:624
void write_billboard_flags(ostream &out, int indent_level) const
Writes just the &lt;Billboard&gt; entry and related fields to the indicated ostream.
Definition: eggGroup.cxx:333
bool has_depth_offset() const
Returns true if the depth-offset flag has been set for this particular object.
void write_header(ostream &out, int indent_level, const char *egg_keyword) const
Writes the first line of the egg object, e.g.
bool is_local_coord() const
Returns true if this node&#39;s vertices are not in the global coordinate space.
Definition: eggNode.I:109
static DartType string_dart_type(const string &strval)
Returns the DartType value associated with the given string representation, or DT_none if the string ...
Definition: eggGroup.cxx:932
virtual EggRenderMode * determine_alpha_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggGroup.cxx:522
void add_group_ref(EggGroup *group)
Adds a new &lt;Ref&gt; entry to the group.
Definition: eggGroup.cxx:851
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:51
DepthWriteMode get_depth_write_mode() const
Returns the depth_write mode that was set, or DWM_unspecified if nothing was set. ...
Definition: eggRenderMode.I:47
virtual bool is_joint() const
Returns true if this particular node represents a &lt;Joint&gt; entry or not.
Definition: eggGroup.cxx:508
virtual EggRenderMode * determine_depth_test_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggGroup.cxx:556
virtual EggRenderMode * determine_draw_order()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggNode.cxx:190
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
const LMatrix4d & get_vertex_frame_inv() const
Returns the inverse of the matrix returned by get_vertex_frame().
Definition: eggNode.I:167
static BlendOperand string_blend_operand(const string &strval)
Returns the BlendOperand value associated with the given string representation, or BO_none if the str...
Definition: eggGroup.cxx:1090
virtual EggRenderMode * determine_depth_test_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggNode.cxx:136
void steal_vrefs(EggGroup *other)
Moves all of the vertex references from the indicated other group into this one.
Definition: eggGroup.cxx:807
static BlendMode string_blend_mode(const string &strval)
Returns the BlendMode value associated with the given string representation, or BM_none if the string...
Definition: eggGroup.cxx:1064
void write_tags(ostream &out, int indent_level) const
Writes just the &lt;Tag&gt; entries, if any, to the indicated ostream.
Definition: eggGroup.cxx:470
bool is_instance_type() const
Returns true if this group is an instance type node; i.e.
Definition: eggGroup.I:39
virtual EggRenderMode * determine_visibility_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggGroup.cxx:573
bool has_bin() const
Returns true if a bin name has been set for this particular object.
void set_row(int row, const LVecBase4d &v)
Replaces the indicated row of the matrix.
Definition: lmatrix.h:5452
virtual EggTransform * as_transform()
Returns this object cross-cast to an EggTransform pointer, if it inherits from EggTransform, or NULL if it does not.
Definition: eggGroup.cxx:1142
virtual void write(ostream &out, int indent_level) const
Writes the group and all of its children to the indicated output stream in Egg format.
Definition: eggGroup.cxx:194
bool remove_object_type(const string &object_type)
Removes the first instance of the indicated object type from the group if it is present.
Definition: eggGroup.cxx:176
void write_collide_flags(ostream &out, int indent_level) const
Writes just the &lt;Collide&gt; entry and related fields to the indicated ostream.
Definition: eggGroup.cxx:352
void unref_all_vertices()
Removes all vertices from the reference list.
Definition: eggGroup.cxx:732
void unref_vertex(EggVertex *vert)
Removes the vertex from the set of those referenced by the group.
Definition: eggGroup.cxx:714
virtual bool determine_decal()
Walks back up the hierarchy, looking for an EggGroup at this level or above that has the &quot;decal&quot; flag...
Definition: eggGroup.cxx:662
VertexRef::const_iterator vref_end() const
Returns an iterator that can, in conjunction with vref_begin(), be used to traverse the entire set of...
Definition: eggGroup.I:1061
The main glue of the egg hierarchy, this corresponds to the &lt;Group&gt;, &lt;Instance&gt;, and &lt;Joint&gt; type nod...
Definition: eggGroup.h:36
void write_model_flags(ostream &out, int indent_level) const
Writes the &lt;Model&gt; flag and related flags to the indicated ostream.
Definition: eggGroup.cxx:394
void set_billboard_center(const LPoint3d &billboard_center)
Sets the point around which the billboard will rotate, if this node contains a billboard specificatio...
Definition: eggGroup.I:91
EggGroup * get_group_ref(int n) const
Returns the nth &lt;Ref&gt; entry within this group.
Definition: eggGroup.cxx:874
This class stores miscellaneous rendering properties that is associated with geometry, and which may be set on the geometry primitive level, on the group above it, or indirectly via a texture.
Definition: eggRenderMode.h:36
void write(ostream &out, int indent_level) const
Writes the attributes to the indicated output stream in Egg format.
virtual EggRenderMode * determine_draw_order()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggGroup.cxx:607
const LMatrix4d & get_node_frame() const
Returns the coordinate frame of the node itself.
Definition: eggNode.I:152
VertexRef::const_iterator vref_begin() const
Returns an iterator that can, in conjunction with vref_end(), be used to traverse the entire set of r...
Definition: eggGroup.I:1045
virtual bool determine_decal()
Walks back up the hierarchy, looking for an EggGroup at this level or above that has the &quot;decal&quot; flag...
Definition: eggNode.cxx:248
virtual EggRenderMode * determine_visibility_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggNode.cxx:154
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
Definition: eggVertex.h:41
const LMatrix4d & get_vertex_frame() const
Returns the coordinate frame of the vertices referenced by primitives at or under this node...
Definition: eggNode.I:135
void set_vertex_membership(EggVertex *vert, double membership)
Explicitly sets the net membership of the indicated vertex in this group to the given value...
Definition: eggGroup.cxx:773
void output_hex(ostream &out, int spaces_every=4) const
Writes the BitMask out as a hexadecimal number, with spaces every four digits.
Definition: bitMask.I:606
bool has_draw_order() const
Returns true if the draw-order flag has been set for this particular object.
bool invert_from(const LMatrix4d &other)
Computes the inverse of the other matrix, and stores the result in this matrix.
Definition: lmatrix.h:6438
virtual EggRenderMode * determine_depth_write_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggNode.cxx:118
bool has_gref(const EggGroup *group) const
Returns true if the indicated group references this vertex, false otherwise.
Definition: eggVertex.cxx:830
bool has_blend_color() const
Returns true if the blend color has been specified, false otherwise.
Definition: eggGroup.I:811
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
Definition: eggGroup.cxx:756
static GroupType string_group_type(const string &strval)
Returns the GroupType value associated with the given string representation, or GT_invalid if the str...
Definition: eggGroup.cxx:912
virtual EggRenderMode * determine_alpha_mode()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggNode.cxx:100
bool has_transform() const
Returns true if the transform is nonempty, false if it is empty (no transform components have been ad...
Definition: eggTransform.I:163
void write_switch_flags(ostream &out, int indent_level) const
Writes the &lt;Switch&gt; flag and related flags to the indicated ostream.
Definition: eggGroup.cxx:425
void write(ostream &out, int indent_level, const string &label) const
Writes the transform to the indicated stream in Egg format.
bool has_object_type(const string &object_type) const
Returns true if the indicated object type has been added to the group, or false otherwise.
Definition: eggGroup.cxx:157
static DCSType string_dcs_type(const string &strval)
Returns the DCSType value associated with the given string representation, or DC_unspecified if the s...
Definition: eggGroup.cxx:954
virtual EggRenderMode * determine_bin()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggNode.cxx:208
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
DepthTestMode get_depth_test_mode() const
Returns the depth_test mode that was set, or DTM_unspecified if nothing was set.
Definition: eggRenderMode.I:73
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:746
AlphaMode get_alpha_mode() const
Returns the alpha mode that was set, or AM_unspecified if nothing was set.
const LColor & get_blend_color() const
Returns the blend color if one has been specified, or (0, 0, 0, 0) if one has not.
Definition: eggGroup.I:822
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:531
virtual bool determine_indexed()
Walks back up the hierarchy, looking for an EggGroup at this level or above that has the &quot;indexed&quot; sc...
Definition: eggGroup.cxx:643
void clear_group_refs()
Removes all of the &lt;Ref&gt; entries within this group.
Definition: eggGroup.cxx:898
void ref_vertex(EggVertex *vert, double membership=1.0)
Adds the vertex to the set of those referenced by the group, at the indicated membership level...
Definition: eggGroup.cxx:678
static BillboardType string_billboard_type(const string &strval)
Returns the BillboardType value associated with the given string representation, or BT_none if the st...
Definition: eggGroup.cxx:978
void write_object_types(ostream &out, int indent_level) const
Writes just the &lt;ObjectTypes&gt; entries, if any, to the indicated ostream.
Definition: eggGroup.cxx:442
void remove_group_ref(int n)
Removes the nth &lt;Ref&gt; entry within this group.
Definition: eggGroup.cxx:886
void write_render_mode(ostream &out, int indent_level) const
Writes the flags inherited from EggRenderMode and similar flags that control obscure render effects...
Definition: eggGroup.cxx:490
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
EggVertexPool * get_pool() const
Returns the vertex pool this vertex belongs in.
Definition: eggVertex.I:25
void write_decal_flags(ostream &out, int indent_level) const
Writes the flags related to decaling, if any.
Definition: eggGroup.cxx:457
virtual EggRenderMode * determine_depth_offset()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggNode.cxx:172
virtual EggRenderMode * determine_depth_offset()
Walks back up the hierarchy, looking for an EggGroup or EggPrimitive or some such object at this leve...
Definition: eggGroup.cxx:590
void add_matrix4(const LMatrix4d &mat)
Appends an arbitrary 4x4 matrix to the current transform.
Definition: eggTransform.I:149
A collection of vertices.
Definition: eggVertexPool.h:46
int get_num_group_refs() const
Returns the number of &lt;Ref&gt; entries within this group.
Definition: eggGroup.cxx:863
const LMatrix4d & get_node_frame_inv() const
Returns the inverse of the matrix returned by get_node_frame().
Definition: eggNode.I:183
static const LVecBase4f & zero()
Returns a zero-length vector.
Definition: lvecBase4.h:492
int get_index() const
Returns the index number of the vertex within its pool.
Definition: eggVertex.I:346
This represents the &lt;Transform&gt; entry of a group or texture node: a list of component transform opera...
Definition: eggTransform.h:33
A type of group node that holds related subnodes.
Definition: eggBin.h:30