|
|
@@ -1,4 +1,4 @@
|
|
|
-// Filename: cullBinHierarchicalZBuffer.cxx
|
|
|
+// Filename: cullBinOcclusionTest.cxx
|
|
|
// Created by: drose (24Mar06)
|
|
|
//
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
@@ -16,7 +16,7 @@
|
|
|
//
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
-#include "cullBinHierarchicalZBuffer.h"
|
|
|
+#include "cullBinOcclusionTest.h"
|
|
|
#include "graphicsStateGuardianBase.h"
|
|
|
#include "geometricBoundingVolume.h"
|
|
|
#include "geomLines.h"
|
|
|
@@ -31,21 +31,47 @@
|
|
|
#include "config_cull.h"
|
|
|
#include "thread.h"
|
|
|
|
|
|
-PStatCollector CullBinHierarchicalZBuffer::_wait_occlusion_pcollector("Wait:Occlusion test");
|
|
|
-PStatCollector CullBinHierarchicalZBuffer::_geoms_occluded_pcollector("Geoms:Occluded");
|
|
|
+#include <algorithm>
|
|
|
+
|
|
|
+PStatCollector CullBinOcclusionTest::_wait_occlusion_pcollector("Draw:Wait occlusion");
|
|
|
+PStatCollector CullBinOcclusionTest::_occlusion_previous_pcollector("Occlusion test:Previously visible");
|
|
|
+PStatCollector CullBinOcclusionTest::_occlusion_passed_pcollector("Occlusion test:Visible");
|
|
|
+PStatCollector CullBinOcclusionTest::_occlusion_failed_pcollector("Occlusion test:Occluded");
|
|
|
+
|
|
|
+const LPoint3f CullBinOcclusionTest::_corner_points[8] = {
|
|
|
+ LPoint3f(-1.0f, -1.0f, -1.0f), // 0
|
|
|
+ LPoint3f(1.0f, -1.0f, -1.0f), // OC_x
|
|
|
+ LPoint3f(-1.0f, 1.0f, -1.0f), // OC_y
|
|
|
+ LPoint3f(1.0f, 1.0f, -1.0f), // OC_x | OC_y
|
|
|
+ LPoint3f(-1.0f, -1.0f, 1.0f), // OC_z
|
|
|
+ LPoint3f(1.0f, -1.0f, 1.0f), // OC_x | OC_z
|
|
|
+ LPoint3f(-1.0f, 1.0f, 1.0f), // OC_y | OC_z
|
|
|
+ LPoint3f(1.0f, 1.0f, 1.0f), // OC_x | OC_y | OC_z
|
|
|
+};
|
|
|
+
|
|
|
+PT(Geom) CullBinOcclusionTest::_octree_solid_test;
|
|
|
+PT(Geom) CullBinOcclusionTest::_octree_wireframe_viz;
|
|
|
+CPT(RenderState) CullBinOcclusionTest::_octree_solid_test_state;
|
|
|
+TypeHandle CullBinOcclusionTest::_type_handle;
|
|
|
+
|
|
|
+// This class is used to sort the corner index numbers into order from
|
|
|
+// closest to the camera to furthest from the camera.
|
|
|
+class SortCornersFrontToBack {
|
|
|
+public:
|
|
|
+ inline bool operator () (int a, int b) {
|
|
|
+ return _distances[a] < _distances[b];
|
|
|
+ }
|
|
|
+ float _distances[8];
|
|
|
+};
|
|
|
|
|
|
-PT(Geom) CullBinHierarchicalZBuffer::_octree_solid_test;
|
|
|
-PT(Geom) CullBinHierarchicalZBuffer::_octree_wireframe_viz;
|
|
|
-CPT(RenderState) CullBinHierarchicalZBuffer::_octree_solid_test_state;
|
|
|
-TypeHandle CullBinHierarchicalZBuffer::_type_handle;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::Destructor
|
|
|
+// Function: CullBinOcclusionTest::Destructor
|
|
|
// Access: Public, Virtual
|
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CullBinHierarchicalZBuffer::
|
|
|
-~CullBinHierarchicalZBuffer() {
|
|
|
+CullBinOcclusionTest::
|
|
|
+~CullBinOcclusionTest() {
|
|
|
ObjectPointers::iterator pi;
|
|
|
for (pi = _object_pointers.begin(); pi != _object_pointers.end(); ++pi) {
|
|
|
delete (*pi);
|
|
|
@@ -53,17 +79,17 @@ CullBinHierarchicalZBuffer::
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::make_bin
|
|
|
+// Function: CullBinOcclusionTest::make_bin
|
|
|
// Access: Public, Static
|
|
|
// Description: Factory constructor for passing to the CullBinManager.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CullBin *CullBinHierarchicalZBuffer::
|
|
|
+CullBin *CullBinOcclusionTest::
|
|
|
make_bin(const string &name, GraphicsStateGuardianBase *gsg) {
|
|
|
- return new CullBinHierarchicalZBuffer(name, gsg);
|
|
|
+ return new CullBinOcclusionTest(name, gsg);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::make_next
|
|
|
+// Function: CullBinOcclusionTest::make_next
|
|
|
// Access: Public, Virtual
|
|
|
// Description: Returns a newly-allocated CullBin object that
|
|
|
// contains a copy of just the subset of the data from
|
|
|
@@ -75,18 +101,21 @@ make_bin(const string &name, GraphicsStateGuardianBase *gsg) {
|
|
|
// return NULL (which is the default behavior of this
|
|
|
// method).
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-PT(CullBin) CullBinHierarchicalZBuffer::
|
|
|
+PT(CullBin) CullBinOcclusionTest::
|
|
|
make_next() const {
|
|
|
- return (CullBin *)NULL;
|
|
|
+ // We use the copy constructor, which creates an empty CullBin
|
|
|
+ // object, but also copies the _prev_draw pointer into it, so that
|
|
|
+ // there will be inter-frame continuity.
|
|
|
+ return new CullBinOcclusionTest(*this);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::add_object
|
|
|
+// Function: CullBinOcclusionTest::add_object
|
|
|
// Access: Public, Virtual
|
|
|
// Description: Adds a geom, along with its associated state, to
|
|
|
// the bin for rendering.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::
|
|
|
+void CullBinOcclusionTest::
|
|
|
add_object(CullableObject *object) {
|
|
|
// Determine the world-space bounding sphere for the object.
|
|
|
CPT(BoundingVolume) volume = object->_geom->get_bounds();
|
|
|
@@ -94,6 +123,8 @@ add_object(CullableObject *object) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ ++_num_objects;
|
|
|
+
|
|
|
PT(BoundingSphere) sphere;
|
|
|
|
|
|
if (volume->is_exact_type(BoundingSphere::get_class_type())) {
|
|
|
@@ -111,7 +142,7 @@ add_object(CullableObject *object) {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::finish_cull
|
|
|
+// Function: CullBinOcclusionTest::finish_cull
|
|
|
// Access: Public
|
|
|
// Description: Called after all the geoms have been added, this
|
|
|
// indicates that the cull process is finished for this
|
|
|
@@ -119,8 +150,8 @@ add_object(CullableObject *object) {
|
|
|
// post-processing (like sorting) before moving on to
|
|
|
// draw.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::
|
|
|
-finish_cull() {
|
|
|
+void CullBinOcclusionTest::
|
|
|
+finish_cull(SceneSetup *scene_setup) {
|
|
|
PStatTimer timer(_cull_this_pcollector);
|
|
|
|
|
|
// Now we have a loose list of objects that are to be rendered.
|
|
|
@@ -133,82 +164,146 @@ finish_cull() {
|
|
|
// starting from the previous frame's octree.
|
|
|
_root.make_initial_bounds();
|
|
|
_root.group_objects();
|
|
|
+
|
|
|
+ // Figure out the best front-to-back order of the corners of each
|
|
|
+ // octree node, based on the current viewing orientation.
|
|
|
+ CPT(TransformState) world_transform = scene_setup->get_world_transform();
|
|
|
+ const LMatrix4f &world_mat = world_transform->get_mat();
|
|
|
+
|
|
|
+ // A temporary object to record distances, and manage the sorting.
|
|
|
+ SortCornersFrontToBack sorter;
|
|
|
+
|
|
|
+ for (int i = 0; i < 8; ++i) {
|
|
|
+ _corners_front_to_back[i] = i;
|
|
|
+
|
|
|
+ LPoint3f p = _corner_points[i] * world_mat;
|
|
|
+ sorter._distances[i] = _gsg->compute_distance_to(p);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now sort, using the STL sort function.
|
|
|
+ ::sort(&_corners_front_to_back[0], &_corners_front_to_back[8], sorter);
|
|
|
+
|
|
|
+ // Finally, use that information to compute the distance of each
|
|
|
+ // octree node fom the camera plane.
|
|
|
+ _root.compute_distance(world_mat, *this);
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::draw
|
|
|
-// Access: Public
|
|
|
+// Function: CullBinOcclusionTest::draw
|
|
|
+// Access: Public, Virtual
|
|
|
// Description: Draws all the geoms in the bin, in the appropriate
|
|
|
// order.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::
|
|
|
+void CullBinOcclusionTest::
|
|
|
draw() {
|
|
|
PStatTimer timer(_draw_this_pcollector);
|
|
|
- _root.draw(*this);
|
|
|
+
|
|
|
+ // We'll want to know the near plane distance.
|
|
|
+ _near_distance = _gsg->get_scene()->get_lens()->get_near();
|
|
|
+
|
|
|
+ // First, draw any objects that were visible last frame.
|
|
|
+ int num_drawn_previous;
|
|
|
+ {
|
|
|
+ MutexHolder holder(_prev_draw->_visible_lock);
|
|
|
+ num_drawn_previous = _root.draw_previous(*this);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cull_cat.is_spam()) {
|
|
|
+ cull_cat.spam()
|
|
|
+ << "Drew " << num_drawn_previous << " objects.\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ // Now draw the objects that may or may not remain.
|
|
|
+ int num_drawn;
|
|
|
+ num_drawn = _root.draw(*this);
|
|
|
+ if (show_octree) {
|
|
|
+ _root.draw_wireframe(*this);
|
|
|
+ }
|
|
|
|
|
|
while (!_pending_nodes.empty()) {
|
|
|
PendingNode &pending = _pending_nodes.front();
|
|
|
- bool is_occluded;
|
|
|
+ int num_fragments;
|
|
|
if (!pending._query->is_answer_ready()) {
|
|
|
// The answer isn't ready yet. We have to wait.
|
|
|
PStatTimer timer(_wait_occlusion_pcollector);
|
|
|
- is_occluded = (pending._query->get_num_fragments() == 0);
|
|
|
+ num_fragments = pending._query->get_num_fragments();
|
|
|
} else {
|
|
|
// The answer is ready right now. There will be no waiting.
|
|
|
- is_occluded = (pending._query->get_num_fragments() == 0);
|
|
|
+ num_fragments = pending._query->get_num_fragments();
|
|
|
}
|
|
|
- if (!is_occluded) {
|
|
|
+ if (cull_cat.is_spam()) {
|
|
|
+ cull_cat.spam()
|
|
|
+ << "OctreeNode " << *pending._octree_node
|
|
|
+ << " shows " << num_fragments << " fragments\n";
|
|
|
+ }
|
|
|
+ if (num_fragments != 0) {
|
|
|
// The octree cell is at least partially visible. Draw it, and
|
|
|
// continue recursion.
|
|
|
- pending._octree_node->draw(*this);
|
|
|
+ num_drawn += pending._octree_node->draw(*this);
|
|
|
if (show_octree) {
|
|
|
pending._octree_node->draw_wireframe(*this);
|
|
|
}
|
|
|
- } else {
|
|
|
- // The octree cell is completely occluded. Don't draw any of
|
|
|
- // it, and don't recurse into it.
|
|
|
- _geoms_occluded_pcollector.add_level(pending._octree_node->get_total_num_objects());
|
|
|
}
|
|
|
_pending_nodes.pop_front();
|
|
|
}
|
|
|
+
|
|
|
+ _occlusion_previous_pcollector.add_level(num_drawn_previous);
|
|
|
+ _occlusion_passed_pcollector.add_level(num_drawn);
|
|
|
+ _occlusion_failed_pcollector.add_level(_num_objects - (num_drawn_previous + num_drawn));
|
|
|
+
|
|
|
+ // Now, store a list of the objects within OctreeNodes that passed
|
|
|
+ // the occlusion test this frame, so we can ensure that they are
|
|
|
+ // drawn first next frame.
|
|
|
+ VisibleGeoms visible_geoms;
|
|
|
+ _root.record_visible_geoms(visible_geoms);
|
|
|
+ visible_geoms.sort();
|
|
|
+
|
|
|
+ {
|
|
|
+ MutexHolder holder(_prev_draw->_visible_lock);
|
|
|
+ _prev_draw->_visible_geoms.swap(visible_geoms);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::Constructor
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::Constructor
|
|
|
// Access: Public
|
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
+CullBinOcclusionTest::OctreeNode::
|
|
|
OctreeNode() {
|
|
|
- _total_num_objects = 0;
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
_corners[i] = (OctreeNode *)NULL;
|
|
|
}
|
|
|
+
|
|
|
+ _is_visible = false;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::Constructor
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::Constructor
|
|
|
// Access: Public
|
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
+CullBinOcclusionTest::OctreeNode::
|
|
|
OctreeNode(float mid_x, float mid_y, float mid_z, float half_side) :
|
|
|
_mid(mid_x, mid_y, mid_z),
|
|
|
_half_side(half_side)
|
|
|
{
|
|
|
- _total_num_objects = 0;
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
_corners[i] = (OctreeNode *)NULL;
|
|
|
}
|
|
|
+
|
|
|
+ // OctreeNodes are considered occluded until they pass the occlusion
|
|
|
+ // test.
|
|
|
+ _is_visible = false;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::Destructor
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::Destructor
|
|
|
// Access: Public
|
|
|
// Description:
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
+CullBinOcclusionTest::OctreeNode::
|
|
|
~OctreeNode() {
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
if (_corners[i] != (OctreeNode *)NULL) {
|
|
|
@@ -218,12 +313,12 @@ CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::make_initial_bounds
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::make_initial_bounds
|
|
|
// Access: Public
|
|
|
// Description: Determines the minmax bounding volume of the root
|
|
|
// OctreeNode, based on the objects it contains.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
make_initial_bounds() {
|
|
|
if (_objects.empty()) {
|
|
|
return;
|
|
|
@@ -255,16 +350,15 @@ make_initial_bounds() {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::group_objects
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::group_objects
|
|
|
// Access: Public
|
|
|
// Description: Recursively groups the objects assigned to this node
|
|
|
// into smaller octree nodes, as appropriate.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
group_objects() {
|
|
|
if ((int)_objects.size() <= max_objects_per_octree_node) {
|
|
|
// No need to do any more subdividing.
|
|
|
- _total_num_objects += (int)_objects.size();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -353,14 +447,35 @@ group_objects() {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::occlusion_test
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::compute_distance
|
|
|
+// Access: Public
|
|
|
+// Description: Recursively computes the _distance member of each
|
|
|
+// octree node, as the linear distance from the camera
|
|
|
+// plane to the nearest corner of the octree node.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
+compute_distance(const LMatrix4f &world_mat,
|
|
|
+ CullBinOcclusionTest &bin) {
|
|
|
+ int index = bin._corners_front_to_back[0];
|
|
|
+ LPoint3f p = get_corner_point(index) * world_mat;
|
|
|
+ _distance = bin._gsg->compute_distance_to(p);
|
|
|
+
|
|
|
+ for (int i = 0; i < 8; ++i) {
|
|
|
+ if (_corners[i] != (OctreeNode *)NULL) {
|
|
|
+ _corners[i]->compute_distance(world_mat, bin);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::occlusion_test
|
|
|
// Access: Public
|
|
|
// Description: Tests the octree node for visibility by rendering the
|
|
|
// octree cube, invisibly, with a query. Returns the
|
|
|
// occlusion query object representing this test.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-PT(OcclusionQueryContext) CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
-occlusion_test(CullBinHierarchicalZBuffer &bin) {
|
|
|
+PT(OcclusionQueryContext) CullBinOcclusionTest::OctreeNode::
|
|
|
+occlusion_test(CullBinOcclusionTest &bin) {
|
|
|
// Draw the bounding volume for visualization. This is
|
|
|
// complicated because we're doing this at such a low level, here
|
|
|
// in the middle of the draw task--we've already completed the
|
|
|
@@ -389,14 +504,63 @@ occlusion_test(CullBinHierarchicalZBuffer &bin) {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::draw
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::draw_previous
|
|
|
+// Access: Public
|
|
|
+// Description: Recursively draws only those objects which are known
|
|
|
+// to have been drawn last frame. Returns the number of
|
|
|
+// objects drawn.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+int CullBinOcclusionTest::OctreeNode::
|
|
|
+draw_previous(CullBinOcclusionTest &bin) {
|
|
|
+ int num_drawn = 0;
|
|
|
+
|
|
|
+ if (!_objects.empty()) {
|
|
|
+ Objects::const_iterator oi;
|
|
|
+ for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
|
|
|
+ CullableObject *object = (*oi)._object;
|
|
|
+ if (!object->_already_drawn) {
|
|
|
+ VisibleGeom vg(object->_geom, object->_net_transform);
|
|
|
+ if (bin._prev_draw->_visible_geoms.find(vg) != bin._prev_draw->_visible_geoms.end()) {
|
|
|
+ // This object is visible.
|
|
|
+ CullHandler::draw(object, bin._gsg);
|
|
|
+ object->_already_drawn = true;
|
|
|
+ ++num_drawn;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < 8; ++i) {
|
|
|
+ // Render the child octree nodes in order from nearest to
|
|
|
+ // farthest.
|
|
|
+ int index = bin._corners_front_to_back[i];
|
|
|
+ if (_corners[index] != (OctreeNode *)NULL) {
|
|
|
+ num_drawn += _corners[index]->draw_previous(bin);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return num_drawn;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::draw
|
|
|
// Access: Public
|
|
|
// Description: Draws all of the objects in this node, and
|
|
|
// recursively performs occlusion tests on all of the
|
|
|
-// nested nodes.
|
|
|
+// nested nodes. Returns the number of objects drawn.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
-draw(CullBinHierarchicalZBuffer &bin) {
|
|
|
+int CullBinOcclusionTest::OctreeNode::
|
|
|
+draw(CullBinOcclusionTest &bin) {
|
|
|
+ // If the node is being drawn, it must have passed the occlusion
|
|
|
+ // test. Flag it as such.
|
|
|
+ _is_visible = true;
|
|
|
+ if (cull_cat.is_spam()) {
|
|
|
+ cull_cat.spam()
|
|
|
+ << "Drawing OctreeNode " << this << "\n";
|
|
|
+ }
|
|
|
+
|
|
|
+ int num_drawn = 0;
|
|
|
+
|
|
|
if (!_objects.empty()) {
|
|
|
// Now draw the objects within the octree node.
|
|
|
Objects::const_iterator oi;
|
|
|
@@ -405,28 +569,54 @@ draw(CullBinHierarchicalZBuffer &bin) {
|
|
|
if (!object->_already_drawn) {
|
|
|
CullHandler::draw(object, bin._gsg);
|
|
|
object->_already_drawn = true;
|
|
|
+ ++num_drawn;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // Now recurse on each child node.
|
|
|
for (int i = 0; i < 8; ++i) {
|
|
|
- if (_corners[i] != (OctreeNode *)NULL) {
|
|
|
- PendingNode pending;
|
|
|
- pending._octree_node = _corners[i];
|
|
|
- pending._query = _corners[i]->occlusion_test(bin);
|
|
|
- bin._pending_nodes.push_back(pending);
|
|
|
+ // Make sure we render the child octree nodes in order from
|
|
|
+ // nearest to farthest.
|
|
|
+ int index = bin._corners_front_to_back[i];
|
|
|
+ if (_corners[index] != (OctreeNode *)NULL) {
|
|
|
+ if (_corners[index]->_distance < bin._near_distance) {
|
|
|
+ // If a corner of the cube pokes through the near plane, go
|
|
|
+ // ahead and render the whole cube without performing an
|
|
|
+ // occlusion test. The occlusion test would be invalid (since
|
|
|
+ // some or all of the cube would be clipped), but it's not
|
|
|
+ // likely that anything will be occluding something so close
|
|
|
+ // to the camera anyway.
|
|
|
+ _corners[index]->draw(bin);
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // Otherwise, if the entire cube is in front of the near
|
|
|
+ // plane, perform an occlusion test by rendering out the
|
|
|
+ // (invisible) octree cube and then see if any pixels make it
|
|
|
+ // through the depth test.
|
|
|
+ PendingNode pending;
|
|
|
+ pending._octree_node = _corners[index];
|
|
|
+ pending._query = _corners[index]->occlusion_test(bin);
|
|
|
+
|
|
|
+ // We push it onto the list of nodes that are awaiting
|
|
|
+ // feedback from the graphics pipe. This way we can go work
|
|
|
+ // on another octree node while we're waiting for this one.
|
|
|
+ bin._pending_nodes.push_back(pending);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ return num_drawn;
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::draw_wireframe
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::draw_wireframe
|
|
|
// Access: Public
|
|
|
// Description: Draws a wireframe representation of the octree cube,
|
|
|
// for debugging and visualization purposes.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
-draw_wireframe(CullBinHierarchicalZBuffer &bin) {
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
+draw_wireframe(CullBinOcclusionTest &bin) {
|
|
|
// As above, this is complicated because we're doing this at such a
|
|
|
// low level.
|
|
|
CPT(TransformState) net_transform = TransformState::make_pos_hpr_scale
|
|
|
@@ -447,14 +637,51 @@ draw_wireframe(CullBinHierarchicalZBuffer &bin) {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::get_octree_solid_test
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::record_visible_geoms
|
|
|
+// Access: Public
|
|
|
+// Description: Records any Geoms associated with OctreeNodes that
|
|
|
+// passed the occlusion test for next frame, to improve
|
|
|
+// temporal coherence.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
+record_visible_geoms(CullBinOcclusionTest::VisibleGeoms &visible_geoms) {
|
|
|
+ if (_is_visible) {
|
|
|
+ Objects::const_iterator oi;
|
|
|
+ for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
|
|
|
+ CullableObject *object = (*oi)._object;
|
|
|
+ nassertv(object->_already_drawn);
|
|
|
+ VisibleGeom vg(object->_geom, object->_net_transform);
|
|
|
+ visible_geoms.push_back(vg);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (int i = 0; i < 8; ++i) {
|
|
|
+ if (_corners[i] != (OctreeNode *)NULL) {
|
|
|
+ _corners[i]->record_visible_geoms(visible_geoms);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::output
|
|
|
+// Access: Public
|
|
|
+// Description:
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
+output(ostream &out) const {
|
|
|
+ out << "OctreeNode " << _mid << ", " << _half_side << ", dist "
|
|
|
+ << _distance;
|
|
|
+}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: CullBinOcclusionTest::get_octree_solid_test
|
|
|
// Access: Private, Static
|
|
|
// Description: Returns a Geom that may be used to render the solid
|
|
|
// faces of octree cube, presumably invisibly. This
|
|
|
// returns a cube over the range (-1, -1, -1) - (1, 1,
|
|
|
// 1).
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CPT(Geom) CullBinHierarchicalZBuffer::
|
|
|
+CPT(Geom) CullBinOcclusionTest::
|
|
|
get_octree_solid_test() {
|
|
|
if (_octree_solid_test == (Geom *)NULL) {
|
|
|
CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3();
|
|
|
@@ -491,13 +718,13 @@ get_octree_solid_test() {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::get_octree_wireframe_viz
|
|
|
+// Function: CullBinOcclusionTest::get_octree_wireframe_viz
|
|
|
// Access: Private, Static
|
|
|
// Description: Returns a Geom that may be used to render an
|
|
|
// OctreeNode in wireframe. This actually draws a
|
|
|
// wireframe cube in the range (-1, -1, -1) - (1, 1, 1).
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CPT(Geom) CullBinHierarchicalZBuffer::
|
|
|
+CPT(Geom) CullBinOcclusionTest::
|
|
|
get_octree_wireframe_viz() {
|
|
|
if (_octree_wireframe_viz == (Geom *)NULL) {
|
|
|
CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
|
|
|
@@ -535,12 +762,12 @@ get_octree_wireframe_viz() {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::get_octree_solid_test_state
|
|
|
+// Function: CullBinOcclusionTest::get_octree_solid_test_state
|
|
|
// Access: Private, Static
|
|
|
// Description: Returns the RenderState appropriate to rendering the
|
|
|
// octree test invisibly.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-CPT(RenderState) CullBinHierarchicalZBuffer::
|
|
|
+CPT(RenderState) CullBinOcclusionTest::
|
|
|
get_octree_solid_test_state() {
|
|
|
if (_octree_solid_test_state == (RenderState *)NULL) {
|
|
|
_octree_solid_test_state = RenderState::make
|
|
|
@@ -553,14 +780,14 @@ get_octree_solid_test_state() {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::multi_assign
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::multi_assign
|
|
|
// Access: Public
|
|
|
// Description: The object intersects a center plane, but is too
|
|
|
// small to justify keeping within this big node.
|
|
|
// Duplicate it into the sub-nodes.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-void CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
-multi_assign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
+multi_assign(const CullBinOcclusionTest::ObjectData &object_data) {
|
|
|
const LPoint3f &c = object_data._bounds->get_center();
|
|
|
float r = object_data._bounds->get_radius();
|
|
|
|
|
|
@@ -722,11 +949,11 @@ multi_assign(const CullBinHierarchicalZBuffer::ObjectData &object_data) {
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-// Function: CullBinHierarchicalZBuffer::OctreeNode::make_corner
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::make_corner
|
|
|
// Access: Private
|
|
|
// Description: Makes a new octree node for the indicated corner.
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
-INLINE void CullBinHierarchicalZBuffer::OctreeNode::
|
|
|
+void CullBinOcclusionTest::OctreeNode::
|
|
|
make_corner(int index) {
|
|
|
nassertv(_corners[index] == NULL);
|
|
|
|
|
|
@@ -778,3 +1005,49 @@ make_corner(int index) {
|
|
|
nassertv(node != (OctreeNode *)NULL);
|
|
|
_corners[index] = node;
|
|
|
}
|
|
|
+
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+// Function: CullBinOcclusionTest::OctreeNode::get_corner_point
|
|
|
+// Access: Private
|
|
|
+// Description: Returns the 3-d point, in world space, of the
|
|
|
+// indicated corner.
|
|
|
+////////////////////////////////////////////////////////////////////
|
|
|
+LPoint3f CullBinOcclusionTest::OctreeNode::
|
|
|
+get_corner_point(int index) {
|
|
|
+ switch (index) {
|
|
|
+ case 0:
|
|
|
+ // -X, -Y, -Z
|
|
|
+ return LPoint3f(_mid[0] - _half_side, _mid[1] - _half_side, _mid[2] - _half_side);
|
|
|
+
|
|
|
+ case OC_x:
|
|
|
+ // +X, -Y, -Z
|
|
|
+ return LPoint3f(_mid[0] + _half_side, _mid[1] - _half_side, _mid[2] - _half_side);
|
|
|
+
|
|
|
+ case OC_y:
|
|
|
+ // -X, +Y, -Z
|
|
|
+ return LPoint3f(_mid[0] - _half_side, _mid[1] + _half_side, _mid[2] - _half_side);
|
|
|
+
|
|
|
+ case OC_x | OC_y:
|
|
|
+ // +X, +Y, -Z
|
|
|
+ return LPoint3f(_mid[0] + _half_side, _mid[1] + _half_side, _mid[2] - _half_side);
|
|
|
+
|
|
|
+ case OC_z:
|
|
|
+ // -X, -Y, +Z
|
|
|
+ return LPoint3f(_mid[0] - _half_side, _mid[1] - _half_side, _mid[2] + _half_side);
|
|
|
+
|
|
|
+ case OC_x | OC_z:
|
|
|
+ // +X, -Y, +Z
|
|
|
+ return LPoint3f(_mid[0] + _half_side, _mid[1] - _half_side, _mid[2] + _half_side);
|
|
|
+
|
|
|
+ case OC_y | OC_z:
|
|
|
+ // -X, +Y, +Z
|
|
|
+ return LPoint3f(_mid[0] - _half_side, _mid[1] + _half_side, _mid[2] + _half_side);
|
|
|
+
|
|
|
+ case OC_x | OC_y | OC_z:
|
|
|
+ // +X, +Y, +Z
|
|
|
+ return LPoint3f(_mid[0] + _half_side, _mid[1] + _half_side, _mid[2] + _half_side);
|
|
|
+ }
|
|
|
+
|
|
|
+ nassertr(false, LPoint3f::zero());
|
|
|
+ return LPoint3f::zero();
|
|
|
+}
|