Panda3D
 All Classes Functions Variables Enumerations
lodNode.cxx
1 // Filename: lodNode.cxx
2 // Created by: drose (06Mar02)
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 "lodNode.h"
16 #include "fadeLodNode.h"
17 #include "cullTraverserData.h"
18 #include "cullTraverser.h"
19 #include "config_pgraphnodes.h"
20 #include "geomVertexData.h"
21 #include "geomVertexWriter.h"
22 #include "geomVertexFormat.h"
23 #include "geomTristrips.h"
24 #include "mathNumbers.h"
25 #include "geom.h"
26 #include "geomNode.h"
27 #include "transformState.h"
28 #include "material.h"
29 #include "materialAttrib.h"
30 #include "materialPool.h"
31 #include "renderState.h"
32 #include "cullFaceAttrib.h"
33 #include "textureAttrib.h"
34 #include "boundingSphere.h"
35 #include "geometricBoundingVolume.h"
36 #include "look_at.h"
37 #include "nodePath.h"
38 #include "shaderAttrib.h"
39 #include "colorAttrib.h"
40 #include "clipPlaneAttrib.h"
41 
42 TypeHandle LODNode::_type_handle;
43 
44 ////////////////////////////////////////////////////////////////////
45 // Function: LODNode::make_default_lod
46 // Access: Published, Static
47 // Description: Creates a new LODNode of the type specified by the
48 // default-lod-type config variable.
49 ////////////////////////////////////////////////////////////////////
50 PT(LODNode) LODNode::
51 make_default_lod(const string &name) {
52  switch (default_lod_type.get_value()) {
53  case LNT_pop:
54  return new LODNode(name);
55 
56  case LNT_fade:
57  return new FadeLODNode(name);
58 
59  default:
60  pgraph_cat.error()
61  << "Invalid LODNodeType value: " << (int)default_lod_type << "\n";
62  return new LODNode(name);
63  }
64 }
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: LODNode::make_copy
68 // Access: Public, Virtual
69 // Description: Returns a newly-allocated Node that is a shallow copy
70 // of this one. It will be a different Node pointer,
71 // but its internal data may or may not be shared with
72 // that of the original Node.
73 ////////////////////////////////////////////////////////////////////
75 make_copy() const {
76  return new LODNode(*this);
77 }
78 
79 ////////////////////////////////////////////////////////////////////
80 // Function: LODNode::safe_to_combine
81 // Access: Public, Virtual
82 // Description: Returns true if it is generally safe to combine this
83 // particular kind of PandaNode with other kinds of
84 // PandaNodes of compatible type, adding children or
85 // whatever. For instance, an LODNode should not be
86 // combined with any other PandaNode, because its set of
87 // children is meaningful.
88 ////////////////////////////////////////////////////////////////////
89 bool LODNode::
90 safe_to_combine() const {
91  return false;
92 }
93 
94 ////////////////////////////////////////////////////////////////////
95 // Function: LODNode::safe_to_combine_children
96 // Access: Public, Virtual
97 // Description: Returns true if it is generally safe to combine the
98 // children of this PandaNode with each other. For
99 // instance, an LODNode's children should not be
100 // combined with each other, because the set of children
101 // is meaningful.
102 ////////////////////////////////////////////////////////////////////
103 bool LODNode::
105  return false;
106 }
107 
108 ////////////////////////////////////////////////////////////////////
109 // Function: LODNode::xform
110 // Access: Public, Virtual
111 // Description: Transforms the contents of this PandaNode by the
112 // indicated matrix, if it means anything to do so. For
113 // most kinds of PandaNodes, this does nothing.
114 ////////////////////////////////////////////////////////////////////
115 void LODNode::
116 xform(const LMatrix4 &mat) {
117  CDWriter cdata(_cycler);
118 
119  cdata->_center = cdata->_center * mat;
120 
121  // We'll take just the length of the y axis as the matrix's scale.
122  LVector3 y;
123  mat.get_row3(y, 1);
124  PN_stdfloat factor = y.length();
125 
126  SwitchVector::iterator si;
127  for (si = cdata->_switch_vector.begin();
128  si != cdata->_switch_vector.end();
129  ++si) {
130  (*si).rescale(factor);
131  }
132 }
133 
134 ////////////////////////////////////////////////////////////////////
135 // Function: LODNode::cull_callback
136 // Access: Public, Virtual
137 // Description: This function will be called during the cull
138 // traversal to perform any additional operations that
139 // should be performed at cull time. This may include
140 // additional manipulation of render state or additional
141 // visible/invisible decisions, or any other arbitrary
142 // operation.
143 //
144 // Note that this function will *not* be called unless
145 // set_cull_callback() is called in the constructor of
146 // the derived class. It is necessary to call
147 // set_cull_callback() to indicated that we require
148 // cull_callback() to be called.
149 //
150 // By the time this function is called, the node has
151 // already passed the bounding-volume test for the
152 // viewing frustum, and the node's transform and state
153 // have already been applied to the indicated
154 // CullTraverserData object.
155 //
156 // The return value is true if this node should be
157 // visible, or false if it should be culled.
158 ////////////////////////////////////////////////////////////////////
159 bool LODNode::
161  if (is_any_shown()) {
162  return show_switches_cull_callback(trav, data);
163  }
164 
165  consider_verify_lods(trav, data);
166 
167  CDReader cdata(_cycler);
168 
169  CPT(TransformState) rel_transform = get_rel_transform(trav, data);
170  LPoint3 center = cdata->_center * rel_transform->get_mat();
171  PN_stdfloat dist2 = center.dot(center);
172 
173  int num_children = min(get_num_children(), (int)cdata->_switch_vector.size());
174  for (int index = 0; index < num_children; ++index) {
175  const Switch &sw = cdata->_switch_vector[index];
176  bool in_range;
177  if (cdata->_got_force_switch) {
178  in_range = (cdata->_force_switch == index);
179  } else {
180  in_range = sw.in_range_2(dist2 * cdata->_lod_scale
181  * trav->get_scene()->get_camera_node()->get_lod_scale());
182  }
183 
184  if (in_range) {
185  // This switch level is in range. Draw its children.
186  PandaNode *child = get_child(index);
187  if (child != (PandaNode *)NULL) {
188  CullTraverserData next_data(data, child);
189  trav->traverse(next_data);
190  }
191  }
192  }
193 
194  // Now return false indicating that we have already taken care of
195  // the traversal from here.
196  return false;
197 }
198 
199 ////////////////////////////////////////////////////////////////////
200 // Function: LODNode::output
201 // Access: Public, Virtual
202 // Description:
203 ////////////////////////////////////////////////////////////////////
204 void LODNode::
205 output(ostream &out) const {
206  PandaNode::output(out);
207  CDReader cdata(_cycler);
208  out << " center(" << cdata->_center << ") ";
209  if (cdata->_switch_vector.empty()) {
210  out << "no switches.";
211  } else {
212  SwitchVector::const_iterator si;
213  si = cdata->_switch_vector.begin();
214  out << "(" << (*si).get_in() << "/" << (*si).get_out() << ")";
215  ++si;
216  while (si != cdata->_switch_vector.end()) {
217  out << " (" << (*si).get_in() << "/" << (*si).get_out() << ")";
218  ++si;
219  }
220  }
221 }
222 
223 ////////////////////////////////////////////////////////////////////
224 // Function: LODNode::is_lod_node
225 // Access: Published, Virtual
226 // Description: A simple downcast check. Returns true if this kind
227 // of node happens to inherit from LODNode, false
228 // otherwise.
229 //
230 // This is provided as a a faster alternative to calling
231 // is_of_type(LODNode::get_class_type()).
232 ////////////////////////////////////////////////////////////////////
233 bool LODNode::
234 is_lod_node() const {
235  return true;
236 }
237 
238 ////////////////////////////////////////////////////////////////////
239 // Function: LODNode::show_switch
240 // Access: Published
241 // Description: This is provided as a debugging aid. show_switch()
242 // will put the LODNode into a special mode where rather
243 // than computing and drawing the appropriate level of
244 // the LOD, a ring is drawn around the LODNode center
245 // indicating the switch distances from the camera for
246 // the indicated level, and the geometry of the
247 // indicated level is drawn in wireframe.
248 //
249 // Multiple different levels can be visualized this way
250 // at once. Call hide_switch() or hide_all_switches() to
251 // undo this mode and restore the LODNode to its normal
252 // behavior.
253 ////////////////////////////////////////////////////////////////////
254 void LODNode::
255 show_switch(int index) {
256  CDWriter cdata(_cycler);
257  do_show_switch(cdata, index, get_default_show_color(index));
259 }
260 
261 ////////////////////////////////////////////////////////////////////
262 // Function: LODNode::show_switch
263 // Access: Published
264 // Description: This is provided as a debugging aid. show_switch()
265 // will put the LODNode into a special mode where rather
266 // than computing and drawing the appropriate level of
267 // the LOD, a ring is drawn around the LODNode center
268 // indicating the switch distances from the camera for
269 // the indicated level, and the geometry of the
270 // indicated level is drawn in wireframe.
271 //
272 // Multiple different levels can be visualized this way
273 // at once. Call hide_switch() or hide_all_switches() to
274 // undo this mode and restore the LODNode to its normal
275 // behavior.
276 ////////////////////////////////////////////////////////////////////
277 void LODNode::
278 show_switch(int index, const LColor &color) {
279  CDWriter cdata(_cycler);
280  do_show_switch(cdata, index, color);
282 }
283 
284 ////////////////////////////////////////////////////////////////////
285 // Function: LODNode::hide_switch
286 // Access: Published
287 // Description: Disables a previous call to show_switch().
288 ////////////////////////////////////////////////////////////////////
289 void LODNode::
290 hide_switch(int index) {
291  CDWriter cdata(_cycler);
292  do_hide_switch(cdata, index);
294 }
295 
296 ////////////////////////////////////////////////////////////////////
297 // Function: LODNode::show_all_switches
298 // Access: Published
299 // Description: Shows all levels in their default colors.
300 ////////////////////////////////////////////////////////////////////
301 void LODNode::
303  CDWriter cdata(_cycler);
304  for (int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
305  do_show_switch(cdata, i, get_default_show_color(i));
306  }
308 }
309 
310 ////////////////////////////////////////////////////////////////////
311 // Function: LODNode::hide_all_switches
312 // Access: Published
313 // Description: Hides all levels, restoring the LODNode to normal
314 // operation.
315 ////////////////////////////////////////////////////////////////////
316 void LODNode::
318  CDWriter cdata(_cycler);
319  for (int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
320  do_hide_switch(cdata, i);
321  }
323 }
324 
325 ////////////////////////////////////////////////////////////////////
326 // Function: LODNode::verify_child_bounds
327 // Access: Published
328 // Description: Returns true if the bounding volumes for the geometry
329 // of each fhild node entirely fits within the
330 // switch_in radius for that child, or false otherwise.
331 // It is almost always a mistake for the geometry of an
332 // LOD level to be larger than its switch_in radius.
333 ////////////////////////////////////////////////////////////////////
334 bool LODNode::
336  bool okflag = true;
337  CDReader cdata(_cycler);
338 
339  for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
340  PN_stdfloat suggested_radius;
341  if (!do_verify_child_bounds(cdata, index, suggested_radius)) {
342  const Switch &sw = cdata->_switch_vector[index];
343  pgraph_cat.warning()
344  << "Level " << index << " geometry of " << *this
345  << " is larger than its switch radius; suggest radius of "
346  << suggested_radius << " instead of " << sw.get_in() << "\n";
347  okflag = false;
348  }
349  }
350 
351  return okflag;
352 }
353 
354 ////////////////////////////////////////////////////////////////////
355 // Function: LODNode::compute_child
356 // Access: Protected
357 // Description: Determines which child should be visible according to
358 // the current camera position. If a child is visible,
359 // returns its index number; otherwise, returns -1.
360 ////////////////////////////////////////////////////////////////////
361 int LODNode::
362 compute_child(CullTraverser *trav, CullTraverserData &data) {
363  if (data.get_net_transform(trav)->is_singular()) {
364  // If we're under a singular transform, we can't compute the LOD;
365  // select none of them instead.
366  return -1;
367  }
368 
369  CDReader cdata(_cycler);
370 
371  if (cdata->_got_force_switch) {
372  return cdata->_force_switch;
373  }
374 
375  CPT(TransformState) rel_transform = get_rel_transform(trav, data);
376  LPoint3 center = cdata->_center * rel_transform->get_mat();
377  PN_stdfloat dist2 = center.dot(center);
378 
379  for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
380  if (cdata->_switch_vector[index].in_range_2(dist2 * cdata->_lod_scale
381  * trav->get_scene()->get_camera_node()->get_lod_scale())) {
382  if (pgraph_cat.is_debug()) {
383  pgraph_cat.debug()
384  << data._node_path << " at distance " << sqrt(dist2)
385  << ", selected child " << index << "\n";
386  }
387 
388  return index;
389  }
390  }
391 
392  if (pgraph_cat.is_debug()) {
393  pgraph_cat.debug()
394  << data._node_path << " at distance " << sqrt(dist2)
395  << ", no children in range.\n";
396  }
397 
398  return -1;
399 }
400 
401 
402 ////////////////////////////////////////////////////////////////////
403 // Function: LODNode::show_switches_cull_callback
404 // Access: Protected
405 // Description: A special version of cull_callback() that is to be
406 // invoked when the LODNode is in show_switch() mode.
407 // This just draws the rings and the wireframe geometry
408 // for the selected switches.
409 ////////////////////////////////////////////////////////////////////
410 bool LODNode::
411 show_switches_cull_callback(CullTraverser *trav, CullTraverserData &data) {
412  CDReader cdata(_cycler);
413 
414  CPT(TransformState) rel_transform = get_rel_transform(trav, data);
415  LPoint3 center = cdata->_center * rel_transform->get_mat();
416  PN_stdfloat dist2 = center.dot(center);
417 
418  // Now orient the disk(s) in camera space such that their origin is
419  // at center, and the (0, 0, 0) point in camera space is on the disk.
420  LMatrix4 mat;
421  look_at(mat, -center, LVector3(0.0f, 0.0f, 1.0f));
422  mat.set_row(3, center);
423  CPT(TransformState) viz_transform =
424  rel_transform->invert_compose(TransformState::make_mat(mat));
425 
426  for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
427  const Switch &sw = cdata->_switch_vector[index];
428  if (sw.is_shown()) {
429  bool in_range;
430  if (cdata->_got_force_switch) {
431  in_range = (cdata->_force_switch == index);
432  } else {
433  in_range = sw.in_range_2(dist2);
434  }
435 
436  if (in_range) {
437  // This switch level is in range. Draw its children in the
438  // funny wireframe mode.
439  if (index < get_num_children()) {
440  PandaNode *child = get_child(index);
441  if (child != (PandaNode *)NULL) {
442  CullTraverserData next_data3(data, child);
443  next_data3._state = next_data3._state->compose(sw.get_viz_model_state());
444  trav->traverse(next_data3);
445  }
446  }
447 
448  // And draw the spindle in this color.
449  CullTraverserData next_data2(data, sw.get_spindle_viz());
450  next_data2.apply_transform_and_state(trav, viz_transform,
451  RenderState::make_empty(),
452  RenderEffects::make_empty(),
453  ClipPlaneAttrib::make());
454  trav->traverse(next_data2);
455  }
456 
457  // Draw the rings for this switch level. We do this after we
458  // have drawn the geometry and the spindle.
459  CullTraverserData next_data(data, sw.get_ring_viz());
460  next_data.apply_transform_and_state(trav, viz_transform,
461  RenderState::make_empty(),
462  RenderEffects::make_empty(),
463  ClipPlaneAttrib::make());
464  trav->traverse(next_data);
465  }
466  }
467 
468  // Now return false indicating that we have already taken care of
469  // the traversal from here.
470  return false;
471 }
472 
473 ////////////////////////////////////////////////////////////////////
474 // Function: LODNode::compute_internal_bounds
475 // Access: Protected, Virtual
476 // Description: Returns a newly-allocated BoundingVolume that
477 // represents the internal contents of the node. Should
478 // be overridden by PandaNode classes that contain
479 // something internally.
480 ////////////////////////////////////////////////////////////////////
481 void LODNode::
482 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
483  int &internal_vertices,
484  int pipeline_stage,
485  Thread *current_thread) const {
486  // First, get ourselves a fresh, empty bounding volume.
487  PT(BoundingVolume) bound = new BoundingSphere;
488 
489  // If we have any visible rings, those count in the bounding volume.
490  if (is_any_shown()) {
491  // Now actually compute the bounding volume by putting it around all
492  // of our geoms' bounding volumes.
493  pvector<const BoundingVolume *> child_volumes;
494  pvector<PT(BoundingVolume) > pt_volumes;
495 
496  CDStageReader cdata(_cycler, pipeline_stage, current_thread);
497 
498  SwitchVector::const_iterator si;
499  for (si = cdata->_switch_vector.begin();
500  si != cdata->_switch_vector.end();
501  ++si) {
502  const Switch &sw = (*si);
503  if (sw.is_shown()) {
504  PT(BoundingVolume) sphere = new BoundingSphere(cdata->_center, sw.get_in());
505  child_volumes.push_back(sphere);
506  pt_volumes.push_back(sphere);
507  }
508  }
509 
510  const BoundingVolume **child_begin = &child_volumes[0];
511  const BoundingVolume **child_end = child_begin + child_volumes.size();
512 
513  bound->around(child_begin, child_end);
514  }
515 
516  internal_bounds = bound;
517  internal_vertices = 0;
518 }
519 
520 ////////////////////////////////////////////////////////////////////
521 // Function: LODNode::get_rel_transform
522 // Access: Protected
523 // Description: Returns the relative transform to convert from the
524 // LODNode space to the camera space.
525 ////////////////////////////////////////////////////////////////////
526 CPT(TransformState) LODNode::
527 get_rel_transform(CullTraverser *trav, CullTraverserData &data) {
528  // Get a pointer to the camera node.
529  Camera *camera = trav->get_scene()->get_camera_node();
530 
531  // Get the camera space transform.
532  CPT(TransformState) rel_transform;
533 
534  NodePath lod_center = camera->get_lod_center();
535  if (!lod_center.is_empty()) {
536  rel_transform =
537  lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
538  } else {
539  NodePath cull_center = camera->get_cull_center();
540  if (!cull_center.is_empty()) {
541  rel_transform =
542  cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
543  } else {
544  rel_transform = data.get_modelview_transform(trav);
545  }
546  }
547 
548  return rel_transform;
549 }
550 
551 ////////////////////////////////////////////////////////////////////
552 // Function: LODNode::do_show_switch
553 // Access: Private
554 // Description: The private implementation of show_switch().
555 ////////////////////////////////////////////////////////////////////
556 void LODNode::
557 do_show_switch(LODNode::CData *cdata, int index, const LColor &color) {
558  nassertv(index >= 0 && index < (int)cdata->_switch_vector.size());
559 
560  if (!cdata->_switch_vector[index].is_shown()) {
561  ++cdata->_num_shown;
562  }
563  cdata->_switch_vector[index].show(color);
564 }
565 
566 ////////////////////////////////////////////////////////////////////
567 // Function: LODNode::do_hide_switch
568 // Access: Private
569 // Description: The private implementation of hide_switch().
570 ////////////////////////////////////////////////////////////////////
571 void LODNode::
572 do_hide_switch(LODNode::CData *cdata, int index) {
573  nassertv(index >= 0 && index < (int)cdata->_switch_vector.size());
574 
575  if (cdata->_switch_vector[index].is_shown()) {
576  --cdata->_num_shown;
577  }
578  cdata->_switch_vector[index].hide();
579 }
580 
581 ////////////////////////////////////////////////////////////////////
582 // Function: LODNode::do_verify_child_bounds
583 // Access: Private
584 // Description: The private implementation of verify_child_bounds(),
585 // this checks the bounding volume of just one child.
586 //
587 // If the return value is false, suggested_radius is
588 // filled with a radius that ought to be large enough to
589 // include the child.
590 ////////////////////////////////////////////////////////////////////
591 bool LODNode::
592 do_verify_child_bounds(const LODNode::CData *cdata, int index,
593  PN_stdfloat &suggested_radius) const {
594  suggested_radius = 0.0f;
595 
596  if (index < get_num_children()) {
597  const Switch &sw = cdata->_switch_vector[index];
598  PandaNode *child = get_child(index);
599  if (child != (PandaNode *)NULL) {
600  UpdateSeq seq;
601  CPT(BoundingVolume) bv = child->get_bounds(seq);
602 
603  if (seq == sw._bounds_seq) {
604  // We previously verified this child, and it hasn't changed
605  // since then.
606  return sw._verify_ok;
607  }
608 
609  ((Switch &)sw)._bounds_seq = seq;
610  ((Switch &)sw)._verify_ok = true;
611 
612  if (bv->is_empty()) {
613  // This child has no geometry, so no one cares anyway.
614  return true;
615  }
616  if (bv->is_infinite()) {
617  // To be strict, we ought to look closer if the child has an
618  // infinite bounding volume, but in practice this is probably
619  // just a special case (e.g. the child contains the camera)
620  // that we don't really want to check.
621  return true;
622  }
623 
624  const Switch &sw = cdata->_switch_vector[index];
625 
626  const GeometricBoundingVolume *gbv;
627  DCAST_INTO_R(gbv, bv, false);
628  BoundingSphere sphere(cdata->_center, sw.get_in());
629  sphere.local_object();
630 
631  int flags = sphere.contains(gbv);
632  if ((flags & BoundingVolume::IF_all) != 0) {
633  // This child's radius completely encloses its bounding volume.
634  // Perfect. (And this is the most common case.)
635  return true;
636  }
637 
638  if (flags == 0) {
639  // This child's radius doesn't even come close to containing
640  // its volume.
641  nassertr(!gbv->is_infinite(), false);
642  sphere.extend_by(gbv);
643  suggested_radius = sphere.get_radius();
644  ((Switch &)sw)._verify_ok = false;
645  return false;
646  }
647 
648  // This child's radius partially encloses its (loose) bounding
649  // volume. We have to look closer to determine whether it, in
650  // fact, fully encloses its geometry.
651  LPoint3 min_point(0.0f, 0.0f, 0.0f);
652  LPoint3 max_point(0.0f, 0.0f, 0.0f);
653 
654  bool found_any = false;
655  child->calc_tight_bounds(min_point, max_point, found_any,
656  TransformState::make_identity(),
658  if (!found_any) {
659  // Hmm, the child has no geometry after all.
660  return true;
661  }
662 
663  // Now we have a bounding box. Define the largest sphere we can
664  // that fits within this box. All we can say about this sphere
665  // is that it should definitely fit entirely within a bounding
666  // sphere that contains all the points of the child.
667  LPoint3 box_center = (min_point + max_point) / 2.0f;
668  PN_stdfloat box_radius = min(min(max_point[0] - box_center[0],
669  max_point[1] - box_center[1]),
670  max_point[2] - box_center[2]);
671 
672  BoundingSphere box_sphere(box_center, box_radius);
673  box_sphere.local_object();
674 
675  // So if any part of this inscribed sphere is outside of the
676  // radius, then the radius is bad.
677  flags = sphere.contains(&box_sphere);
678  if ((flags & BoundingVolume::IF_all) == 0) {
679  // No good.
680  if (gbv->is_infinite()) {
681  sphere.extend_by(&box_sphere);
682  } else {
683  sphere.extend_by(gbv);
684  }
685  suggested_radius = sphere.get_radius();
686  ((Switch &)sw)._verify_ok = false;
687  return false;
688  }
689  }
690  }
691 
692  return true;
693 }
694 
695 ////////////////////////////////////////////////////////////////////
696 // Function: LODNode::do_auto_verify_lods
697 // Access: Private
698 // Description: Called internally by consider_verify_lods().
699 ////////////////////////////////////////////////////////////////////
700 void LODNode::
701 do_auto_verify_lods(CullTraverser *trav, CullTraverserData &data) {
702  UpdateSeq seq;
703  get_bounds(seq);
704  CDLockedReader cdata(_cycler);
705 
706  if (cdata->_got_force_switch) {
707  // If we're forcing a particular switch, don't verify the LOD
708  // sizes, since they don't really apply anymore anyway. Assume
709  // the user knows what he's doing.
710  return;
711  }
712 
713  if (seq != cdata->_bounds_seq) {
714  // Time to validate the children again.
715  for (int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
716  PN_stdfloat suggested_radius;
717  if (!do_verify_child_bounds(cdata, index, suggested_radius)) {
718  const Switch &sw = cdata->_switch_vector[index];
719  ostringstream strm;
720  strm
721  << "Level " << index << " geometry of " << data._node_path
722  << " is larger than its switch radius; suggest radius of "
723  << suggested_radius << " instead of " << sw.get_in()
724  << " (configure verify-lods 0 to ignore this error)";
725  nassert_raise(strm.str());
726  }
727  }
728  CDWriter cdataw(_cycler, cdata);
729  cdataw->_bounds_seq = seq;
730  }
731 }
732 
733 ////////////////////////////////////////////////////////////////////
734 // Function: LODNode::get_default_show_color
735 // Access: Private, Static
736 // Description: Returns a default color appropriate for showing the
737 // indicated level.
738 ////////////////////////////////////////////////////////////////////
739 const LColor &LODNode::
740 get_default_show_color(int index) {
741  static LColor default_colors[] = {
742  LColor(1.0f, 0.0f, 0.0f, 0.7f),
743  LColor(0.0f, 1.0f, 0.0f, 0.7f),
744  LColor(0.0f, 0.0f, 1.0f, 0.7f),
745  LColor(0.0f, 1.0f, 1.0f, 0.7f),
746  LColor(1.0f, 0.0f, 1.0f, 0.7f),
747  LColor(1.0f, 1.0f, 0.0f, 0.7f),
748  };
749  static const int num_default_colors = sizeof(default_colors) / sizeof(LColor);
750 
751  return default_colors[index % num_default_colors];
752 }
753 
754 
755 ////////////////////////////////////////////////////////////////////
756 // Function: LODNode::register_with_read_factory
757 // Access: Public, Static
758 // Description: Tells the BamReader how to create objects of type
759 // LODNode.
760 ////////////////////////////////////////////////////////////////////
761 void LODNode::
763  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
764 }
765 
766 ////////////////////////////////////////////////////////////////////
767 // Function: LODNode::write_datagram
768 // Access: Public, Virtual
769 // Description: Writes the contents of this object to the datagram
770 // for shipping out to a Bam file.
771 ////////////////////////////////////////////////////////////////////
772 void LODNode::
774  PandaNode::write_datagram(manager, dg);
775  manager->write_cdata(dg, _cycler);
776 }
777 
778 ////////////////////////////////////////////////////////////////////
779 // Function: LODNode::make_from_bam
780 // Access: Protected, Static
781 // Description: This function is called by the BamReader's factory
782 // when a new object of type LODNode is encountered
783 // in the Bam file. It should create the LODNode
784 // and extract its information from the file.
785 ////////////////////////////////////////////////////////////////////
786 TypedWritable *LODNode::
787 make_from_bam(const FactoryParams &params) {
788  LODNode *node = new LODNode("");
789 
790  DatagramIterator scan;
791  BamReader *manager;
792 
793  parse_params(params, scan, manager);
794  node->fillin(scan, manager);
795 
796  return node;
797 }
798 
799 ////////////////////////////////////////////////////////////////////
800 // Function: LODNode::fillin
801 // Access: Protected
802 // Description: This internal function is called by make_from_bam to
803 // read in all of the relevant data from the BamFile for
804 // the new LODNode.
805 ////////////////////////////////////////////////////////////////////
806 void LODNode::
807 fillin(DatagramIterator &scan, BamReader *manager) {
808  PandaNode::fillin(scan, manager);
809  manager->read_cdata(scan, _cycler);
810 }
811 
812 ////////////////////////////////////////////////////////////////////
813 // Function: LODNode::CData::make_copy
814 // Access: Public, Virtual
815 // Description:
816 ////////////////////////////////////////////////////////////////////
817 CycleData *LODNode::CData::
818 make_copy() const {
819  return new CData(*this);
820 }
821 
822 ////////////////////////////////////////////////////////////////////
823 // Function: LODNode::CData::check_limits
824 // Access: Public
825 // Description: Ensures that the _lowest and _highest members are set
826 // appropriately after a change to the set of switches.
827 ////////////////////////////////////////////////////////////////////
828 void LODNode::CData::
829 check_limits() {
830  _lowest = 0;
831  _highest = 0;
832  for (size_t i = 1; i < _switch_vector.size(); ++i) {
833  if (_switch_vector[i].get_out() > _switch_vector[_lowest].get_out()) {
834  _lowest = i;
835  }
836  if (_switch_vector[i].get_in() < _switch_vector[_highest].get_in()) {
837  _highest = i;
838  }
839  }
840 }
841 
842 ////////////////////////////////////////////////////////////////////
843 // Function: LODNode::CData::write_datagram
844 // Access: Public, Virtual
845 // Description: Writes the contents of this object to the datagram
846 // for shipping out to a Bam file.
847 ////////////////////////////////////////////////////////////////////
848 void LODNode::CData::
849 write_datagram(BamWriter *manager, Datagram &dg) const {
850  _center.write_datagram(dg);
851 
852  dg.add_uint16(_switch_vector.size());
853 
854  SwitchVector::const_iterator si;
855  for (si = _switch_vector.begin();
856  si != _switch_vector.end();
857  ++si) {
858  (*si).write_datagram(dg);
859  }
860 }
861 
862 ////////////////////////////////////////////////////////////////////
863 // Function: LODNode::CData::fillin
864 // Access: Public, Virtual
865 // Description: This internal function is called by make_from_bam to
866 // read in all of the relevant data from the BamFile for
867 // the new LODNode.
868 ////////////////////////////////////////////////////////////////////
869 void LODNode::CData::
870 fillin(DatagramIterator &scan, BamReader *manager) {
871  _center.read_datagram(scan);
872 
873  _switch_vector.clear();
874 
875  int num_switches = scan.get_uint16();
876  _switch_vector.reserve(num_switches);
877  for (int i = 0; i < num_switches; i++) {
878  Switch sw(0, 0);
879  sw.read_datagram(scan);
880 
881  _switch_vector.push_back(sw);
882  }
883  _lod_scale = 1;
884 }
885 
886 ////////////////////////////////////////////////////////////////////
887 // Function: LODNode::Switch::compute_ring_viz
888 // Access: Private
889 // Description: Computes a Geom suitable for rendering the ring
890 // associated with this switch.
891 ////////////////////////////////////////////////////////////////////
892 void LODNode::Switch::
893 compute_ring_viz() {
894  // We render the ring as a series of concentric ring-shaped triangle
895  // strips, each of which has num_slices quads.
896  static const int num_slices = 50;
897  static const int num_rings = 1;
898 
899  // There are also two more triangle strips, one for the outer edge,
900  // and one for the inner edge.
901  static const PN_stdfloat edge_ratio = 0.1; // ratio of edge height to diameter.
902 
903  const GeomVertexFormat *format = GeomVertexFormat::get_v3n3cp();
904  PT(GeomVertexData) vdata = new GeomVertexData("LOD_ring", format, Geom::UH_static);
905 
906  // Fill up the vertex table with all of the vertices.
907  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
908  GeomVertexWriter normal(vdata, InternalName::get_normal());
909  GeomVertexWriter color(vdata, InternalName::get_color());
910 
911  // First, the vertices for the flat ring.
912  int ri, si;
913  for (ri = 0; ri <= num_rings; ++ri) {
914  // r is in the range [0.0, 1.0].
915  PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
916 
917  // d is in the range [_out, _in].
918  PN_stdfloat d = r * (_in - _out) + _out;
919 
920  for (si = 0; si < num_slices; ++si) {
921  // s is in the range [0.0, 1.0).
922  PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
923 
924  // t is in the range [0.0, 2pi).
925  PN_stdfloat t = MathNumbers::pi * 2.0f * s;
926 
927  PN_stdfloat x = ccos(t);
928  PN_stdfloat y = csin(t);
929  vertex.add_data3(x * d, y * d, 0.0f);
930  normal.add_data3(0.0f, 0.0f, 1.0f);
931  color.add_data4(_show_color);
932  }
933  }
934 
935  // Next, the vertices for the inner and outer edges.
936  for (ri = 0; ri <= 1; ++ri) {
937  PN_stdfloat r = (PN_stdfloat)ri;
938  PN_stdfloat d = r * (_in - _out) + _out;
939 
940  for (si = 0; si < num_slices; ++si) {
941  PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
942  PN_stdfloat t = MathNumbers::pi * 2.0f * s;
943 
944  PN_stdfloat x = ccos(t);
945  PN_stdfloat y = csin(t);
946 
947  vertex.add_data3(x * d, y * d, 0.5f * edge_ratio * d);
948  normal.add_data3(x, y, 0.0f);
949  color.add_data4(_show_color);
950  }
951 
952  for (si = 0; si < num_slices; ++si) {
953  PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
954  PN_stdfloat t = MathNumbers::pi * 2.0f * s;
955 
956  PN_stdfloat x = ccos(t);
957  PN_stdfloat y = csin(t);
958 
959  vertex.add_data3(x * d, y * d, -0.5f * edge_ratio * d);
960  normal.add_data3(x, y, 0.0f);
961  color.add_data4(_show_color);
962  }
963  }
964 
965  // Now create the triangle strips. One tristrip for each ring.
966  PT(GeomTristrips) strips = new GeomTristrips(Geom::UH_static);
967  for (ri = 0; ri < num_rings; ++ri) {
968  for (si = 0; si < num_slices; ++si) {
969  strips->add_vertex(ri * num_slices + si);
970  strips->add_vertex((ri + 1) * num_slices + si);
971  }
972  strips->add_vertex(ri * num_slices);
973  strips->add_vertex((ri + 1) * num_slices);
974  strips->close_primitive();
975  }
976 
977  // And then one triangle strip for each of the inner and outer
978  // edges.
979  for (ri = 0; ri <= 1; ++ri) {
980  for (si = 0; si < num_slices; ++si) {
981  strips->add_vertex((num_rings + 1 + ri * 2) * num_slices + si);
982  strips->add_vertex((num_rings + 1 + ri * 2 + 1) * num_slices + si);
983  }
984  strips->add_vertex((num_rings + 1 + ri * 2) * num_slices);
985  strips->add_vertex((num_rings + 1 + ri * 2 + 1) * num_slices);
986  strips->close_primitive();
987  }
988 
989  PT(Geom) ring_geom = new Geom(vdata);
990  ring_geom->add_primitive(strips);
991 
992  PT(GeomNode) geom_node = new GeomNode("ring");
993  geom_node->add_geom(ring_geom);
994 
995  // Get a material for two-sided lighting.
996  PT(Material) material = new Material();
997  material->set_twoside(true);
998  material = MaterialPool::get_material(material);
999 
1000  CPT(RenderState) viz_state =
1001  RenderState::make(CullFaceAttrib::make(CullFaceAttrib::M_cull_none),
1002  TextureAttrib::make_off(),
1003  ShaderAttrib::make_off(),
1004  MaterialAttrib::make(material),
1005  RenderState::get_max_priority());
1006  if (_show_color[3] != 1.0f) {
1007  viz_state = viz_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha),
1009  }
1010 
1011  geom_node->set_state(viz_state);
1012 
1013  _ring_viz = geom_node.p();
1014 }
1015 
1016 ////////////////////////////////////////////////////////////////////
1017 // Function: LODNode::Switch::compute_spindle_viz
1018 // Access: Private
1019 // Description: Computes a Geom suitable for rendering the LODNode
1020 // spindle in the color of this switch.
1021 ////////////////////////////////////////////////////////////////////
1022 void LODNode::Switch::
1023 compute_spindle_viz() {
1024  // We render the spindle as a cylinder, which consists of num_rings
1025  // rings stacked vertically, each of which is a triangle strip of
1026  // num_slices quads. The scale is -10 .. 10 vertically, with a radius
1027  // of 1.0.
1028  static const int num_slices = 10;
1029  static const int num_rings = 10;
1030 
1031  const GeomVertexFormat *format = GeomVertexFormat::get_v3n3cp();
1032  PT(GeomVertexData) vdata = new GeomVertexData("LOD_spindle", format, Geom::UH_static);
1033 
1034  // Fill up the vertex table with all of the vertices.
1035  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
1036  GeomVertexWriter normal(vdata, InternalName::get_normal());
1037  GeomVertexWriter color(vdata, InternalName::get_color());
1038 
1039  int ri, si;
1040  for (ri = 0; ri <= num_rings; ++ri) {
1041  // r is in the range [0.0, 1.0].
1042  PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
1043 
1044  // z is in the range [100.0, -100.0]
1045  PN_stdfloat z = 100.0f - r * 200.0f;
1046 
1047  for (si = 0; si < num_slices; ++si) {
1048  // s is in the range [0.0, 1.0).
1049  PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
1050 
1051  // t is in the range [0.0, 2pi).
1052  PN_stdfloat t = MathNumbers::pi * 2.0f * s;
1053 
1054  PN_stdfloat x = ccos(t);
1055  PN_stdfloat y = csin(t);
1056  vertex.add_data3(x, y, z);
1057  normal.add_data3(x, y, 0.0f);
1058  color.add_data4(_show_color);
1059  }
1060  }
1061 
1062  // Now create the triangle strips. One tristrip for each ring.
1063  PT(GeomTristrips) strips = new GeomTristrips(Geom::UH_static);
1064  for (ri = 0; ri < num_rings; ++ri) {
1065  for (si = 0; si < num_slices; ++si) {
1066  strips->add_vertex(ri * num_slices + si);
1067  strips->add_vertex((ri + 1) * num_slices + si);
1068  }
1069  strips->add_vertex(ri * num_slices);
1070  strips->add_vertex((ri + 1) * num_slices);
1071  strips->close_primitive();
1072  }
1073 
1074  PT(Geom) spindle_geom = new Geom(vdata);
1075  spindle_geom->add_primitive(strips);
1076 
1077  PT(GeomNode) geom_node = new GeomNode("spindle");
1078  geom_node->add_geom(spindle_geom);
1079 
1080  CPT(RenderState) viz_state =
1081  RenderState::make(CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise),
1082  TextureAttrib::make_off(),
1083  ShaderAttrib::make_off(),
1084  RenderState::get_max_priority());
1085  if (_show_color[3] != 1.0f) {
1086  viz_state = viz_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha),
1088  }
1089 
1090  geom_node->set_state(viz_state);
1091 
1092  _spindle_viz = geom_node.p();
1093 }
1094 
1095 ////////////////////////////////////////////////////////////////////
1096 // Function: LODNode::Switch::compute_viz_model_state
1097 // Access: Private
1098 // Description: Computes a RenderState for rendering the children of
1099 // this switch in colored wireframe mode.
1100 ////////////////////////////////////////////////////////////////////
1101 void LODNode::Switch::
1102 compute_viz_model_state() {
1103  // The RenderState::make() function only takes up to four attribs at
1104  // once. Since we need more attribs than that, we have to make up
1105  // our state in two steps.
1106  _viz_model_state = RenderState::make(RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
1107  TextureAttrib::make_off(),
1108  ShaderAttrib::make_off(),
1109  ColorAttrib::make_flat(_show_color),
1111  CPT(RenderState) st2 = RenderState::make(TransparencyAttrib::make(TransparencyAttrib::M_none),
1112  RenderState::get_max_priority());
1113  _viz_model_state = _viz_model_state->compose(st2);
1114 }
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
virtual bool is_lod_node() const
A simple downcast check.
Definition: lodNode.cxx:234
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
PN_stdfloat get_in(int index) const
Returns the &quot;in&quot; distance of the indicated switch range.
Definition: lodNode.I:154
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: pandaNode.cxx:4164
virtual bool safe_to_combine_children() const
Returns true if it is generally safe to combine the children of this PandaNode with each other...
Definition: lodNode.cxx:104
bool is_any_shown() const
Returns true if any switch has been shown with show_switch(), indicating the LODNode is in debug show...
Definition: lodNode.I:267
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:747
Indicates which, if any, material should be applied to geometry.
static int get_max_priority()
Returns the maximum priority number (sometimes called override) that may be set on any node...
virtual bool safe_to_combine() const
Returns true if it is generally safe to combine this particular kind of PandaNode with other kinds of...
Definition: lodNode.cxx:90
This controls the enabling of transparency.
This defines a bounding sphere, consisting of a center and a radius.
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:398
The MaterialPool (there is only one in the universe) serves to unify different pointers to the same M...
Definition: materialPool.h:42
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
Definition: lodNode.cxx:160
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
Defines a series of triangle strips.
Definition: geomTristrips.h:25
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
void hide_switch(int index)
Disables a previous call to show_switch().
Definition: lodNode.cxx:290
void hide_all_switches()
Hides all levels, restoring the LODNode to normal operation.
Definition: lodNode.cxx:317
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:43
Camera * get_camera_node() const
Returns the camera used to render the scene.
Definition: sceneSetup.I:144
PN_uint16 get_uint16()
Extracts an unsigned 16-bit integer.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Definition: textureAttrib.h:34
Indicates which faces should be culled based on their vertex ordering.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
static void register_with_read_factory()
Tells the BamReader how to create objects of type LODNode.
Definition: lodNode.cxx:762
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
float length() const
Returns the length of the vector, by the Pythagorean theorem.
Definition: lvecBase3.h:765
void show_switch(int index)
This is provided as a debugging aid.
Definition: lodNode.cxx:255
PN_stdfloat get_out(int index) const
Returns the &quot;out&quot; distance of the indicated switch range.
Definition: lodNode.I:168
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
void add_vertex(int vertex)
Adds the indicated vertex to the list of vertex indices used by the graphics primitive type...
A Level-of-Detail node with alpha based switching.
Definition: fadeLodNode.h:26
void mark_internal_bounds_stale(Thread *current_thread=Thread::get_current_thread())
Should be called by a derived class to mark the internal bounding volume stale, so that compute_inter...
Definition: pandaNode.cxx:2467
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
A Level-of-Detail node.
Definition: lodNode.h:31
const NodePath & get_cull_center() const
Returns the point from which the culling operations will be performed, if it was set by set_cull_cent...
Definition: camera.I:147
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of child nodes this node has.
Definition: pandaNode.I:68
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this PandaNode by the indicated matrix, if it means anything to do so...
Definition: lodNode.cxx:116
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
PN_stdfloat get_lod_scale() const
Returns the multiplier for LOD distances.
Definition: camera.I:260
Defines the way an object appears in the presence of lighting.
Definition: material.h:34
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:181
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
bool is_infinite() const
The other side of the empty coin is an infinite volume.
void apply_transform_and_state(CullTraverser *trav)
Applies the transform and state from the current node onto the current data.
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition: lodNode.cxx:75
PandaNode * get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the nth child node of this node.
Definition: pandaNode.I:82
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
A thread; that is, a lightweight process.
Definition: thread.h:51
void show_all_switches()
Shows all levels in their default colors.
Definition: lodNode.cxx:302
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:236
bool verify_child_bounds() const
Returns true if the bounding volumes for the geometry of each fhild node entirely fits within the swi...
Definition: lodNode.cxx:335
LVecBase3f get_row3(int row) const
Retrieves the row column of the matrix as a 3-component vector, ignoring the last column...
Definition: lmatrix.h:1312
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
This is a sequence number that increments monotonically.
Definition: updateSeq.h:43
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition: camera.h:37
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: lodNode.cxx:773
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37