2
* This file is part of peekabot.
4
* peekabot is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* peekabot is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
* \brief \c OctreeNode class implementation.
22
* \todo Could benefit from clearer separation from \c Octree, i.e. by
23
* adding dedicated methods for add/remove child nodes.
31
#include "Cullable.hh"
32
#include "primitives/Camera.hh"
33
#include "OctreeTraverser.hh"
37
using namespace peekabot;
38
using namespace peekabot::renderer;
42
Octree::OctreeNode::OctreeNode(OctreeNode *parent,
45
const real32 &min_node_size) :
46
m_half_size(half_size),
50
m_min_node_size(min_node_size),
53
for( int i = 0; i < 8; i++ )
57
Octree::OctreeNode::~OctreeNode()
59
for( int i = 0; i < 8; i++ )
60
if( m_children[i] != 0 )
67
Octree::OctreeNode *Octree::OctreeNode::add_object(Cullable *obj) throw()
69
// 1. It is enclosed by a child node and the minimum node size has not
71
// 1.1. That child exists, do a recurisve add.
72
// 1.2. It doesn't exist, create it and do a recursive add.
73
// 2. Not enclosed by a child, add to this node.
75
//assert(encloses(obj));
80
if( obj->get_bounding_sphere().get_radius() <= m_half_size/2 &&
81
m_half_size/2 > m_min_node_size )
84
for( int i = 0; i < 8; i++ )
86
if( enclosed_in_octant(obj, i) )
88
if( m_children[i] == 0 )
90
// invariant: m_half_size/2 > m_min_node_size
92
m_children[i] = new OctreeNode(
95
m_pos + octant_offset(i),
101
return m_children[i]->add_object(obj);
105
TheMessageHub::instance().publish(
107
"Pending assertion failure! Object not enclosed in a child node, "
108
"even though it should meet the requirements to do so. "
109
"bounding sphere: pos=(%f, %f, %f), r=%f",
110
obj->get_bounding_sphere().get_pos()(0),
111
obj->get_bounding_sphere().get_pos()(1),
112
obj->get_bounding_sphere().get_pos()(2),
113
obj->get_bounding_sphere().get_radius());
115
assert(false); // Should never happend
119
m_contained_objects.insert(obj);
126
void Octree::OctreeNode::remove_object(Cullable *obj,
127
bool update_enclosed_count) throw()
129
assert(contains(obj));
131
m_contained_objects.erase(obj);
134
if( update_enclosed_count )
136
// Decrease enclosed count for ancestor nodes
137
OctreeNode *parent = m_parent;
140
--parent->m_enclosed;
142
parent = parent->m_parent;
148
bool Octree::OctreeNode::encloses(Cullable *obj) const throw()
150
if( obj->get_bounding_sphere().get_radius() > m_half_size )
153
real32 x = obj->get_bounding_sphere().get_pos()(0);
154
real32 y = obj->get_bounding_sphere().get_pos()(1);
155
real32 z = obj->get_bounding_sphere().get_pos()(2);
157
if( x >= m_pos(0) - m_half_size/2 && x < m_pos(0) + m_half_size/2 &&
158
y >= m_pos(1) - m_half_size/2 && y < m_pos(1) + m_half_size/2 &&
159
z >= m_pos(2) - m_half_size/2 && z < m_pos(2) + m_half_size/2 )
168
bool Octree::OctreeNode::enclosed_in_octant(Cullable *obj,
169
int octant) const throw()
171
if( obj->get_bounding_sphere().get_radius() > m_half_size/2 )
174
real32 x = obj->get_bounding_sphere().get_pos()(0);
175
real32 y = obj->get_bounding_sphere().get_pos()(1);
176
real32 z = obj->get_bounding_sphere().get_pos()(2);
178
return ( ( octant&1 ? x >= m_pos(0) : x < m_pos(0) ) &&
179
( octant&2 ? y >= m_pos(1) : y < m_pos(1) ) &&
180
( octant&4 ? z >= m_pos(2) : z < m_pos(2) ) );
184
bool Octree::OctreeNode::contains(const Cullable *obj) const throw()
186
return m_contained_objects.find(const_cast<Cullable *>(obj))
187
!= m_contained_objects.end();
192
/*bool Octree::OctreeNode::is_empty() const throw()
194
//assert(m_enclosed >= 0);
196
return m_enclosed == 0;
201
Vector3r Octree::OctreeNode::octant_offset(int octant) const throw()
203
real32 hs = m_half_size/4;
205
return Vector3r( hs*(2*(octant&1)-1),
207
hs*(((octant&4)>>1)-1) );
211
Octree::OctreeNode *Octree::OctreeNode::get_parent() throw()
217
const Octree::OctreeNode *Octree::OctreeNode::get_parent() const throw()
223
int Octree::OctreeNode::get_child_count() const throw()
225
return m_child_count;
229
void Octree::OctreeNode::eval_recursive(
230
OctreeTraverser *traverser,
231
const int order[]) const throw()
233
for( ContainedSet::const_iterator it = m_contained_objects.begin();
234
it != m_contained_objects.end(); ++it )
236
traverser->traverse(*it);
239
// Execute fn on recursively on all non-empty child nodes
240
for( int i = 0; i < 8; i++ )
242
OctreeNode *child = m_children[order[i]];
243
if( child && !child->is_empty() )
244
child->eval_recursive(traverser, order);
250
void Octree::OctreeNode::traverse_pvs(primitives::Camera *camera,
251
OctreeTraverser *traverser,
253
int plane_mask) const throw()
258
const boost::shared_ptr<Frustum> frustum(camera->get_frustum());
260
BoundingSphere bsphere = BoundingSphere(m_pos, m_half_size*M_SQRT2);
263
IntersectionTestResult culltest = bsphere.contained_in(
264
frustum->get_bounding_sphere());
266
//if( culltest != DISJOINT )
267
// culltest = bsphere.contained_in(camera->get_frustum_bounding_cone());
269
if( culltest != DISJOINT )
270
culltest = frustum->is_in_frustum(bsphere, plane_mask);
272
if( culltest == DISJOINT )
274
// Cull all child nodes!
277
else if( culltest == INSIDE )
279
eval_recursive(traverser, order);
281
else // culltest == INTERSECT
283
// If partial intersection:
284
// 1. Do object level tests for contained objects
285
// 2. traverse child nodes
287
for( std::set<Cullable *>::iterator it = m_contained_objects.begin();
288
it != m_contained_objects.end(); ++it )
290
object_level_cull(*it, camera, traverser, plane_mask);
295
for( int i = 0; i < 8; i++ )
297
OctreeNode *child = m_children[order[i]];
299
if( child ) //&& !child->is_empty() )
301
child->traverse_pvs(camera,
313
// Performs sphere-sphere and sphere-frustum tests
314
void Octree::OctreeNode::object_level_cull(
316
primitives::Camera *camera,
317
OctreeTraverser *traverser,
318
int plane_mask) throw()
320
const boost::shared_ptr<Frustum> frustum(camera->get_frustum());
323
IntersectionTestResult culltest = node->get_bounding_sphere().contained_in(
324
frustum->get_bounding_sphere());
326
if( culltest != DISJOINT )
327
culltest = frustum->is_in_frustum(
328
node->get_bounding_sphere(), plane_mask);
330
if( culltest != DISJOINT )
331
traverser->traverse(node);
337
Octree::OctreeNode *Octree::OctreeNode::get_octant(int i) throw()
339
return m_children[i];
343
const Octree::OctreeNode *Octree::OctreeNode::get_octant(int i) const throw()
345
return m_children[i];