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