|
|
@@ -17,14 +17,13 @@
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
#include "qpcullTraverser.h"
|
|
|
+#include "cullTraverserData.h"
|
|
|
#include "transformState.h"
|
|
|
#include "renderState.h"
|
|
|
#include "billboardAttrib.h"
|
|
|
#include "cullHandler.h"
|
|
|
#include "dcast.h"
|
|
|
#include "qpgeomNode.h"
|
|
|
-#include "colorAttrib.h"
|
|
|
-#include "textureAttrib.h"
|
|
|
#include "config_pgraph.h"
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -130,8 +129,10 @@ void qpCullTraverser::
|
|
|
traverse(PandaNode *root) {
|
|
|
nassertv(_cull_handler != (CullHandler *)NULL);
|
|
|
|
|
|
- r_traverse(root, _render_transform, TransformState::make_identity(),
|
|
|
- _initial_state, _view_frustum, _guard_band);
|
|
|
+ CullTraverserData data(_render_transform, TransformState::make_identity(),
|
|
|
+ _initial_state, _view_frustum, _guard_band,
|
|
|
+ _camera_transform);
|
|
|
+ r_traverse(root, data);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -140,134 +141,55 @@ traverse(PandaNode *root) {
|
|
|
// Description: The recursive traversal implementation.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void qpCullTraverser::
|
|
|
-r_traverse(PandaNode *node,
|
|
|
- const TransformState *render_transform,
|
|
|
- const TransformState *net_transform,
|
|
|
- const RenderState *state,
|
|
|
- GeometricBoundingVolume *view_frustum,
|
|
|
- GeometricBoundingVolume *guard_band) {
|
|
|
- CPT(RenderState) next_state = state;
|
|
|
+r_traverse(PandaNode *node, const CullTraverserData &data) {
|
|
|
+ CullTraverserData next_data(data);
|
|
|
|
|
|
- if (view_frustum != (GeometricBoundingVolume *)NULL) {
|
|
|
- // If we have a viewing frustum, check to see if the node's
|
|
|
- // bounding volume falls within it.
|
|
|
- const BoundingVolume &node_volume = node->get_bound();
|
|
|
- nassertv(node_volume.is_of_type(GeometricBoundingVolume::get_class_type()));
|
|
|
- const GeometricBoundingVolume *node_gbv =
|
|
|
- DCAST(GeometricBoundingVolume, &node_volume);
|
|
|
+ // Most nodes will have no transform or state, and will not
|
|
|
+ // contain decals or require a special cull callback. As an
|
|
|
+ // optimization, we should tag nodes with these properties as
|
|
|
+ // being "fancy", and skip this processing for non-fancy nodes.
|
|
|
|
|
|
- int result = view_frustum->contains(node_gbv);
|
|
|
- if (result == BoundingVolume::IF_no_intersection) {
|
|
|
- // No intersection at all. Cull.
|
|
|
- if (!qpfake_view_frustum_cull) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // If we have fake view-frustum culling enabled, instead of
|
|
|
- // actually culling an object we simply force it to be drawn in
|
|
|
- // red wireframe.
|
|
|
- view_frustum = (GeometricBoundingVolume *)NULL;
|
|
|
- CPT(RenderState) fake_effect = get_fake_view_frustum_cull_effect();
|
|
|
- next_state = next_state->compose(fake_effect);
|
|
|
-
|
|
|
- } else if ((result & BoundingVolume::IF_all) != 0) {
|
|
|
- // The node and its descendants are completely enclosed within
|
|
|
- // the frustum. No need to cull further.
|
|
|
- view_frustum = (GeometricBoundingVolume *)NULL;
|
|
|
+ if (next_data.is_in_view(node)) {
|
|
|
+ next_data.apply_transform_and_state(node);
|
|
|
|
|
|
- } else {
|
|
|
- if (node->is_final()) {
|
|
|
- // The bounding volume is partially, but not completely,
|
|
|
- // within the viewing frustum. Normally we'd keep testing
|
|
|
- // child bounding volumes as we continue down. But this node
|
|
|
- // has the "final" flag, so the user is claiming that there is
|
|
|
- // some important reason we should consider everything visible
|
|
|
- // at this point. So be it.
|
|
|
- view_frustum = (GeometricBoundingVolume *)NULL;
|
|
|
+ if (node->has_cull_callback()) {
|
|
|
+ if (!node->cull_callback(next_data)) {
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- CPT(TransformState) next_render_transform = render_transform;
|
|
|
- CPT(TransformState) next_net_transform = net_transform;
|
|
|
- PT(GeometricBoundingVolume) next_view_frustum = view_frustum;
|
|
|
- PT(GeometricBoundingVolume) next_guard_band = guard_band;
|
|
|
|
|
|
- const TransformState *node_transform = node->get_transform();
|
|
|
- if (!node_transform->is_identity()) {
|
|
|
- next_render_transform = render_transform->compose(node_transform);
|
|
|
- next_net_transform = net_transform->compose(node_transform);
|
|
|
-
|
|
|
- if ((view_frustum != (GeometricBoundingVolume *)NULL) ||
|
|
|
- (guard_band != (GeometricBoundingVolume *)NULL)) {
|
|
|
- // We need to move the viewing frustums into the node's
|
|
|
- // coordinate space by applying the node's inverse transform.
|
|
|
- if (node_transform->is_singular()) {
|
|
|
- // But we can't invert a singular transform! Instead of
|
|
|
- // trying, we'll just give up on frustum culling from this
|
|
|
- // point down.
|
|
|
- view_frustum = (GeometricBoundingVolume *)NULL;
|
|
|
- guard_band = (GeometricBoundingVolume *)NULL;
|
|
|
-
|
|
|
- } else {
|
|
|
- CPT(TransformState) inv_transform =
|
|
|
- node_transform->invert_compose(TransformState::make_identity());
|
|
|
-
|
|
|
- if (view_frustum != (GeometricBoundingVolume *)NULL) {
|
|
|
- next_view_frustum = DCAST(GeometricBoundingVolume, view_frustum->make_copy());
|
|
|
- next_view_frustum->xform(inv_transform->get_mat());
|
|
|
- }
|
|
|
+ const RenderState *node_state = node->get_state();
|
|
|
+ if (node_state->has_decal()) {
|
|
|
+ start_decal(node, next_data);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ if (node->is_geom_node()) {
|
|
|
+ qpGeomNode *geom_node = DCAST(qpGeomNode, node);
|
|
|
|
|
|
- if (guard_band != (GeometricBoundingVolume *)NULL) {
|
|
|
- next_guard_band = DCAST(GeometricBoundingVolume, guard_band->make_copy());
|
|
|
- next_guard_band->xform(inv_transform->get_mat());
|
|
|
+ // Get all the Geoms, with no decalling.
|
|
|
+ int num_geoms = geom_node->get_num_geoms();
|
|
|
+ for (int i = 0; i < num_geoms; i++) {
|
|
|
+ CullableObject *object = new CullableObject(next_data, geom_node, i);
|
|
|
+ _cull_handler->record_object(object);
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- const RenderState *node_state = node->get_state();
|
|
|
- next_state = next_state->compose(node_state);
|
|
|
-
|
|
|
- const BillboardAttrib *billboard = node_state->get_billboard();
|
|
|
- if (billboard != (const BillboardAttrib *)NULL) {
|
|
|
- // Got to apply a billboard transform here.
|
|
|
- CPT(TransformState) billboard_transform =
|
|
|
- billboard->do_billboard(net_transform, _camera_transform);
|
|
|
- next_render_transform = next_render_transform->compose(billboard_transform);
|
|
|
- next_net_transform = next_net_transform->compose(billboard_transform);
|
|
|
|
|
|
- // We can't reliably cull within a billboard, because the geometry
|
|
|
- // might get rotated out of its bounding volume. So once we get
|
|
|
- // within a billboard, we consider it all visible.
|
|
|
- next_view_frustum = (GeometricBoundingVolume *)NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (node_state->has_decal()) {
|
|
|
- start_decal(node, next_render_transform, next_state);
|
|
|
+ // Now visit all the node's children.
|
|
|
+ PandaNode::Children cr = node->get_children();
|
|
|
+ int num_children = cr.get_num_children();
|
|
|
+ if (node->has_selective_visibility()) {
|
|
|
+ int i = node->get_first_visible_child();
|
|
|
+ while (i < num_children) {
|
|
|
+ r_traverse(cr.get_child(i), next_data);
|
|
|
+ i = node->get_next_visible_child(i);
|
|
|
+ }
|
|
|
|
|
|
- } else {
|
|
|
- if (node->is_geom_node()) {
|
|
|
- qpGeomNode *geom_node = DCAST(qpGeomNode, node);
|
|
|
-
|
|
|
- // Get all the Geoms, with no decalling.
|
|
|
- int num_geoms = geom_node->get_num_geoms();
|
|
|
- for (int i = 0; i < num_geoms; i++) {
|
|
|
- CullableObject *object = new CullableObject;
|
|
|
- object->_geom = geom_node->get_geom(i);
|
|
|
- object->_state = next_state->compose(geom_node->get_geom_state(i));
|
|
|
- object->_transform = next_render_transform;
|
|
|
- _cull_handler->record_object(object);
|
|
|
+ } else {
|
|
|
+ for (int i = 0; i < num_children; i++) {
|
|
|
+ r_traverse(cr.get_child(i), next_data);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // Now visit all the node's children.
|
|
|
- PandaNode::Children cr = node->get_children();
|
|
|
- int num_children = cr.get_num_children();
|
|
|
- for (int i = 0; i < num_children; i++) {
|
|
|
- r_traverse(cr.get_child(i), next_render_transform, next_net_transform,
|
|
|
- next_state, next_view_frustum, next_guard_band);
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -276,16 +198,10 @@ r_traverse(PandaNode *node,
|
|
|
// Access: Private
|
|
|
// Description: Collects a base node and all of the decals applied to
|
|
|
// it. This involves recursing below the base GeomNode
|
|
|
-// to find all the decal geoms; we don't bother to apply
|
|
|
-// any view-frustum culling at this point, and we don't
|
|
|
-// presently support billboards or LOD's within the
|
|
|
-// decals. Hard to justify the duplicate code this
|
|
|
-// would require.
|
|
|
+// to find all the decal geoms.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
void qpCullTraverser::
|
|
|
-start_decal(PandaNode *node,
|
|
|
- const TransformState *render_transform,
|
|
|
- const RenderState *state) {
|
|
|
+start_decal(PandaNode *node, const CullTraverserData &data) {
|
|
|
if (!node->is_geom_node()) {
|
|
|
pgraph_cat.error()
|
|
|
<< "DecalAttrib applied to " << *node << ", not a GeomNode.\n";
|
|
|
@@ -297,17 +213,22 @@ start_decal(PandaNode *node,
|
|
|
// CullableObject node, followed by all of the decal Geoms, in
|
|
|
// order.
|
|
|
|
|
|
- const TransformState *next_render_transform = render_transform;
|
|
|
- const RenderState *next_state = state;
|
|
|
-
|
|
|
// Since the CullableObject is a linked list which gets built in
|
|
|
// LIFO order, we start with the decals.
|
|
|
CullableObject *decals = (CullableObject *)NULL;
|
|
|
PandaNode::Children cr = node->get_children();
|
|
|
int num_children = cr.get_num_children();
|
|
|
- for (int i = num_children - 1; i >= 0; i--) {
|
|
|
- decals =
|
|
|
- r_get_decals(cr.get_child(i), next_render_transform, next_state, decals);
|
|
|
+ if (node->has_selective_visibility()) {
|
|
|
+ int i = node->get_first_visible_child();
|
|
|
+ while (i < num_children) {
|
|
|
+ decals = r_get_decals(cr.get_child(i), data, decals);
|
|
|
+ i = node->get_next_visible_child(i);
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ for (int i = num_children - 1; i >= 0; i--) {
|
|
|
+ decals = r_get_decals(cr.get_child(i), data, decals);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// Now create a new, empty CullableObject to separate the decals
|
|
|
@@ -317,14 +238,9 @@ start_decal(PandaNode *node,
|
|
|
// And now get the base Geoms, again in reverse order.
|
|
|
CullableObject *object = separator;
|
|
|
qpGeomNode *geom_node = DCAST(qpGeomNode, node);
|
|
|
-
|
|
|
- // Get all the Geoms, with no decalling.
|
|
|
int num_geoms = geom_node->get_num_geoms();
|
|
|
for (int i = num_geoms - 1; i >= 0; i--) {
|
|
|
- object = new CullableObject(object);
|
|
|
- object->_geom = geom_node->get_geom(i);
|
|
|
- object->_state = next_state->compose(geom_node->get_geom_state(i));
|
|
|
- object->_transform = next_render_transform;
|
|
|
+ object = new CullableObject(data, geom_node, i, object);
|
|
|
}
|
|
|
|
|
|
if (object != separator) {
|
|
|
@@ -345,60 +261,39 @@ start_decal(PandaNode *node,
|
|
|
// they were encountered in the scene graph).
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
CullableObject *qpCullTraverser::
|
|
|
-r_get_decals(PandaNode *node,
|
|
|
- const TransformState *render_transform,
|
|
|
- const RenderState *state,
|
|
|
+r_get_decals(PandaNode *node, const CullTraverserData &data,
|
|
|
CullableObject *decals) {
|
|
|
- const TransformState *node_transform = node->get_transform();
|
|
|
- const RenderState *node_state = node->get_state();
|
|
|
+ CullTraverserData next_data(data);
|
|
|
|
|
|
- CPT(TransformState) next_render_transform =
|
|
|
- render_transform->compose(node_transform);
|
|
|
- CPT(RenderState) next_state =
|
|
|
- state->compose(node_state);
|
|
|
+ if (next_data.is_in_view(node)) {
|
|
|
+ next_data.apply_transform_and_state(node);
|
|
|
|
|
|
- // First, visit all of the node's children.
|
|
|
- PandaNode::Children cr = node->get_children();
|
|
|
- int num_children = cr.get_num_children();
|
|
|
- for (int i = num_children - 1; i >= 0; i--) {
|
|
|
- decals =
|
|
|
- r_get_decals(cr.get_child(i), next_render_transform, next_state, decals);
|
|
|
- }
|
|
|
-
|
|
|
- // Now, tack on any geoms within the node.
|
|
|
- if (node->is_geom_node()) {
|
|
|
- qpGeomNode *geom_node = DCAST(qpGeomNode, node);
|
|
|
+ // First, visit all of the node's children.
|
|
|
+ PandaNode::Children cr = node->get_children();
|
|
|
+ int num_children = cr.get_num_children();
|
|
|
+ if (node->has_selective_visibility()) {
|
|
|
+ int i = node->get_first_visible_child();
|
|
|
+ while (i < num_children) {
|
|
|
+ decals = r_get_decals(cr.get_child(i), next_data, decals);
|
|
|
+ i = node->get_next_visible_child(i);
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ for (int i = num_children - 1; i >= 0; i--) {
|
|
|
+ decals = r_get_decals(cr.get_child(i), next_data, decals);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- int num_geoms = geom_node->get_num_geoms();
|
|
|
- for (int i = num_geoms - 1; i >= 0; i--) {
|
|
|
- decals = new CullableObject(decals);
|
|
|
- decals->_geom = geom_node->get_geom(i);
|
|
|
- decals->_state = next_state->compose(geom_node->get_geom_state(i));
|
|
|
- decals->_transform = next_render_transform;
|
|
|
+ // Now, tack on any geoms within the node.
|
|
|
+ if (node->is_geom_node()) {
|
|
|
+ qpGeomNode *geom_node = DCAST(qpGeomNode, node);
|
|
|
+
|
|
|
+ int num_geoms = geom_node->get_num_geoms();
|
|
|
+ for (int i = num_geoms - 1; i >= 0; i--) {
|
|
|
+ decals = new CullableObject(next_data, geom_node, i, decals);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return decals;
|
|
|
}
|
|
|
-
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-// Function: qpCullTraverser::get_fake_view_frustum_cull_effect
|
|
|
-// Access: Private, Static
|
|
|
-// Description: Returns a RenderState for rendering stuff in red
|
|
|
-// wireframe, strictly for the fake_view_frustum_cull
|
|
|
-// effect.
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
-CPT(RenderState) qpCullTraverser::
|
|
|
-get_fake_view_frustum_cull_effect() {
|
|
|
- // Once someone asks for this pointer, we hold its reference count
|
|
|
- // and never free it.
|
|
|
- static CPT(RenderState) effect = (const RenderState *)NULL;
|
|
|
- if (effect == (const RenderState *)NULL) {
|
|
|
- effect = RenderState::make
|
|
|
- (ColorAttrib::make_flat(Colorf(1.0f, 0.0f, 0.0f, 1.0f)),
|
|
|
- TextureAttrib::make_off(),
|
|
|
- 1000);
|
|
|
- }
|
|
|
- return effect;
|
|
|
-}
|
|
|
-
|