Panda3D
Loading...
Searching...
No Matches
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
41TypeHandle LODNode::_type_handle;
42
43/**
44 * Creates a new LODNode of the type specified by the default-lod-type config
45 * variable.
46 */
47PT(LODNode) LODNode::
48make_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 */
69make_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 */
80safe_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 */
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 */
99xform(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 */
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 */
178void LODNode::
179output(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 */
205is_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 */
221show_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 */
239show_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 */
249hide_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 */
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 */
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 */
286verify_child_bounds() const {
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 */
310int LODNode::
311compute_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 */
356bool LODNode::
357show_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 */
418void LODNode::
419compute_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.
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 */
461CPT(TransformState) LODNode::
462get_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 */
489void LODNode::
490do_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 */
502void LODNode::
503do_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 */
519bool LODNode::
520do_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(),
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 */
626void LODNode::
627do_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 */
662const LColor &LODNode::
663get_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 */
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 */
691write_datagram(BamWriter *manager, Datagram &dg) {
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 */
701TypedWritable *LODNode::
702make_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 */
718void LODNode::
719fillin(DatagramIterator &scan, BamReader *manager) {
720 PandaNode::fillin(scan, manager);
721 manager->read_cdata(scan, _cycler);
722}
723
724/**
725 *
726 */
727CycleData *LODNode::CData::
728make_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 */
736void LODNode::CData::
737check_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 */
754void LODNode::CData::
755write_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 */
772void LODNode::CData::
773fillin(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 */
793void LODNode::Switch::
794compute_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 */
920void LODNode::Switch::
921compute_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 */
996void LODNode::Switch::
997compute_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}
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
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.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
This defines a bounding sphere, consisting of a center and a radius.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
bool is_infinite() const
The other side of the empty coin is an infinite volume.
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
Definition camera.h:35
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
get_lod_scale
Returns the multiplier for LOD distances.
Definition camera.h:87
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
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
SceneSetup * get_scene() const
Returns the SceneSetup object.
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
A single page of data maintained by a PipelineCycler.
Definition cycleData.h:50
A class to retrieve the individual data elements previously stored in a Datagram.
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition datagram.I:85
An instance of this class is passed to the Factory when requesting it to do its business and construc...
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
A Level-of-Detail node with alpha based switching.
Definition fadeLodNode.h:24
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
Defines a series of triangle strips.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
This class defines the physical layout of the vertex data stored within a Geom.
static const GeomVertexFormat * get_v3n3cp()
Returns a standard vertex format with a packed color, a 3-component normal, and a 3-component vertex ...
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition geom.h:54
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
A Level-of-Detail node.
Definition lodNode.h:28
get_out
Returns the "out" distance of the indicated switch range.
Definition lodNode.h:61
get_in
Returns the "in" distance of the indicated switch range.
Definition lodNode.h:59
void show_all_switches()
Shows all levels in their default colors.
Definition lodNode.cxx:259
void hide_all_switches()
Hides all levels, restoring the LODNode to normal operation.
Definition lodNode.cxx:271
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
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
static void register_with_read_factory()
Tells the BamReader how to create objects of type LODNode.
Definition lodNode.cxx:682
void hide_switch(int index)
Disables a previous call to show_switch().
Definition lodNode.cxx:249
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
Definition lodNode.cxx:69
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
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
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
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
void show_switch(int index)
This is provided as a debugging aid.
Definition lodNode.cxx:221
virtual bool is_lod_node() const
A simple downcast check.
Definition lodNode.cxx:205
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
static Material * get_material(Material *temp)
Returns a Material pointer that represents the same material described by temp, except that it is a s...
Defines the way an object appears in the presence of lighting.
Definition material.h:43
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition nodePath.I:188
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
get_child
Returns the nth child node of this node.
Definition pandaNode.h:124
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
get_num_children
Returns the number of child nodes this node has.
Definition pandaNode.h:124
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
Camera * get_camera_node() const
Returns the camera used to render the scene.
Definition sceneSetup.I:115
A thread; that is, a lightweight process.
Definition thread.h:46
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
Indicates a coordinate-system transform on vertices.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
This is a sequence number that increments monotonically.
Definition updateSeq.h:37
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.