|
@@ -1,1057 +0,0 @@
|
|
|
-// Filename: cullBinOcclusionTest.cxx
|
|
|
|
|
-// Created by: drose (24Mar06)
|
|
|
|
|
-//
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-//
|
|
|
|
|
-// PANDA 3D SOFTWARE
|
|
|
|
|
-// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
|
|
|
-//
|
|
|
|
|
-// All use of this software is subject to the terms of the Panda 3d
|
|
|
|
|
-// Software license. You should have received a copy of this license
|
|
|
|
|
-// along with this source code; you will also find a current copy of
|
|
|
|
|
-// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
|
|
|
-//
|
|
|
|
|
-// To contact the maintainers of this program write to
|
|
|
|
|
-// [email protected] .
|
|
|
|
|
-//
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-
|
|
|
|
|
-#include "cullBinOcclusionTest.h"
|
|
|
|
|
-#include "graphicsStateGuardianBase.h"
|
|
|
|
|
-#include "geometricBoundingVolume.h"
|
|
|
|
|
-#include "geomLines.h"
|
|
|
|
|
-#include "geomTriangles.h"
|
|
|
|
|
-#include "geomVertexWriter.h"
|
|
|
|
|
-#include "depthWriteAttrib.h"
|
|
|
|
|
-#include "depthTestAttrib.h"
|
|
|
|
|
-#include "colorWriteAttrib.h"
|
|
|
|
|
-#include "cullableObject.h"
|
|
|
|
|
-#include "cullHandler.h"
|
|
|
|
|
-#include "pStatTimer.h"
|
|
|
|
|
-#include "config_cull.h"
|
|
|
|
|
-#include "thread.h"
|
|
|
|
|
-
|
|
|
|
|
-#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];
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: CullBinOcclusionTest::Destructor
|
|
|
|
|
-// Access: Public, Virtual
|
|
|
|
|
-// Description:
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-CullBinOcclusionTest::
|
|
|
|
|
-~CullBinOcclusionTest() {
|
|
|
|
|
- ObjectPointers::iterator pi;
|
|
|
|
|
- for (pi = _object_pointers.begin(); pi != _object_pointers.end(); ++pi) {
|
|
|
|
|
- delete (*pi);
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: CullBinOcclusionTest::make_bin
|
|
|
|
|
-// Access: Public, Static
|
|
|
|
|
-// Description: Factory constructor for passing to the CullBinManager.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-CullBin *CullBinOcclusionTest::
|
|
|
|
|
-make_bin(const string &name, GraphicsStateGuardianBase *gsg,
|
|
|
|
|
- const PStatCollector &draw_region_pcollector) {
|
|
|
|
|
- return new CullBinOcclusionTest(name, gsg, draw_region_pcollector);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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
|
|
|
|
|
-// this CullBin object that is worth keeping around
|
|
|
|
|
-// for next frame.
|
|
|
|
|
-//
|
|
|
|
|
-// If a particular CullBin object has no data worth
|
|
|
|
|
-// preserving till next frame, it is acceptable to
|
|
|
|
|
-// return NULL (which is the default behavior of this
|
|
|
|
|
-// method).
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-PT(CullBin) CullBinOcclusionTest::
|
|
|
|
|
-make_next() const {
|
|
|
|
|
- // 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: CullBinOcclusionTest::add_object
|
|
|
|
|
-// Access: Public, Virtual
|
|
|
|
|
-// Description: Adds a geom, along with its associated state, to
|
|
|
|
|
-// the bin for rendering.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void CullBinOcclusionTest::
|
|
|
|
|
-add_object(CullableObject *object, Thread *current_thread) {
|
|
|
|
|
- // Determine the world-space bounding sphere for the object.
|
|
|
|
|
- CPT(BoundingVolume) volume = object->_geom->get_bounds();
|
|
|
|
|
- if (volume->is_empty()) {
|
|
|
|
|
- delete object;
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- ++_num_objects;
|
|
|
|
|
-
|
|
|
|
|
- PT(BoundingSphere) sphere;
|
|
|
|
|
-
|
|
|
|
|
- if (volume->is_exact_type(BoundingSphere::get_class_type())) {
|
|
|
|
|
- sphere = DCAST(BoundingSphere, volume->make_copy());
|
|
|
|
|
- } else {
|
|
|
|
|
- const GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, volume);
|
|
|
|
|
- PT(BoundingSphere) sphere = new BoundingSphere;
|
|
|
|
|
- sphere->around(&gbv, &gbv + 1);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- object->_already_drawn = false;
|
|
|
|
|
- sphere->xform(object->_net_transform->get_mat());
|
|
|
|
|
- _root.initial_assign(ObjectData(object, sphere));
|
|
|
|
|
- _object_pointers.push_back(object);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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
|
|
|
|
|
-// frame and gives the bins a chance to do any
|
|
|
|
|
-// post-processing (like sorting) before moving on to
|
|
|
|
|
-// draw.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void CullBinOcclusionTest::
|
|
|
|
|
-finish_cull(SceneSetup *scene_setup, Thread *current_thread) {
|
|
|
|
|
- PStatTimer timer(_cull_this_pcollector, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- // Now we have a loose list of objects that are to be rendered.
|
|
|
|
|
- // We'd rather have them in an octree, which has much better
|
|
|
|
|
- // grouping properties for the purpose of this algorithm.
|
|
|
|
|
-
|
|
|
|
|
- // For now, we'll just build an octree here at runtime, a new one
|
|
|
|
|
- // fresh for each frame. Maybe it won't be *too* bad. But later,
|
|
|
|
|
- // we can optimize this to take advantage of temporal coherence by
|
|
|
|
|
- // 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: CullBinOcclusionTest::draw
|
|
|
|
|
-// Access: Public, Virtual
|
|
|
|
|
-// Description: Draws all the geoms in the bin, in the appropriate
|
|
|
|
|
-// order.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void CullBinOcclusionTest::
|
|
|
|
|
-draw(Thread *current_thread) {
|
|
|
|
|
- PStatTimer timer(_draw_this_pcollector, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- // 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, current_thread);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- 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, current_thread);
|
|
|
|
|
- if (show_octree) {
|
|
|
|
|
- _root.draw_wireframe(*this, current_thread);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- while (!_pending_nodes.empty()) {
|
|
|
|
|
- PendingNode &pending = _pending_nodes.front();
|
|
|
|
|
- int num_fragments;
|
|
|
|
|
- if (!pending._query->is_answer_ready()) {
|
|
|
|
|
- // The answer isn't ready yet. We have to wait.
|
|
|
|
|
- PStatTimer timer(_wait_occlusion_pcollector);
|
|
|
|
|
- num_fragments = pending._query->get_num_fragments();
|
|
|
|
|
- } else {
|
|
|
|
|
- // The answer is ready right now. There will be no waiting.
|
|
|
|
|
- num_fragments = pending._query->get_num_fragments();
|
|
|
|
|
- }
|
|
|
|
|
- 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.
|
|
|
|
|
- num_drawn += pending._octree_node->draw(*this, current_thread);
|
|
|
|
|
- if (show_octree) {
|
|
|
|
|
- pending._octree_node->draw_wireframe(*this, current_thread);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- _pending_nodes.pop_front();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _occlusion_previous_pcollector.add_level_now(num_drawn_previous);
|
|
|
|
|
- _occlusion_passed_pcollector.add_level_now(num_drawn);
|
|
|
|
|
- _occlusion_failed_pcollector.add_level_now(_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: CullBinOcclusionTest::OctreeNode::Constructor
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description:
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-OctreeNode() {
|
|
|
|
|
- for (int i = 0; i < 8; ++i) {
|
|
|
|
|
- _corners[i] = (OctreeNode *)NULL;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- _is_visible = false;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: CullBinOcclusionTest::OctreeNode::Constructor
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description:
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-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)
|
|
|
|
|
-{
|
|
|
|
|
- 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: CullBinOcclusionTest::OctreeNode::Destructor
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description:
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-~OctreeNode() {
|
|
|
|
|
- for (int i = 0; i < 8; ++i) {
|
|
|
|
|
- if (_corners[i] != (OctreeNode *)NULL) {
|
|
|
|
|
- delete _corners[i];
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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 CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-make_initial_bounds() {
|
|
|
|
|
- if (_objects.empty()) {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- LPoint3f scene_min = _objects[0]._bounds->get_center();
|
|
|
|
|
- LPoint3f scene_max = _objects[0]._bounds->get_center();
|
|
|
|
|
-
|
|
|
|
|
- Objects::const_iterator oi;
|
|
|
|
|
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
|
|
|
|
|
- LPoint3f object_min = (*oi)._bounds->get_min();
|
|
|
|
|
- LPoint3f object_max = (*oi)._bounds->get_max();
|
|
|
|
|
- scene_min[0] = min(scene_min[0], object_min[0]);
|
|
|
|
|
- scene_min[1] = min(scene_min[1], object_min[1]);
|
|
|
|
|
- scene_min[2] = min(scene_min[2], object_min[2]);
|
|
|
|
|
- scene_max[0] = max(scene_max[0], object_max[0]);
|
|
|
|
|
- scene_max[1] = max(scene_max[1], object_max[1]);
|
|
|
|
|
- scene_max[2] = max(scene_max[2], object_max[2]);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- float side = max(max(scene_max[0] - scene_min[0],
|
|
|
|
|
- scene_max[1] - scene_min[1]),
|
|
|
|
|
- scene_max[2] - scene_min[2]);
|
|
|
|
|
-
|
|
|
|
|
- _mid.set((scene_min[0] + scene_max[0]) * 0.5f,
|
|
|
|
|
- (scene_min[1] + scene_max[1]) * 0.5f,
|
|
|
|
|
- (scene_min[2] + scene_max[2]) * 0.5f);
|
|
|
|
|
- _half_side = side * 0.5f;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: CullBinOcclusionTest::OctreeNode::group_objects
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description: Recursively groups the objects assigned to this node
|
|
|
|
|
-// into smaller octree nodes, as appropriate.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-group_objects() {
|
|
|
|
|
- if ((int)_objects.size() <= max_objects_per_octree_node) {
|
|
|
|
|
- // No need to do any more subdividing.
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Assign all objects to one or more corners.
|
|
|
|
|
-
|
|
|
|
|
- Objects old_objects;
|
|
|
|
|
- old_objects.swap(_objects);
|
|
|
|
|
-
|
|
|
|
|
- Objects::const_iterator oi;
|
|
|
|
|
- for (oi = old_objects.begin(); oi != old_objects.end(); ++oi) {
|
|
|
|
|
- const ObjectData &object_data = (*oi);
|
|
|
|
|
- const LPoint3f &c = object_data._bounds->get_center();
|
|
|
|
|
- float r = object_data._bounds->get_radius();
|
|
|
|
|
-
|
|
|
|
|
- if (c[0] + r <= _mid[0]) {
|
|
|
|
|
- // -X
|
|
|
|
|
- if (c[1] + r <= _mid[1]) {
|
|
|
|
|
- // -X, -Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // -X, -Y, -Z
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // -X, -Y, +Z
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // -X, -Y, 0
|
|
|
|
|
- reassign(object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (c[1] - r >= _mid[1]) {
|
|
|
|
|
- // -X, +Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // -X, +Y, -Z
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // -X, +Y, +Z
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // -X, +Y, 0
|
|
|
|
|
- reassign(object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // -X, 0
|
|
|
|
|
- reassign(object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (c[0] - r >= _mid[0]) {
|
|
|
|
|
- // +X
|
|
|
|
|
- if (c[1] + r <= _mid[1]) {
|
|
|
|
|
- // +X, -Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // +X, -Y, -Z
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // +X, -Y, +Z
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // +X, -Y, 0
|
|
|
|
|
- reassign(object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (c[1] - r >= _mid[1]) {
|
|
|
|
|
- // +X, +Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // +X, +Y, -Z
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // +X, +Y, +Z
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // +X, +Y, 0
|
|
|
|
|
- reassign(object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // +X, 0
|
|
|
|
|
- reassign(object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // 0
|
|
|
|
|
- reassign(object_data);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- for (int i = 0; i < 8; ++i) {
|
|
|
|
|
- if (_corners[i] != (OctreeNode *)NULL) {
|
|
|
|
|
- _corners[i]->group_objects();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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) CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-occlusion_test(CullBinOcclusionTest &bin, Thread *current_thread) {
|
|
|
|
|
- // 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
|
|
|
|
|
- // cull traversal, so we can't just create a CullableObject or do
|
|
|
|
|
- // anything else that requires a pointer to a CullTraverser.
|
|
|
|
|
- // Instead, we have to do the relevant code by hand.
|
|
|
|
|
- CPT(TransformState) net_transform = TransformState::make_pos_hpr_scale
|
|
|
|
|
- (_mid, LVecBase3f(0.0f, 0.0f, 0.0f),
|
|
|
|
|
- LVecBase3f(_half_side, _half_side, _half_side));
|
|
|
|
|
- CPT(TransformState) world_transform = bin._gsg->get_scene()->get_world_transform();
|
|
|
|
|
- CPT(TransformState) modelview_transform = world_transform->compose(net_transform);
|
|
|
|
|
- CPT(TransformState) internal_transform = bin._gsg->get_cs_transform()->compose(modelview_transform);
|
|
|
|
|
-
|
|
|
|
|
- CPT(RenderState) state = get_octree_solid_test_state();
|
|
|
|
|
- PT(GeomMunger) munger = bin._gsg->get_geom_munger(state, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- CPT(Geom) viz = get_octree_solid_test();
|
|
|
|
|
- CPT(GeomVertexData) munged_data = viz->get_vertex_data();
|
|
|
|
|
- munger->munge_geom(viz, munged_data, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- bin._gsg->set_state_and_transform(state, internal_transform);
|
|
|
|
|
-
|
|
|
|
|
- PStatTimer timer(bin._draw_occlusion_pcollector);
|
|
|
|
|
- bin._gsg->begin_occlusion_query();
|
|
|
|
|
- viz->draw(bin._gsg, munger, munged_data, current_thread);
|
|
|
|
|
- return bin._gsg->end_occlusion_query();
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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, Thread *current_thread) {
|
|
|
|
|
- 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, current_thread);
|
|
|
|
|
- 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, current_thread);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- 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. Returns the number of objects drawn.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-int CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-draw(CullBinOcclusionTest &bin, Thread *current_thread) {
|
|
|
|
|
- // 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;
|
|
|
|
|
- for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
|
|
|
|
|
- CullableObject *object = (*oi)._object;
|
|
|
|
|
- if (!object->_already_drawn) {
|
|
|
|
|
- CullHandler::draw(object, bin._gsg, current_thread);
|
|
|
|
|
- object->_already_drawn = true;
|
|
|
|
|
- ++num_drawn;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Now recurse on each child node.
|
|
|
|
|
- for (int i = 0; i < 8; ++i) {
|
|
|
|
|
- // 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, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- } 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, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- // 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: CullBinOcclusionTest::OctreeNode::draw_wireframe
|
|
|
|
|
-// Access: Public
|
|
|
|
|
-// Description: Draws a wireframe representation of the octree cube,
|
|
|
|
|
-// for debugging and visualization purposes.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-draw_wireframe(CullBinOcclusionTest &bin, Thread *current_thread) {
|
|
|
|
|
- // As above, this is complicated because we're doing this at such a
|
|
|
|
|
- // low level.
|
|
|
|
|
- CPT(TransformState) net_transform = TransformState::make_pos_hpr_scale
|
|
|
|
|
- (_mid, LVecBase3f(0.0f, 0.0f, 0.0f),
|
|
|
|
|
- LVecBase3f(_half_side, _half_side, _half_side));
|
|
|
|
|
- CPT(TransformState) world_transform = bin._gsg->get_scene()->get_world_transform();
|
|
|
|
|
- CPT(TransformState) modelview_transform = world_transform->compose(net_transform);
|
|
|
|
|
- CPT(TransformState) internal_transform = bin._gsg->get_cs_transform()->compose(modelview_transform);
|
|
|
|
|
-
|
|
|
|
|
- CPT(RenderState) state = RenderState::make_empty();
|
|
|
|
|
- PT(GeomMunger) munger = bin._gsg->get_geom_munger(state, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- CPT(Geom) viz = get_octree_wireframe_viz();
|
|
|
|
|
- CPT(GeomVertexData) munged_data = viz->get_vertex_data();
|
|
|
|
|
- munger->munge_geom(viz, munged_data, current_thread);
|
|
|
|
|
-
|
|
|
|
|
- bin._gsg->set_state_and_transform(state, internal_transform);
|
|
|
|
|
- viz->draw(bin._gsg, munger, munged_data, current_thread);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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) CullBinOcclusionTest::
|
|
|
|
|
-get_octree_solid_test() {
|
|
|
|
|
- if (_octree_solid_test == (Geom *)NULL) {
|
|
|
|
|
- CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3();
|
|
|
|
|
- PT(GeomVertexData) vdata =
|
|
|
|
|
- new GeomVertexData("octree", format, Geom::UH_static);
|
|
|
|
|
- GeomVertexWriter vertex(vdata, InternalName::get_vertex());
|
|
|
|
|
- vertex.add_data3f(-1.0f, -1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, -1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(-1.0f, -1.0f, 1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, -1.0f, 1.0f);
|
|
|
|
|
- vertex.add_data3f(-1.0f, 1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, 1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(-1.0f, 1.0f, 1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, 1.0f, 1.0f);
|
|
|
|
|
- PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
|
|
|
|
|
- tris->add_vertices(2, 0, 3); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(0, 1, 3); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(3, 1, 7); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(1, 5, 7); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(7, 5, 6); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(5, 4, 6); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(6, 4, 2); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(4, 0, 2); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(6, 2, 7); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(2, 3, 7); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(0, 4, 1); tris->close_primitive();
|
|
|
|
|
- tris->add_vertices(4, 5, 1); tris->close_primitive();
|
|
|
|
|
-
|
|
|
|
|
- _octree_solid_test = new Geom(vdata);
|
|
|
|
|
- _octree_solid_test->add_primitive(tris);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return _octree_solid_test;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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) CullBinOcclusionTest::
|
|
|
|
|
-get_octree_wireframe_viz() {
|
|
|
|
|
- if (_octree_wireframe_viz == (Geom *)NULL) {
|
|
|
|
|
- CPT(GeomVertexFormat) format = GeomVertexFormat::get_v3cp();
|
|
|
|
|
- PT(GeomVertexData) vdata =
|
|
|
|
|
- new GeomVertexData("octree", format, Geom::UH_static);
|
|
|
|
|
- GeomVertexWriter vertex(vdata, InternalName::get_vertex());
|
|
|
|
|
- vertex.add_data3f(-1.0f, -1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, -1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(-1.0f, -1.0f, 1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, -1.0f, 1.0f);
|
|
|
|
|
- vertex.add_data3f(-1.0f, 1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, 1.0f, -1.0f);
|
|
|
|
|
- vertex.add_data3f(-1.0f, 1.0f, 1.0f);
|
|
|
|
|
- vertex.add_data3f(1.0f, 1.0f, 1.0f);
|
|
|
|
|
- CPT(GeomVertexData) cvdata = vdata->set_color(Colorf(1.0f, 0.5f, 0.0f, 1.0f));
|
|
|
|
|
- PT(GeomLines) lines = new GeomLines(Geom::UH_static);
|
|
|
|
|
- lines->add_vertices(0, 1); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(1, 3); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(3, 2); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(2, 0); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(0, 4); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(4, 6); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(6, 7); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(7, 5); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(5, 4); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(1, 5); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(3, 7); lines->close_primitive();
|
|
|
|
|
- lines->add_vertices(2, 6); lines->close_primitive();
|
|
|
|
|
-
|
|
|
|
|
- _octree_wireframe_viz = new Geom(cvdata);
|
|
|
|
|
- _octree_wireframe_viz->add_primitive(lines);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return _octree_wireframe_viz;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: CullBinOcclusionTest::get_octree_solid_test_state
|
|
|
|
|
-// Access: Private, Static
|
|
|
|
|
-// Description: Returns the RenderState appropriate to rendering the
|
|
|
|
|
-// octree test invisibly.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-CPT(RenderState) CullBinOcclusionTest::
|
|
|
|
|
-get_octree_solid_test_state() {
|
|
|
|
|
- if (_octree_solid_test_state == (RenderState *)NULL) {
|
|
|
|
|
- _octree_solid_test_state = RenderState::make
|
|
|
|
|
- (DepthWriteAttrib::make(DepthWriteAttrib::M_off),
|
|
|
|
|
- DepthTestAttrib::make(DepthTestAttrib::M_less),
|
|
|
|
|
- ColorWriteAttrib::make(ColorWriteAttrib::C_off));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return _octree_solid_test_state;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// 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 CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-multi_assign(const CullBinOcclusionTest::ObjectData &object_data) {
|
|
|
|
|
- const LPoint3f &c = object_data._bounds->get_center();
|
|
|
|
|
- float r = object_data._bounds->get_radius();
|
|
|
|
|
-
|
|
|
|
|
- if (c[0] + r <= _mid[0]) {
|
|
|
|
|
- // -X
|
|
|
|
|
- if (c[1] + r <= _mid[1]) {
|
|
|
|
|
- // -X, -Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // -X, -Y, -Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // -X, -Y, +Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else {
|
|
|
|
|
- // -X, -Y, 0
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (c[1] - r >= _mid[1]) {
|
|
|
|
|
- // -X, +Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // -X, +Y, -Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // -X, +Y, +Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else {
|
|
|
|
|
- // -X, +Y, 0
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // -X, 0
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // -X, 0, -Z
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // -X, 0, +Z
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // -X, 0, 0
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else if (c[0] - r >= _mid[0]) {
|
|
|
|
|
- // +X
|
|
|
|
|
- if (c[1] + r <= _mid[1]) {
|
|
|
|
|
- // +X, -Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // +X, -Y, -Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // +X, -Y, +Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else {
|
|
|
|
|
- // +X, -Y, 0
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (c[1] - r >= _mid[1]) {
|
|
|
|
|
- // +X, +Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // +X, +Y, -Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // +X, +Y, +Z
|
|
|
|
|
- nassertv(false);
|
|
|
|
|
- } else {
|
|
|
|
|
- // +X, +Y, 0
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // +X, 0
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // +X, 0, -Z
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // +X, 0, +Z
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // +X, 0, 0
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // 0
|
|
|
|
|
- if (c[1] + r <= _mid[1]) {
|
|
|
|
|
- // 0, -Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // 0, -Y, -Z
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // 0, -Y, +Z
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // 0, -Y, 0
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else if (c[1] - r >= _mid[1]) {
|
|
|
|
|
- // 0, +Y
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // 0, +Y, -Z
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // 0, +Y, +Z
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // 0, +Y, 0
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- // 0, 0
|
|
|
|
|
- if (c[2] + r <= _mid[2]) {
|
|
|
|
|
- // 0, 0, -Z
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- } else if (c[2] - r >= _mid[2]) {
|
|
|
|
|
- // 0, 0, +Z
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- } else {
|
|
|
|
|
- // 0, 0, 0
|
|
|
|
|
- assign_to_corner(0, object_data);
|
|
|
|
|
- assign_to_corner(OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_y | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_z, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y, object_data);
|
|
|
|
|
- assign_to_corner(OC_x | OC_y | OC_z, object_data);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-// Function: CullBinOcclusionTest::OctreeNode::make_corner
|
|
|
|
|
-// Access: Private
|
|
|
|
|
-// Description: Makes a new octree node for the indicated corner.
|
|
|
|
|
-////////////////////////////////////////////////////////////////////
|
|
|
|
|
-void CullBinOcclusionTest::OctreeNode::
|
|
|
|
|
-make_corner(int index) {
|
|
|
|
|
- nassertv(_corners[index] == NULL);
|
|
|
|
|
-
|
|
|
|
|
- OctreeNode *node = NULL;
|
|
|
|
|
-
|
|
|
|
|
- double q = _half_side * 0.5f;
|
|
|
|
|
-
|
|
|
|
|
- switch (index) {
|
|
|
|
|
- case 0:
|
|
|
|
|
- // -X, -Y, -Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] - q, _mid[1] - q, _mid[2] - q, q);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case OC_x:
|
|
|
|
|
- // +X, -Y, -Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] + q, _mid[1] - q, _mid[2] - q, q);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case OC_y:
|
|
|
|
|
- // -X, +Y, -Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] - q, _mid[1] + q, _mid[2] - q, q);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case OC_x | OC_y:
|
|
|
|
|
- // +X, +Y, -Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] + q, _mid[1] + q, _mid[2] - q, q);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case OC_z:
|
|
|
|
|
- // -X, -Y, +Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] - q, _mid[1] - q, _mid[2] + q, q);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case OC_x | OC_z:
|
|
|
|
|
- // +X, -Y, +Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] + q, _mid[1] - q, _mid[2] + q, q);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case OC_y | OC_z:
|
|
|
|
|
- // -X, +Y, +Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] - q, _mid[1] + q, _mid[2] + q, q);
|
|
|
|
|
- break;
|
|
|
|
|
-
|
|
|
|
|
- case OC_x | OC_y | OC_z:
|
|
|
|
|
- // +X, +Y, +Z
|
|
|
|
|
- node = new OctreeNode(_mid[0] + q, _mid[1] + q, _mid[2] + q, q);
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- 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();
|
|
|
|
|
-}
|
|
|