48 make_default_lod(
const std::string &name) {
49 switch (default_lod_type.get_value()) {
58 <<
"Invalid LODNodeType value: " << (int)default_lod_type <<
"\n";
102 cdata->_center = cdata->_center * mat;
107 PN_stdfloat factor = y.length();
109 SwitchVector::iterator si;
110 for (si = cdata->_switch_vector.begin();
111 si != cdata->_switch_vector.end();
113 (*si).rescale(factor);
138 return show_switches_cull_callback(trav, data);
141 consider_verify_lods(trav, data);
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);
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];
153 if (cdata->_got_force_switch) {
154 in_range = (cdata->_force_switch == index);
156 in_range = sw.in_range_2(dist2 * cdata->_lod_scale
163 if (child !=
nullptr) {
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.";
186 SwitchVector::const_iterator si;
187 si = cdata->_switch_vector.begin();
188 out <<
"(" << (*si).get_in() <<
"/" << (*si).get_out() <<
")";
190 while (si != cdata->_switch_vector.end()) {
191 out <<
" (" << (*si).get_in() <<
"/" << (*si).get_out() <<
")";
223 do_show_switch(cdata, index, get_default_show_color(index));
224 mark_internal_bounds_stale();
241 do_show_switch(cdata, index, color);
242 mark_internal_bounds_stale();
251 do_hide_switch(cdata, index);
252 mark_internal_bounds_stale();
261 for (
int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
262 do_show_switch(cdata, i, get_default_show_color(i));
264 mark_internal_bounds_stale();
273 for (
int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
274 do_hide_switch(cdata, i);
276 mark_internal_bounds_stale();
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];
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";
312 if (data.get_net_transform(trav)->is_singular()) {
318 CDReader cdata(_cycler);
320 if (cdata->_got_force_switch) {
321 return cdata->_force_switch;
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);
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
331 if (pgraph_cat.is_debug()) {
333 << data.get_node_path() <<
" at distance " << sqrt(dist2)
334 <<
", selected child " << index <<
"\n";
341 if (pgraph_cat.is_debug()) {
343 << data.get_node_path() <<
" at distance " << sqrt(dist2)
344 <<
", no children in range.\n";
358 CDReader cdata(_cycler);
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);
367 look_at(mat, -center, LVector3(0.0f, 0.0f, 1.0f));
368 mat.set_row(3, center);
370 rel_transform->invert_compose(TransformState::make_mat(mat));
372 for (
int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
373 const Switch &sw = cdata->_switch_vector[index];
376 if (cdata->_got_force_switch) {
377 in_range = (cdata->_force_switch == index);
379 in_range = sw.in_range_2(dist2);
387 if (child !=
nullptr) {
389 next_data3._state = next_data3._state->compose(sw.get_viz_model_state());
396 next_data2.apply_transform(viz_transform);
403 next_data.apply_transform(viz_transform);
420 int &internal_vertices,
422 Thread *current_thread)
const {
433 CDStageReader cdata(_cycler, pipeline_stage, current_thread);
435 SwitchVector::const_iterator si;
436 for (si = cdata->_switch_vector.begin();
437 si != cdata->_switch_vector.end();
439 const Switch &sw = (*si);
442 child_volumes.push_back(sphere);
443 pt_volumes.push_back(sphere);
448 const BoundingVolume **child_end = child_begin + child_volumes.size();
450 bound->around(child_begin, child_end);
453 internal_bounds = bound;
454 internal_vertices = 0;
472 lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
477 cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
479 rel_transform = data.get_modelview_transform(trav);
483 return rel_transform;
490 do_show_switch(LODNode::CData *cdata,
int index,
const LColor &color) {
491 nassertv(index >= 0 && index < (
int)cdata->_switch_vector.size());
493 if (!cdata->_switch_vector[index].is_shown()) {
496 cdata->_switch_vector[index].show(color);
503 do_hide_switch(LODNode::CData *cdata,
int index) {
504 nassertv(index >= 0 && index < (
int)cdata->_switch_vector.size());
506 if (cdata->_switch_vector[index].is_shown()) {
509 cdata->_switch_vector[index].hide();
520 do_verify_child_bounds(
const LODNode::CData *cdata,
int index,
521 PN_stdfloat &suggested_radius)
const {
522 suggested_radius = 0.0f;
525 const Switch &sw = cdata->_switch_vector[index];
527 if (child !=
nullptr) {
531 if (seq == sw._bounds_seq) {
534 return sw._verify_ok;
537 ((Switch &)sw)._bounds_seq = seq;
538 ((Switch &)sw)._verify_ok =
true;
540 if (bv->is_empty()) {
544 if (bv->is_infinite()) {
552 const Switch &sw = cdata->_switch_vector[index];
555 DCAST_INTO_R(gbv, bv,
false);
557 sphere.local_object();
559 int flags = sphere.contains(gbv);
560 if ((flags & BoundingVolume::IF_all) != 0) {
570 sphere.extend_by(gbv);
571 suggested_radius = sphere.get_radius();
572 ((Switch &)sw)._verify_ok =
false;
579 LPoint3 min_point(0.0f, 0.0f, 0.0f);
580 LPoint3 max_point(0.0f, 0.0f, 0.0f);
582 bool found_any =
false;
583 child->calc_tight_bounds(min_point, max_point, found_any,
584 TransformState::make_identity(),
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]);
601 box_sphere.local_object();
605 flags = sphere.contains(&box_sphere);
606 if ((flags & BoundingVolume::IF_all) == 0) {
609 sphere.extend_by(&box_sphere);
611 sphere.extend_by(gbv);
613 suggested_radius = sphere.get_radius();
614 ((Switch &)sw)._verify_ok =
false;
630 CDLockedReader cdata(_cycler);
632 if (cdata->_got_force_switch) {
639 if (seq != cdata->_bounds_seq) {
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;
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());
654 CDWriter cdataw(_cycler, cdata);
655 cdataw->_bounds_seq = seq;
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),
672 static const int num_default_colors =
sizeof(default_colors) /
sizeof(LColor);
674 return default_colors[index % num_default_colors];
709 node->fillin(scan, manager);
720 PandaNode::fillin(scan, manager);
729 return new CData(*
this);
736 void LODNode::CData::
740 for (
size_t i = 1; i < _switch_vector.size(); ++i) {
741 if (_switch_vector[i].get_out() > _switch_vector[_lowest].get_out()) {
744 if (_switch_vector[i].get_in() < _switch_vector[_highest].get_in()) {
754 void LODNode::CData::
756 _center.write_datagram(dg);
760 SwitchVector::const_iterator si;
761 for (si = _switch_vector.begin();
762 si != _switch_vector.end();
764 (*si).write_datagram(dg);
772 void LODNode::CData::
774 _center.read_datagram(scan);
776 _switch_vector.clear();
779 _switch_vector.reserve(num_switches);
780 for (
int i = 0; i < num_switches; i++) {
782 sw.read_datagram(scan);
784 _switch_vector.push_back(sw);
793 void LODNode::Switch::
797 static const int num_slices = 50;
798 static const int num_rings = 1;
802 static const PN_stdfloat edge_ratio = 0.1;
814 for (ri = 0; ri <= num_rings; ++ri) {
816 PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
819 PN_stdfloat d = r * (_in - _out) + _out;
821 for (si = 0; si < num_slices; ++si) {
823 PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
826 PN_stdfloat t = MathNumbers::pi * 2.0f * s;
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);
837 for (ri = 0; ri <= 1; ++ri) {
838 PN_stdfloat r = (PN_stdfloat)ri;
839 PN_stdfloat d = r * (_in - _out) + _out;
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;
845 PN_stdfloat x = ccos(t);
846 PN_stdfloat y = csin(t);
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);
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;
857 PN_stdfloat x = ccos(t);
858 PN_stdfloat y = csin(t);
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);
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);
873 strips->add_vertex(ri * num_slices);
874 strips->add_vertex((ri + 1) * num_slices);
875 strips->close_primitive();
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);
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();
890 ring_geom->add_primitive(strips);
893 geom_node->add_geom(ring_geom);
897 material->set_twoside(
true);
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());
911 geom_node->set_state(viz_state);
913 _ring_viz = geom_node.p();
920 void LODNode::Switch::
921 compute_spindle_viz() {
925 static const int num_slices = 10;
926 static const int num_rings = 10;
937 for (ri = 0; ri <= num_rings; ++ri) {
939 PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
942 PN_stdfloat z = 100.0f - r * 200.0f;
944 for (si = 0; si < num_slices; ++si) {
946 PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
949 PN_stdfloat t = MathNumbers::pi * 2.0f * s;
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);
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);
966 strips->add_vertex(ri * num_slices);
967 strips->add_vertex((ri + 1) * num_slices);
968 strips->close_primitive();
972 spindle_geom->add_primitive(strips);
975 geom_node->add_geom(spindle_geom);
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());
987 geom_node->set_state(viz_state);
989 _spindle_viz = geom_node.p();
996 void LODNode::Switch::
997 compute_viz_model_state() {
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);