1 #ifndef GIM_BOX_SET_H_INCLUDED
2 #define GIM_BOX_SET_H_INCLUDED
4 /*! \file gim_box_set.h
5 \author Francisco Leon Najera
8 -----------------------------------------------------------------------------
9 This source file is part of GIMPACT Library.
11 For the latest info, see http://gimpact.sourceforge.net/
13 Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371.
14 email: projectileman@yahoo.com
16 This library is free software; you can redistribute it and/or
17 modify it under the terms of EITHER:
18 (1) The GNU Lesser General Public License as published by the Free
19 Software Foundation; either version 2.1 of the License, or (at
20 your option) any later version. The text of the GNU Lesser
21 General Public License is included with this library in the
22 file GIMPACT-LICENSE-LGPL.TXT.
23 (2) The BSD-style license that is included with this library in
24 the file GIMPACT-LICENSE-BSD.TXT.
25 (3) The zlib/libpng license that is included with this library in
26 the file GIMPACT-LICENSE-ZLIB.TXT.
28 This library is distributed in the hope that it will be useful,
29 but WITHOUT ANY WARRANTY; without even the implied warranty of
30 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files
31 GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details.
33 -----------------------------------------------------------------------------
36 #include "gim_array.h"
37 #include "gim_radixsort.h"
38 #include "gim_box_collision.h"
39 #include "gim_tri_collision.h"
43 class gim_pair_set : public gim_array<GIM_PAIR>
46 gim_pair_set() : gim_array<GIM_PAIR>(32)
49 inline void push_pair(GUINT index1, GUINT index2)
51 push_back(GIM_PAIR(index1, index2));
54 inline void push_pair_inv(GUINT index1, GUINT index2)
56 push_back(GIM_PAIR(index2, index1));
60 //! Prototype Base class for primitive classification
62 This class is a wrapper for primitive collections.
63 This tells relevant info for the Bounding Box set classes, which take care of space classification.
64 This class can manage Compound shapes and trimeshes, and if it is managing trimesh then the Hierarchy Bounding Box classes will take advantage of primitive Vs Box overlapping tests for getting optimal results and less Per Box compairisons.
66 class GIM_PRIMITIVE_MANAGER_PROTOTYPE
69 virtual ~GIM_PRIMITIVE_MANAGER_PROTOTYPE() {}
70 //! determines if this manager consist on only triangles, which special case will be optimized
71 virtual bool is_trimesh() = 0;
72 virtual GUINT get_primitive_count() = 0;
73 virtual void get_primitive_box(GUINT prim_index, GIM_AABB& primbox) = 0;
74 virtual void get_primitive_triangle(GUINT prim_index, GIM_TRIANGLE& triangle) = 0;
83 //! Node Structure for trees
84 struct GIM_BOX_TREE_NODE
87 GUINT m_left; //!< Left subtree
88 GUINT m_right; //!< Right subtree
89 GUINT m_escapeIndex; //!< Scape index for traversing
90 GUINT m_data; //!< primitive index if apply
100 SIMD_FORCE_INLINE bool is_leaf_node() const
102 return (!m_left && !m_right);
106 //! Basic Box tree structure
111 gim_array<GIM_BOX_TREE_NODE> m_node_array;
114 GUINT _sort_and_calc_splitting_index(
115 gim_array<GIM_AABB_DATA>& primitive_boxes,
116 GUINT startIndex, GUINT endIndex, GUINT splitAxis);
118 GUINT _calc_splitting_axis(gim_array<GIM_AABB_DATA>& primitive_boxes, GUINT startIndex, GUINT endIndex);
120 void _build_sub_tree(gim_array<GIM_AABB_DATA>& primitive_boxes, GUINT startIndex, GUINT endIndex);
128 //! prototype functions for box tree management
130 void build_tree(gim_array<GIM_AABB_DATA>& primitive_boxes);
132 SIMD_FORCE_INLINE void clearNodes()
134 m_node_array.clear();
139 SIMD_FORCE_INLINE GUINT getNodeCount() const
144 //! tells if the node is a leaf
145 SIMD_FORCE_INLINE bool isLeafNode(GUINT nodeindex) const
147 return m_node_array[nodeindex].is_leaf_node();
150 SIMD_FORCE_INLINE GUINT getNodeData(GUINT nodeindex) const
152 return m_node_array[nodeindex].m_data;
155 SIMD_FORCE_INLINE void getNodeBound(GUINT nodeindex, GIM_AABB& bound) const
157 bound = m_node_array[nodeindex].m_bound;
160 SIMD_FORCE_INLINE void setNodeBound(GUINT nodeindex, const GIM_AABB& bound)
162 m_node_array[nodeindex].m_bound = bound;
165 SIMD_FORCE_INLINE GUINT getLeftNodeIndex(GUINT nodeindex) const
167 return m_node_array[nodeindex].m_left;
170 SIMD_FORCE_INLINE GUINT getRightNodeIndex(GUINT nodeindex) const
172 return m_node_array[nodeindex].m_right;
175 SIMD_FORCE_INLINE GUINT getScapeNodeIndex(GUINT nodeindex) const
177 return m_node_array[nodeindex].m_escapeIndex;
183 //! Generic Box Tree Template
185 This class offers an structure for managing a box tree of primitives.
186 Requires a Primitive prototype (like GIM_PRIMITIVE_MANAGER_PROTOTYPE ) and
187 a Box tree structure ( like GIM_BOX_TREE).
189 template <typename _GIM_PRIMITIVE_MANAGER_PROTOTYPE, typename _GIM_BOX_TREE_PROTOTYPE>
190 class GIM_BOX_TREE_TEMPLATE_SET
193 _GIM_PRIMITIVE_MANAGER_PROTOTYPE m_primitive_manager;
194 _GIM_BOX_TREE_PROTOTYPE m_box_tree;
198 SIMD_FORCE_INLINE void refit()
200 GUINT nodecount = getNodeCount();
203 if (isLeafNode(nodecount))
206 m_primitive_manager.get_primitive_box(getNodeData(nodecount), leafbox);
207 setNodeBound(nodecount, leafbox);
212 GUINT childindex = getLeftNodeIndex(nodecount);
214 getNodeBound(childindex, bound);
216 childindex = getRightNodeIndex(nodecount);
218 getNodeBound(childindex, bound2);
221 setNodeBound(nodecount, bound);
227 GIM_BOX_TREE_TEMPLATE_SET()
231 SIMD_FORCE_INLINE GIM_AABB getGlobalBox() const
234 getNodeBound(0, totalbox);
238 SIMD_FORCE_INLINE void setPrimitiveManager(const _GIM_PRIMITIVE_MANAGER_PROTOTYPE& primitive_manager)
240 m_primitive_manager = primitive_manager;
243 const _GIM_PRIMITIVE_MANAGER_PROTOTYPE& getPrimitiveManager() const
245 return m_primitive_manager;
248 _GIM_PRIMITIVE_MANAGER_PROTOTYPE& getPrimitiveManager()
250 return m_primitive_manager;
253 //! node manager prototype functions
256 //! this attemps to refit the box set.
257 SIMD_FORCE_INLINE void update()
262 //! this rebuild the entire set
263 SIMD_FORCE_INLINE void buildSet()
265 //obtain primitive boxes
266 gim_array<GIM_AABB_DATA> primitive_boxes;
267 primitive_boxes.resize(m_primitive_manager.get_primitive_count(), false);
269 for (GUINT i = 0; i < primitive_boxes.size(); i++)
271 m_primitive_manager.get_primitive_box(i, primitive_boxes[i].m_bound);
272 primitive_boxes[i].m_data = i;
275 m_box_tree.build_tree(primitive_boxes);
278 //! returns the indices of the primitives in the m_primitive_manager
279 SIMD_FORCE_INLINE bool boxQuery(const GIM_AABB& box, gim_array<GUINT>& collided_results) const
282 GUINT numNodes = getNodeCount();
284 while (curIndex < numNodes)
287 getNodeBound(curIndex, bound);
289 //catch bugs in tree data
291 bool aabbOverlap = bound.has_collision(box);
292 bool isleafnode = isLeafNode(curIndex);
294 if (isleafnode && aabbOverlap)
296 collided_results.push_back(getNodeData(curIndex));
299 if (aabbOverlap || isleafnode)
307 curIndex += getScapeNodeIndex(curIndex);
310 if (collided_results.size() > 0) return true;
314 //! returns the indices of the primitives in the m_primitive_manager
315 SIMD_FORCE_INLINE bool boxQueryTrans(const GIM_AABB& box,
316 const btTransform& transform, gim_array<GUINT>& collided_results) const
318 GIM_AABB transbox = box;
319 transbox.appy_transform(transform);
320 return boxQuery(transbox, collided_results);
323 //! returns the indices of the primitives in the m_primitive_manager
324 SIMD_FORCE_INLINE bool rayQuery(
325 const btVector3& ray_dir, const btVector3& ray_origin,
326 gim_array<GUINT>& collided_results) const
329 GUINT numNodes = getNodeCount();
331 while (curIndex < numNodes)
334 getNodeBound(curIndex, bound);
336 //catch bugs in tree data
338 bool aabbOverlap = bound.collide_ray(ray_origin, ray_dir);
339 bool isleafnode = isLeafNode(curIndex);
341 if (isleafnode && aabbOverlap)
343 collided_results.push_back(getNodeData(curIndex));
346 if (aabbOverlap || isleafnode)
354 curIndex += getScapeNodeIndex(curIndex);
357 if (collided_results.size() > 0) return true;
361 //! tells if this set has hierarcht
362 SIMD_FORCE_INLINE bool hasHierarchy() const
367 //! tells if this set is a trimesh
368 SIMD_FORCE_INLINE bool isTrimesh() const
370 return m_primitive_manager.is_trimesh();
374 SIMD_FORCE_INLINE GUINT getNodeCount() const
376 return m_box_tree.getNodeCount();
379 //! tells if the node is a leaf
380 SIMD_FORCE_INLINE bool isLeafNode(GUINT nodeindex) const
382 return m_box_tree.isLeafNode(nodeindex);
385 SIMD_FORCE_INLINE GUINT getNodeData(GUINT nodeindex) const
387 return m_box_tree.getNodeData(nodeindex);
390 SIMD_FORCE_INLINE void getNodeBound(GUINT nodeindex, GIM_AABB& bound) const
392 m_box_tree.getNodeBound(nodeindex, bound);
395 SIMD_FORCE_INLINE void setNodeBound(GUINT nodeindex, const GIM_AABB& bound)
397 m_box_tree.setNodeBound(nodeindex, bound);
400 SIMD_FORCE_INLINE GUINT getLeftNodeIndex(GUINT nodeindex) const
402 return m_box_tree.getLeftNodeIndex(nodeindex);
405 SIMD_FORCE_INLINE GUINT getRightNodeIndex(GUINT nodeindex) const
407 return m_box_tree.getRightNodeIndex(nodeindex);
410 SIMD_FORCE_INLINE GUINT getScapeNodeIndex(GUINT nodeindex) const
412 return m_box_tree.getScapeNodeIndex(nodeindex);
415 SIMD_FORCE_INLINE void getNodeTriangle(GUINT nodeindex, GIM_TRIANGLE& triangle) const
417 m_primitive_manager.get_primitive_triangle(getNodeData(nodeindex), triangle);
421 //! Class for Box Tree Sets
423 this has the GIM_BOX_TREE implementation for bounding boxes.
425 template <typename _GIM_PRIMITIVE_MANAGER_PROTOTYPE>
426 class GIM_BOX_TREE_SET : public GIM_BOX_TREE_TEMPLATE_SET<_GIM_PRIMITIVE_MANAGER_PROTOTYPE, GIM_BOX_TREE>
431 /// GIM_BOX_SET collision methods
432 template <typename BOX_SET_CLASS0, typename BOX_SET_CLASS1>
433 class GIM_TREE_TREE_COLLIDER
436 gim_pair_set* m_collision_pairs;
437 BOX_SET_CLASS0* m_boxset0;
438 BOX_SET_CLASS1* m_boxset1;
445 bool node0_has_triangle;
446 bool node1_has_triangle;
449 GIM_BOX_BOX_TRANSFORM_CACHE trans_cache_1to0;
450 btTransform trans_cache_0to1;
452 btVector4 m_tri0_plane;
454 btVector4 m_tri1_plane;
457 GIM_TREE_TREE_COLLIDER()
459 current_node0 = G_UINT_INFINITY;
460 current_node1 = G_UINT_INFINITY;
464 SIMD_FORCE_INLINE void retrieve_node0_triangle(GUINT node0)
466 if (node0_has_triangle) return;
467 m_boxset0->getNodeTriangle(node0, m_tri0);
469 m_tri0.m_vertices[0] = trans_cache_0to1(m_tri0.m_vertices[0]);
470 m_tri0.m_vertices[1] = trans_cache_0to1(m_tri0.m_vertices[1]);
471 m_tri0.m_vertices[2] = trans_cache_0to1(m_tri0.m_vertices[2]);
472 m_tri0.get_plane(m_tri0_plane);
474 node0_has_triangle = true;
477 SIMD_FORCE_INLINE void retrieve_node1_triangle(GUINT node1)
479 if (node1_has_triangle) return;
480 m_boxset1->getNodeTriangle(node1, m_tri1);
482 m_tri1.m_vertices[0] = trans_cache_1to0.transform(m_tri1.m_vertices[0]);
483 m_tri1.m_vertices[1] = trans_cache_1to0.transform(m_tri1.m_vertices[1]);
484 m_tri1.m_vertices[2] = trans_cache_1to0.transform(m_tri1.m_vertices[2]);
485 m_tri1.get_plane(m_tri1_plane);
487 node1_has_triangle = true;
490 SIMD_FORCE_INLINE void retrieve_node0_info(GUINT node0)
492 if (node0 == current_node0) return;
493 m_boxset0->getNodeBound(node0, m_box0);
494 node0_is_leaf = m_boxset0->isLeafNode(node0);
495 node0_has_triangle = false;
496 current_node0 = node0;
499 SIMD_FORCE_INLINE void retrieve_node1_info(GUINT node1)
501 if (node1 == current_node1) return;
502 m_boxset1->getNodeBound(node1, m_box1);
503 node1_is_leaf = m_boxset1->isLeafNode(node1);
504 node1_has_triangle = false;
505 current_node1 = node1;
508 SIMD_FORCE_INLINE bool node_collision(GUINT node0, GUINT node1)
510 retrieve_node0_info(node0);
511 retrieve_node1_info(node1);
512 bool result = m_box0.overlapping_trans_cache(m_box1, trans_cache_1to0, true);
513 if (!result) return false;
515 if (t0_is_trimesh && node0_is_leaf)
517 //perform primitive vs box collision
518 retrieve_node0_triangle(node0);
519 //do triangle vs box collision
520 m_box1.increment_margin(m_tri0.m_margin);
522 result = m_box1.collide_triangle_exact(
523 m_tri0.m_vertices[0], m_tri0.m_vertices[1], m_tri0.m_vertices[2], m_tri0_plane);
525 m_box1.increment_margin(-m_tri0.m_margin);
527 if (!result) return false;
530 else if (t1_is_trimesh && node1_is_leaf)
532 //perform primitive vs box collision
533 retrieve_node1_triangle(node1);
534 //do triangle vs box collision
535 m_box0.increment_margin(m_tri1.m_margin);
537 result = m_box0.collide_triangle_exact(
538 m_tri1.m_vertices[0], m_tri1.m_vertices[1], m_tri1.m_vertices[2], m_tri1_plane);
540 m_box0.increment_margin(-m_tri1.m_margin);
542 if (!result) return false;
548 //stackless collision routine
549 void find_collision_pairs()
551 gim_pair_set stack_collisions;
552 stack_collisions.reserve(32);
555 stack_collisions.push_pair(0, 0);
557 while (stack_collisions.size())
559 //retrieve the last pair and pop
560 GUINT node0 = stack_collisions.back().m_index1;
561 GUINT node1 = stack_collisions.back().m_index2;
562 stack_collisions.pop_back();
563 if (node_collision(node0, node1)) // a collision is found
569 m_collision_pairs->push_pair(m_boxset0->getNodeData(node0), m_boxset1->getNodeData(node1));
574 stack_collisions.push_pair(node0, m_boxset1->getLeftNodeIndex(node1));
577 stack_collisions.push_pair(node0, m_boxset1->getRightNodeIndex(node1));
585 stack_collisions.push_pair(m_boxset0->getLeftNodeIndex(node0), node1);
587 stack_collisions.push_pair(m_boxset0->getRightNodeIndex(node0), node1);
591 GUINT left0 = m_boxset0->getLeftNodeIndex(node0);
592 GUINT right0 = m_boxset0->getRightNodeIndex(node0);
593 GUINT left1 = m_boxset1->getLeftNodeIndex(node1);
594 GUINT right1 = m_boxset1->getRightNodeIndex(node1);
596 stack_collisions.push_pair(left0, left1);
598 stack_collisions.push_pair(left0, right1);
600 stack_collisions.push_pair(right0, left1);
602 stack_collisions.push_pair(right0, right1);
604 } // else if node1 is not a leaf
605 } // else if node0 is not a leaf
607 } // if(node_collision(node0,node1))
608 } //while(stack_collisions.size())
612 void find_collision(BOX_SET_CLASS0* boxset1, const btTransform& trans1,
613 BOX_SET_CLASS1* boxset2, const btTransform& trans2,
614 gim_pair_set& collision_pairs, bool complete_primitive_tests = true)
616 m_collision_pairs = &collision_pairs;
620 trans_cache_1to0.calc_from_homogenic(trans1, trans2);
622 trans_cache_0to1 = trans2.inverse();
623 trans_cache_0to1 *= trans1;
625 if (complete_primitive_tests)
627 t0_is_trimesh = boxset1->getPrimitiveManager().is_trimesh();
628 t1_is_trimesh = boxset2->getPrimitiveManager().is_trimesh();
632 t0_is_trimesh = false;
633 t1_is_trimesh = false;
636 find_collision_pairs();
640 #endif // GIM_BOXPRUNING_H_INCLUDED