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 -----------------------------------------------------------------------------
37 #include "gim_array.h"
38 #include "gim_radixsort.h"
39 #include "gim_box_collision.h"
40 #include "gim_tri_collision.h"
52 GIM_PAIR(const GIM_PAIR & p)
54 m_index1 = p.m_index1;
55 m_index2 = p.m_index2;
58 GIM_PAIR(GUINT index1, GUINT index2)
66 class gim_pair_set: public gim_array<GIM_PAIR>
69 gim_pair_set():gim_array<GIM_PAIR>(32)
72 inline void push_pair(GUINT index1,GUINT index2)
74 push_back(GIM_PAIR(index1,index2));
77 inline void push_pair_inv(GUINT index1,GUINT index2)
79 push_back(GIM_PAIR(index2,index1));
84 //! Prototype Base class for primitive classification
86 This class is a wrapper for primitive collections.
87 This tells relevant info for the Bounding Box set classes, which take care of space classification.
88 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.
90 class GIM_PRIMITIVE_MANAGER_PROTOTYPE
94 virtual ~GIM_PRIMITIVE_MANAGER_PROTOTYPE() {}
95 //! determines if this manager consist on only triangles, which special case will be optimized
96 virtual bool is_trimesh() = 0;
97 virtual GUINT get_primitive_count() = 0;
98 virtual void get_primitive_box(GUINT prim_index ,GIM_AABB & primbox) = 0;
99 virtual void get_primitive_triangle(GUINT prim_index,GIM_TRIANGLE & triangle) = 0;
109 //! Node Structure for trees
110 struct GIM_BOX_TREE_NODE
113 GUINT m_left;//!< Left subtree
114 GUINT m_right;//!< Right subtree
115 GUINT m_escapeIndex;//!< Scape index for traversing
116 GUINT m_data;//!< primitive index if apply
126 SIMD_FORCE_INLINE bool is_leaf_node() const
128 return (!m_left && !m_right);
132 //! Basic Box tree structure
137 gim_array<GIM_BOX_TREE_NODE> m_node_array;
139 GUINT _sort_and_calc_splitting_index(
140 gim_array<GIM_AABB_DATA> & primitive_boxes,
141 GUINT startIndex, GUINT endIndex, GUINT splitAxis);
143 GUINT _calc_splitting_axis(gim_array<GIM_AABB_DATA> & primitive_boxes, GUINT startIndex, GUINT endIndex);
145 void _build_sub_tree(gim_array<GIM_AABB_DATA> & primitive_boxes, GUINT startIndex, GUINT endIndex);
152 //! prototype functions for box tree management
154 void build_tree(gim_array<GIM_AABB_DATA> & primitive_boxes);
156 SIMD_FORCE_INLINE void clearNodes()
158 m_node_array.clear();
163 SIMD_FORCE_INLINE GUINT getNodeCount() const
168 //! tells if the node is a leaf
169 SIMD_FORCE_INLINE bool isLeafNode(GUINT nodeindex) const
171 return m_node_array[nodeindex].is_leaf_node();
174 SIMD_FORCE_INLINE GUINT getNodeData(GUINT nodeindex) const
176 return m_node_array[nodeindex].m_data;
179 SIMD_FORCE_INLINE void getNodeBound(GUINT nodeindex, GIM_AABB & bound) const
181 bound = m_node_array[nodeindex].m_bound;
184 SIMD_FORCE_INLINE void setNodeBound(GUINT nodeindex, const GIM_AABB & bound)
186 m_node_array[nodeindex].m_bound = bound;
189 SIMD_FORCE_INLINE GUINT getLeftNodeIndex(GUINT nodeindex) const
191 return m_node_array[nodeindex].m_left;
194 SIMD_FORCE_INLINE GUINT getRightNodeIndex(GUINT nodeindex) const
196 return m_node_array[nodeindex].m_right;
199 SIMD_FORCE_INLINE GUINT getScapeNodeIndex(GUINT nodeindex) const
201 return m_node_array[nodeindex].m_escapeIndex;
208 //! Generic Box Tree Template
210 This class offers an structure for managing a box tree of primitives.
211 Requires a Primitive prototype (like GIM_PRIMITIVE_MANAGER_PROTOTYPE ) and
212 a Box tree structure ( like GIM_BOX_TREE).
214 template<typename _GIM_PRIMITIVE_MANAGER_PROTOTYPE, typename _GIM_BOX_TREE_PROTOTYPE>
215 class GIM_BOX_TREE_TEMPLATE_SET
218 _GIM_PRIMITIVE_MANAGER_PROTOTYPE m_primitive_manager;
219 _GIM_BOX_TREE_PROTOTYPE m_box_tree;
222 SIMD_FORCE_INLINE void refit()
224 GUINT nodecount = getNodeCount();
227 if(isLeafNode(nodecount))
230 m_primitive_manager.get_primitive_box(getNodeData(nodecount),leafbox);
231 setNodeBound(nodecount,leafbox);
236 GUINT childindex = getLeftNodeIndex(nodecount);
238 getNodeBound(childindex,bound);
240 childindex = getRightNodeIndex(nodecount);
242 getNodeBound(childindex,bound2);
245 setNodeBound(nodecount,bound);
251 GIM_BOX_TREE_TEMPLATE_SET()
255 SIMD_FORCE_INLINE GIM_AABB getGlobalBox() const
258 getNodeBound(0, totalbox);
262 SIMD_FORCE_INLINE void setPrimitiveManager(const _GIM_PRIMITIVE_MANAGER_PROTOTYPE & primitive_manager)
264 m_primitive_manager = primitive_manager;
267 const _GIM_PRIMITIVE_MANAGER_PROTOTYPE & getPrimitiveManager() const
269 return m_primitive_manager;
272 _GIM_PRIMITIVE_MANAGER_PROTOTYPE & getPrimitiveManager()
274 return m_primitive_manager;
277 //! node manager prototype functions
280 //! this attemps to refit the box set.
281 SIMD_FORCE_INLINE void update()
286 //! this rebuild the entire set
287 SIMD_FORCE_INLINE void buildSet()
289 //obtain primitive boxes
290 gim_array<GIM_AABB_DATA> primitive_boxes;
291 primitive_boxes.resize(m_primitive_manager.get_primitive_count(),false);
293 for (GUINT i = 0;i<primitive_boxes.size() ;i++ )
295 m_primitive_manager.get_primitive_box(i,primitive_boxes[i].m_bound);
296 primitive_boxes[i].m_data = i;
299 m_box_tree.build_tree(primitive_boxes);
302 //! returns the indices of the primitives in the m_primitive_manager
303 SIMD_FORCE_INLINE bool boxQuery(const GIM_AABB & box, gim_array<GUINT> & collided_results) const
306 GUINT numNodes = getNodeCount();
308 while (curIndex < numNodes)
311 getNodeBound(curIndex,bound);
313 //catch bugs in tree data
315 bool aabbOverlap = bound.has_collision(box);
316 bool isleafnode = isLeafNode(curIndex);
318 if (isleafnode && aabbOverlap)
320 collided_results.push_back(getNodeData(curIndex));
323 if (aabbOverlap || isleafnode)
331 curIndex+= getScapeNodeIndex(curIndex);
334 if(collided_results.size()>0) return true;
338 //! returns the indices of the primitives in the m_primitive_manager
339 SIMD_FORCE_INLINE bool boxQueryTrans(const GIM_AABB & box,
340 const btTransform & transform, gim_array<GUINT> & collided_results) const
342 GIM_AABB transbox=box;
343 transbox.appy_transform(transform);
344 return boxQuery(transbox,collided_results);
347 //! returns the indices of the primitives in the m_primitive_manager
348 SIMD_FORCE_INLINE bool rayQuery(
349 const btVector3 & ray_dir,const btVector3 & ray_origin ,
350 gim_array<GUINT> & collided_results) const
353 GUINT numNodes = getNodeCount();
355 while (curIndex < numNodes)
358 getNodeBound(curIndex,bound);
360 //catch bugs in tree data
362 bool aabbOverlap = bound.collide_ray(ray_origin,ray_dir);
363 bool isleafnode = isLeafNode(curIndex);
365 if (isleafnode && aabbOverlap)
367 collided_results.push_back(getNodeData( curIndex));
370 if (aabbOverlap || isleafnode)
378 curIndex+= getScapeNodeIndex(curIndex);
381 if(collided_results.size()>0) return true;
385 //! tells if this set has hierarcht
386 SIMD_FORCE_INLINE bool hasHierarchy() const
391 //! tells if this set is a trimesh
392 SIMD_FORCE_INLINE bool isTrimesh() const
394 return m_primitive_manager.is_trimesh();
398 SIMD_FORCE_INLINE GUINT getNodeCount() const
400 return m_box_tree.getNodeCount();
403 //! tells if the node is a leaf
404 SIMD_FORCE_INLINE bool isLeafNode(GUINT nodeindex) const
406 return m_box_tree.isLeafNode(nodeindex);
409 SIMD_FORCE_INLINE GUINT getNodeData(GUINT nodeindex) const
411 return m_box_tree.getNodeData(nodeindex);
414 SIMD_FORCE_INLINE void getNodeBound(GUINT nodeindex, GIM_AABB & bound) const
416 m_box_tree.getNodeBound(nodeindex, bound);
419 SIMD_FORCE_INLINE void setNodeBound(GUINT nodeindex, const GIM_AABB & bound)
421 m_box_tree.setNodeBound(nodeindex, bound);
424 SIMD_FORCE_INLINE GUINT getLeftNodeIndex(GUINT nodeindex) const
426 return m_box_tree.getLeftNodeIndex(nodeindex);
429 SIMD_FORCE_INLINE GUINT getRightNodeIndex(GUINT nodeindex) const
431 return m_box_tree.getRightNodeIndex(nodeindex);
434 SIMD_FORCE_INLINE GUINT getScapeNodeIndex(GUINT nodeindex) const
436 return m_box_tree.getScapeNodeIndex(nodeindex);
439 SIMD_FORCE_INLINE void getNodeTriangle(GUINT nodeindex,GIM_TRIANGLE & triangle) const
441 m_primitive_manager.get_primitive_triangle(getNodeData(nodeindex),triangle);
446 //! Class for Box Tree Sets
448 this has the GIM_BOX_TREE implementation for bounding boxes.
450 template<typename _GIM_PRIMITIVE_MANAGER_PROTOTYPE>
451 class GIM_BOX_TREE_SET: public GIM_BOX_TREE_TEMPLATE_SET< _GIM_PRIMITIVE_MANAGER_PROTOTYPE, GIM_BOX_TREE>
461 /// GIM_BOX_SET collision methods
462 template<typename BOX_SET_CLASS0,typename BOX_SET_CLASS1>
463 class GIM_TREE_TREE_COLLIDER
466 gim_pair_set * m_collision_pairs;
467 BOX_SET_CLASS0 * m_boxset0;
468 BOX_SET_CLASS1 * m_boxset1;
475 bool node0_has_triangle;
476 bool node1_has_triangle;
479 GIM_BOX_BOX_TRANSFORM_CACHE trans_cache_1to0;
480 btTransform trans_cache_0to1;
482 btVector4 m_tri0_plane;
484 btVector4 m_tri1_plane;
488 GIM_TREE_TREE_COLLIDER()
490 current_node0 = G_UINT_INFINITY;
491 current_node1 = G_UINT_INFINITY;
494 SIMD_FORCE_INLINE void retrieve_node0_triangle(GUINT node0)
496 if(node0_has_triangle) return;
497 m_boxset0->getNodeTriangle(node0,m_tri0);
499 m_tri0.m_vertices[0] = trans_cache_0to1(m_tri0.m_vertices[0]);
500 m_tri0.m_vertices[1] = trans_cache_0to1(m_tri0.m_vertices[1]);
501 m_tri0.m_vertices[2] = trans_cache_0to1(m_tri0.m_vertices[2]);
502 m_tri0.get_plane(m_tri0_plane);
504 node0_has_triangle = true;
507 SIMD_FORCE_INLINE void retrieve_node1_triangle(GUINT node1)
509 if(node1_has_triangle) return;
510 m_boxset1->getNodeTriangle(node1,m_tri1);
512 m_tri1.m_vertices[0] = trans_cache_1to0.transform(m_tri1.m_vertices[0]);
513 m_tri1.m_vertices[1] = trans_cache_1to0.transform(m_tri1.m_vertices[1]);
514 m_tri1.m_vertices[2] = trans_cache_1to0.transform(m_tri1.m_vertices[2]);
515 m_tri1.get_plane(m_tri1_plane);
517 node1_has_triangle = true;
520 SIMD_FORCE_INLINE void retrieve_node0_info(GUINT node0)
522 if(node0 == current_node0) return;
523 m_boxset0->getNodeBound(node0,m_box0);
524 node0_is_leaf = m_boxset0->isLeafNode(node0);
525 node0_has_triangle = false;
526 current_node0 = node0;
529 SIMD_FORCE_INLINE void retrieve_node1_info(GUINT node1)
531 if(node1 == current_node1) return;
532 m_boxset1->getNodeBound(node1,m_box1);
533 node1_is_leaf = m_boxset1->isLeafNode(node1);
534 node1_has_triangle = false;
535 current_node1 = node1;
538 SIMD_FORCE_INLINE bool node_collision(GUINT node0 ,GUINT node1)
540 retrieve_node0_info(node0);
541 retrieve_node1_info(node1);
542 bool result = m_box0.overlapping_trans_cache(m_box1,trans_cache_1to0,true);
543 if(!result) return false;
545 if(t0_is_trimesh && node0_is_leaf)
547 //perform primitive vs box collision
548 retrieve_node0_triangle(node0);
549 //do triangle vs box collision
550 m_box1.increment_margin(m_tri0.m_margin);
552 result = m_box1.collide_triangle_exact(
553 m_tri0.m_vertices[0],m_tri0.m_vertices[1],m_tri0.m_vertices[2],m_tri0_plane);
555 m_box1.increment_margin(-m_tri0.m_margin);
557 if(!result) return false;
560 else if(t1_is_trimesh && node1_is_leaf)
562 //perform primitive vs box collision
563 retrieve_node1_triangle(node1);
564 //do triangle vs box collision
565 m_box0.increment_margin(m_tri1.m_margin);
567 result = m_box0.collide_triangle_exact(
568 m_tri1.m_vertices[0],m_tri1.m_vertices[1],m_tri1.m_vertices[2],m_tri1_plane);
570 m_box0.increment_margin(-m_tri1.m_margin);
572 if(!result) return false;
578 //stackless collision routine
579 void find_collision_pairs()
581 gim_pair_set stack_collisions;
582 stack_collisions.reserve(32);
585 stack_collisions.push_pair(0,0);
588 while(stack_collisions.size())
590 //retrieve the last pair and pop
591 GUINT node0 = stack_collisions.back().m_index1;
592 GUINT node1 = stack_collisions.back().m_index2;
593 stack_collisions.pop_back();
594 if(node_collision(node0,node1)) // a collision is found
600 m_collision_pairs->push_pair(m_boxset0->getNodeData(node0),m_boxset1->getNodeData(node1));
605 stack_collisions.push_pair(node0,m_boxset1->getLeftNodeIndex(node1));
608 stack_collisions.push_pair(node0,m_boxset1->getRightNodeIndex(node1));
616 stack_collisions.push_pair(m_boxset0->getLeftNodeIndex(node0),node1);
618 stack_collisions.push_pair(m_boxset0->getRightNodeIndex(node0),node1);
622 GUINT left0 = m_boxset0->getLeftNodeIndex(node0);
623 GUINT right0 = m_boxset0->getRightNodeIndex(node0);
624 GUINT left1 = m_boxset1->getLeftNodeIndex(node1);
625 GUINT right1 = m_boxset1->getRightNodeIndex(node1);
627 stack_collisions.push_pair(left0,left1);
629 stack_collisions.push_pair(left0,right1);
631 stack_collisions.push_pair(right0,left1);
633 stack_collisions.push_pair(right0,right1);
635 }// else if node1 is not a leaf
636 }// else if node0 is not a leaf
638 }// if(node_collision(node0,node1))
639 }//while(stack_collisions.size())
642 void find_collision(BOX_SET_CLASS0 * boxset1, const btTransform & trans1,
643 BOX_SET_CLASS1 * boxset2, const btTransform & trans2,
644 gim_pair_set & collision_pairs, bool complete_primitive_tests = true)
646 m_collision_pairs = &collision_pairs;
650 trans_cache_1to0.calc_from_homogenic(trans1,trans2);
652 trans_cache_0to1 = trans2.inverse();
653 trans_cache_0to1 *= trans1;
656 if(complete_primitive_tests)
658 t0_is_trimesh = boxset1->getPrimitiveManager().is_trimesh();
659 t1_is_trimesh = boxset2->getPrimitiveManager().is_trimesh();
663 t0_is_trimesh = false;
664 t1_is_trimesh = false;
667 find_collision_pairs();
672 #endif // GIM_BOXPRUNING_H_INCLUDED