16 #include "fadeLodNode.h"
17 #include "cullTraverserData.h"
18 #include "cullTraverser.h"
19 #include "config_pgraphnodes.h"
20 #include "geomVertexData.h"
21 #include "geomVertexWriter.h"
22 #include "geomVertexFormat.h"
23 #include "geomTristrips.h"
24 #include "mathNumbers.h"
27 #include "transformState.h"
29 #include "materialAttrib.h"
30 #include "materialPool.h"
31 #include "renderState.h"
32 #include "cullFaceAttrib.h"
33 #include "textureAttrib.h"
34 #include "boundingSphere.h"
35 #include "geometricBoundingVolume.h"
38 #include "shaderAttrib.h"
39 #include "colorAttrib.h"
40 #include "clipPlaneAttrib.h"
51 make_default_lod(const
string &name) {
52 switch (default_lod_type.get_value()) {
61 <<
"Invalid LODNodeType value: " << (int)default_lod_type <<
"\n";
119 cdata->_center = cdata->_center * mat;
124 PN_stdfloat factor = y.
length();
126 SwitchVector::iterator si;
127 for (si = cdata->_switch_vector.begin();
128 si != cdata->_switch_vector.end();
130 (*si).rescale(factor);
162 return show_switches_cull_callback(trav, data);
165 consider_verify_lods(trav, data);
169 CPT(TransformState) rel_transform = get_rel_transform(trav, data);
170 LPoint3 center = cdata->_center * rel_transform->get_mat();
171 PN_stdfloat dist2 = center.dot(center);
173 int num_children = min(
get_num_children(), (
int)cdata->_switch_vector.size());
174 for (
int index = 0; index < num_children; ++index) {
175 const Switch &sw = cdata->_switch_vector[index];
177 if (cdata->_got_force_switch) {
178 in_range = (cdata->_force_switch == index);
180 in_range = sw.in_range_2(dist2 * cdata->_lod_scale
205 output(ostream &out)
const {
206 PandaNode::output(out);
207 CDReader cdata(_cycler);
208 out <<
" center(" << cdata->_center <<
") ";
209 if (cdata->_switch_vector.empty()) {
210 out <<
"no switches.";
212 SwitchVector::const_iterator si;
213 si = cdata->_switch_vector.begin();
214 out <<
"(" << (*si).get_in() <<
"/" << (*si).get_out() <<
")";
216 while (si != cdata->_switch_vector.end()) {
217 out <<
" (" << (*si).get_in() <<
"/" << (*si).get_out() <<
")";
257 do_show_switch(cdata, index, get_default_show_color(index));
280 do_show_switch(cdata, index, color);
292 do_hide_switch(cdata, index);
304 for (
int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
305 do_show_switch(cdata, i, get_default_show_color(i));
319 for (
int i = 0; i < (int)cdata->_switch_vector.size(); ++i) {
320 do_hide_switch(cdata, i);
339 for (
int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
340 PN_stdfloat suggested_radius;
341 if (!do_verify_child_bounds(cdata, index, suggested_radius)) {
342 const Switch &sw = cdata->_switch_vector[index];
344 <<
"Level " << index <<
" geometry of " << *
this
345 <<
" is larger than its switch radius; suggest radius of "
346 << suggested_radius <<
" instead of " << sw.get_in() <<
"\n";
363 if (data.get_net_transform(trav)->is_singular()) {
369 CDReader cdata(_cycler);
371 if (cdata->_got_force_switch) {
372 return cdata->_force_switch;
375 CPT(TransformState) rel_transform = get_rel_transform(trav, data);
376 LPoint3 center = cdata->_center * rel_transform->get_mat();
377 PN_stdfloat dist2 = center.dot(center);
379 for (
int index = 0; index < (
int)cdata->_switch_vector.size(); ++index) {
380 if (cdata->_switch_vector[index].in_range_2(dist2 * cdata->_lod_scale
381 * trav->get_scene()->get_camera_node()->get_lod_scale())) {
382 if (pgraph_cat.is_debug()) {
384 << data._node_path <<
" at distance " << sqrt(dist2)
385 <<
", selected child " << index <<
"\n";
392 if (pgraph_cat.is_debug()) {
394 << data._node_path <<
" at distance " << sqrt(dist2)
395 <<
", no children in range.\n";
412 CDReader cdata(_cycler);
414 CPT(TransformState) rel_transform = get_rel_transform(trav, data);
415 LPoint3 center = cdata->_center * rel_transform->get_mat();
416 PN_stdfloat dist2 = center.dot(center);
421 look_at(mat, -center,
LVector3(0.0f, 0.0f, 1.0f));
422 mat.set_row(3, center);
423 CPT(TransformState) viz_transform =
424 rel_transform->invert_compose(TransformState::make_mat(mat));
426 for (
int index = 0; index < (
int)cdata->_switch_vector.size(); ++index) {
427 const Switch &sw = cdata->_switch_vector[index];
430 if (cdata->_got_force_switch) {
431 in_range = (cdata->_force_switch == index);
433 in_range = sw.in_range_2(dist2);
443 next_data3._state = next_data3._state->compose(sw.get_viz_model_state());
444 trav->traverse(next_data3);
451 RenderState::make_empty(),
452 RenderEffects::make_empty(),
453 ClipPlaneAttrib::make());
454 trav->traverse(next_data2);
461 RenderState::make_empty(),
462 RenderEffects::make_empty(),
463 ClipPlaneAttrib::make());
464 trav->traverse(next_data);
483 int &internal_vertices,
485 Thread *current_thread)
const {
496 CDStageReader cdata(_cycler, pipeline_stage, current_thread);
498 SwitchVector::const_iterator si;
499 for (si = cdata->_switch_vector.begin();
500 si != cdata->_switch_vector.end();
502 const Switch &sw = (*si);
505 child_volumes.push_back(sphere);
506 pt_volumes.push_back(sphere);
511 const
BoundingVolume **child_end = child_begin + child_volumes.size();
513 bound->around(child_begin, child_end);
516 internal_bounds = bound;
517 internal_vertices = 0;
529 Camera *camera = trav->get_scene()->get_camera_node();
532 CPT(TransformState) rel_transform;
534 NodePath lod_center = camera->get_lod_center();
535 if (!lod_center.is_empty()) {
537 lod_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
542 cull_center.get_net_transform()->invert_compose(data.get_net_transform(trav));
544 rel_transform = data.get_modelview_transform(trav);
548 return rel_transform;
557 do_show_switch(LODNode::CData *cdata,
int index,
const LColor &
color) {
558 nassertv(index >= 0 && index < (
int)cdata->_switch_vector.size());
560 if (!cdata->_switch_vector[index].is_shown()) {
563 cdata->_switch_vector[index].show(color);
572 do_hide_switch(LODNode::CData *cdata,
int index) {
573 nassertv(index >= 0 && index < (
int)cdata->_switch_vector.size());
575 if (cdata->_switch_vector[index].is_shown()) {
578 cdata->_switch_vector[index].hide();
592 do_verify_child_bounds(
const LODNode::CData *cdata,
int index,
593 PN_stdfloat &suggested_radius)
const {
594 suggested_radius = 0.0f;
597 const Switch &sw = cdata->_switch_vector[index];
603 if (seq == sw._bounds_seq) {
606 return sw._verify_ok;
609 ((Switch &)sw)._bounds_seq = seq;
610 ((Switch &)sw)._verify_ok =
true;
612 if (bv->is_empty()) {
616 if (bv->is_infinite()) {
624 const Switch &sw = cdata->_switch_vector[index];
627 DCAST_INTO_R(gbv, bv,
false);
628 BoundingSphere sphere(cdata->_center, sw.get_in());
629 sphere.local_object();
631 int flags = sphere.contains(gbv);
632 if ((flags & BoundingVolume::IF_all) != 0) {
642 sphere.extend_by(gbv);
643 suggested_radius = sphere.get_radius();
644 ((Switch &)sw)._verify_ok =
false;
651 LPoint3 min_point(0.0f, 0.0f, 0.0f);
652 LPoint3 max_point(0.0f, 0.0f, 0.0f);
654 bool found_any =
false;
655 child->calc_tight_bounds(min_point, max_point, found_any,
656 TransformState::make_identity(),
667 LPoint3 box_center = (min_point + max_point) / 2.0f;
668 PN_stdfloat box_radius = min(min(max_point[0] - box_center[0],
669 max_point[1] - box_center[1]),
670 max_point[2] - box_center[2]);
672 BoundingSphere box_sphere(box_center, box_radius);
673 box_sphere.local_object();
677 flags = sphere.contains(&box_sphere);
678 if ((flags & BoundingVolume::IF_all) == 0) {
681 sphere.extend_by(&box_sphere);
683 sphere.extend_by(gbv);
685 suggested_radius = sphere.get_radius();
686 ((Switch &)sw)._verify_ok =
false;
704 CDLockedReader cdata(_cycler);
706 if (cdata->_got_force_switch) {
713 if (seq != cdata->_bounds_seq) {
715 for (
int index = 0; index < (int)cdata->_switch_vector.size(); ++index) {
716 PN_stdfloat suggested_radius;
717 if (!do_verify_child_bounds(cdata, index, suggested_radius)) {
718 const Switch &sw = cdata->_switch_vector[index];
721 <<
"Level " << index <<
" geometry of " << data._node_path
722 <<
" is larger than its switch radius; suggest radius of "
723 << suggested_radius <<
" instead of " << sw.get_in()
724 <<
" (configure verify-lods 0 to ignore this error)";
725 nassert_raise(strm.str());
728 CDWriter cdataw(_cycler, cdata);
729 cdataw->_bounds_seq = seq;
740 get_default_show_color(
int index) {
741 static LColor default_colors[] = {
742 LColor(1.0f, 0.0f, 0.0f, 0.7f),
743 LColor(0.0f, 1.0f, 0.0f, 0.7f),
744 LColor(0.0f, 0.0f, 1.0f, 0.7f),
745 LColor(0.0f, 1.0f, 1.0f, 0.7f),
746 LColor(1.0f, 0.0f, 1.0f, 0.7f),
747 LColor(1.0f, 1.0f, 0.0f, 0.7f),
749 static const int num_default_colors =
sizeof(default_colors) /
sizeof(
LColor);
751 return default_colors[index % num_default_colors];
793 parse_params(params, scan, manager);
794 node->fillin(scan, manager);
808 PandaNode::fillin(scan, manager);
819 return new CData(*
this);
828 void LODNode::CData::
832 for (
size_t i = 1; i < _switch_vector.size(); ++i) {
833 if (_switch_vector[i].
get_out() > _switch_vector[_lowest].get_out()) {
836 if (_switch_vector[i].
get_in() < _switch_vector[_highest].get_in()) {
848 void LODNode::CData::
850 _center.write_datagram(dg);
854 SwitchVector::const_iterator si;
855 for (si = _switch_vector.begin();
856 si != _switch_vector.end();
858 (*si).write_datagram(dg);
869 void LODNode::CData::
871 _center.read_datagram(scan);
873 _switch_vector.clear();
876 _switch_vector.reserve(num_switches);
877 for (
int i = 0; i < num_switches; i++) {
879 sw.read_datagram(scan);
881 _switch_vector.push_back(sw);
892 void LODNode::Switch::
896 static const int num_slices = 50;
897 static const int num_rings = 1;
901 static const PN_stdfloat edge_ratio = 0.1;
903 const GeomVertexFormat *format = GeomVertexFormat::get_v3n3cp();
913 for (ri = 0; ri <= num_rings; ++ri) {
915 PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
918 PN_stdfloat d = r * (_in - _out) + _out;
920 for (si = 0; si < num_slices; ++si) {
922 PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
925 PN_stdfloat t = MathNumbers::pi * 2.0f * s;
927 PN_stdfloat x = ccos(t);
928 PN_stdfloat y = csin(t);
929 vertex.add_data3(x * d, y * d, 0.0f);
930 normal.add_data3(0.0f, 0.0f, 1.0f);
931 color.add_data4(_show_color);
936 for (ri = 0; ri <= 1; ++ri) {
937 PN_stdfloat r = (PN_stdfloat)ri;
938 PN_stdfloat d = r * (_in - _out) + _out;
940 for (si = 0; si < num_slices; ++si) {
941 PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
942 PN_stdfloat t = MathNumbers::pi * 2.0f * s;
944 PN_stdfloat x = ccos(t);
945 PN_stdfloat y = csin(t);
947 vertex.add_data3(x * d, y * d, 0.5f * edge_ratio * d);
948 normal.add_data3(x, y, 0.0f);
949 color.add_data4(_show_color);
952 for (si = 0; si < num_slices; ++si) {
953 PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
954 PN_stdfloat t = MathNumbers::pi * 2.0f * s;
956 PN_stdfloat x = ccos(t);
957 PN_stdfloat y = csin(t);
959 vertex.add_data3(x * d, y * d, -0.5f * edge_ratio * d);
960 normal.add_data3(x, y, 0.0f);
961 color.add_data4(_show_color);
967 for (ri = 0; ri < num_rings; ++ri) {
968 for (si = 0; si < num_slices; ++si) {
970 strips->add_vertex((ri + 1) * num_slices + si);
972 strips->add_vertex(ri * num_slices);
973 strips->add_vertex((ri + 1) * num_slices);
974 strips->close_primitive();
979 for (ri = 0; ri <= 1; ++ri) {
980 for (si = 0; si < num_slices; ++si) {
981 strips->add_vertex((num_rings + 1 + ri * 2) * num_slices + si);
982 strips->add_vertex((num_rings + 1 + ri * 2 + 1) * num_slices + si);
984 strips->add_vertex((num_rings + 1 + ri * 2) * num_slices);
985 strips->add_vertex((num_rings + 1 + ri * 2 + 1) * num_slices);
986 strips->close_primitive();
989 PT(
Geom) ring_geom = new
Geom(vdata);
990 ring_geom->add_primitive(strips);
993 geom_node->add_geom(ring_geom);
997 material->set_twoside(true);
1006 if (_show_color[3] != 1.0f) {
1007 viz_state = viz_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha),
1011 geom_node->set_state(viz_state);
1013 _ring_viz = geom_node.p();
1022 void LODNode::Switch::
1023 compute_spindle_viz() {
1028 static const int num_slices = 10;
1029 static const int num_rings = 10;
1031 const GeomVertexFormat *format = GeomVertexFormat::get_v3n3cp();
1040 for (ri = 0; ri <= num_rings; ++ri) {
1042 PN_stdfloat r = (PN_stdfloat)ri / (PN_stdfloat)num_rings;
1045 PN_stdfloat z = 100.0f - r * 200.0f;
1047 for (si = 0; si < num_slices; ++si) {
1049 PN_stdfloat s = (PN_stdfloat)si / (PN_stdfloat)num_slices;
1052 PN_stdfloat t = MathNumbers::pi * 2.0f * s;
1054 PN_stdfloat x = ccos(t);
1055 PN_stdfloat y = csin(t);
1056 vertex.add_data3(x, y, z);
1057 normal.add_data3(x, y, 0.0f);
1058 color.add_data4(_show_color);
1064 for (ri = 0; ri < num_rings; ++ri) {
1065 for (si = 0; si < num_slices; ++si) {
1067 strips->add_vertex((ri + 1) * num_slices + si);
1069 strips->add_vertex(ri * num_slices);
1070 strips->add_vertex((ri + 1) * num_slices);
1071 strips->close_primitive();
1074 PT(
Geom) spindle_geom = new
Geom(vdata);
1075 spindle_geom->add_primitive(strips);
1078 geom_node->add_geom(spindle_geom);
1085 if (_show_color[3] != 1.0f) {
1086 viz_state = viz_state->add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha),
1090 geom_node->set_state(viz_state);
1092 _spindle_viz = geom_node.p();
1101 void LODNode::Switch::
1102 compute_viz_model_state() {
1106 _viz_model_state = RenderState::make(RenderModeAttrib::make(RenderModeAttrib::M_wireframe),
1107 TextureAttrib::make_off(),
1108 ShaderAttrib::make_off(),
1109 ColorAttrib::make_flat(_show_color),
1113 _viz_model_state = _viz_model_state->compose(st2);
A basic node of the scene graph or data graph.
virtual bool is_lod_node() const
A simple downcast check.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
PN_stdfloat get_in(int index) const
Returns the "in" distance of the indicated switch range.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
virtual bool safe_to_combine_children() const
Returns true if it is generally safe to combine the children of this PandaNode with each other...
bool is_any_shown() const
Returns true if any switch has been shown with show_switch(), indicating the LODNode is in debug show...
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Indicates which, if any, material should be applied to geometry.
static int get_max_priority()
Returns the maximum priority number (sometimes called override) that may be set on any node...
virtual bool safe_to_combine() const
Returns true if it is generally safe to combine this particular kind of PandaNode with other kinds of...
This controls the enabling of transparency.
This defines a bounding sphere, consisting of a center and a radius.
A single page of data maintained by a PipelineCycler.
Base class for objects that can be written to and read from Bam files.
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
The MaterialPool (there is only one in the universe) serves to unify different pointers to the same M...
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Defines a series of triangle strips.
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
void hide_switch(int index)
Disables a previous call to show_switch().
void hide_all_switches()
Hides all levels, restoring the LODNode to normal operation.
SceneSetup * get_scene() const
Returns the SceneSetup object.
Camera * get_camera_node() const
Returns the camera used to render the scene.
PN_uint16 get_uint16()
Extracts an unsigned 16-bit integer.
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
Indicates the set of TextureStages and their associated Textures that should be applied to (or remove...
Indicates which faces should be culled based on their vertex ordering.
This is our own Panda specialization on the default STL vector.
static void register_with_read_factory()
Tells the BamReader how to create objects of type LODNode.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
float length() const
Returns the length of the vector, by the Pythagorean theorem.
void show_switch(int index)
This is provided as a debugging aid.
PN_stdfloat get_out(int index) const
Returns the "out" distance of the indicated switch range.
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
void add_vertex(int vertex)
Adds the indicated vertex to the list of vertex indices used by the graphics primitive type...
A Level-of-Detail node with alpha based switching.
void mark_internal_bounds_stale(Thread *current_thread=Thread::get_current_thread())
Should be called by a derived class to mark the internal bounding volume stale, so that compute_inter...
This is a 4-by-4 transform matrix.
const NodePath & get_cull_center() const
Returns the point from which the culling operations will be performed, if it was set by set_cull_cent...
int get_num_children(Thread *current_thread=Thread::get_current_thread()) const
Returns the number of child nodes this node has.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
virtual void xform(const LMatrix4 &mat)
Transforms the contents of this PandaNode by the indicated matrix, if it means anything to do so...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
PN_stdfloat get_lod_scale() const
Returns the multiplier for LOD distances.
Defines the way an object appears in the presence of lighting.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
This is the base class for all three-component vectors and points.
bool is_infinite() const
The other side of the empty coin is an infinite volume.
void apply_transform_and_state(CullTraverser *trav)
Applies the transform and state from the current node onto the current data.
virtual PandaNode * make_copy() const
Returns a newly-allocated Node that is a shallow copy of this one.
PandaNode * get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns the nth child node of this node.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
A thread; that is, a lightweight process.
void show_all_switches()
Shows all levels in their default colors.
bool is_empty() const
Returns true if the NodePath contains no nodes.
bool verify_child_bounds() const
Returns true if the bounding volumes for the geometry of each fhild node entirely fits within the swi...
LVecBase3f get_row3(int row) const
Retrieves the row column of the matrix as a 3-component vector, ignoring the last column...
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
This is a sequence number that increments monotonically.
A node that can be positioned around in the scene graph to represent a point of view for rendering a ...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
A node that holds Geom objects, renderable pieces of geometry.