/* * Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz> * * This source file is part of GIMPACT Library. * * For the latest info, see http://gimpact.sourceforge.net/ * * Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. * email: projectileman@yahoo.com * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ package com.bulletphysics.extras.gimpact; import com.bulletphysics.extras.gimpact.BoxCollision.AABB; import com.bulletphysics.extras.gimpact.BoxCollision.BoxBoxTransformCache; import com.bulletphysics.linearmath.Transform; import com.bulletphysics.util.IntArrayList; import com.bulletphysics.util.Stack; import javax.vecmath.Vector3f; /** * * @author jezek2 */ class GImpactBvh { protected BvhTree box_tree = new BvhTree(); protected PrimitiveManagerBase primitive_manager; /** * This constructor doesn't build the tree. you must call buildSet. */ public GImpactBvh() { primitive_manager = null; } /** * This constructor doesn't build the tree. you must call buildSet. */ public GImpactBvh(PrimitiveManagerBase primitive_manager) { this.primitive_manager = primitive_manager; } public AABB getGlobalBox(AABB out) { getNodeBound(0, out); return out; } public void setPrimitiveManager(PrimitiveManagerBase primitive_manager) { this.primitive_manager = primitive_manager; } public PrimitiveManagerBase getPrimitiveManager() { return primitive_manager; } // stackless refit protected void refit() { Stack stack = Stack.enter(); AABB leafbox = stack.allocAABB(); AABB bound = stack.allocAABB(); AABB temp_box = stack.allocAABB(); int nodecount = getNodeCount(); while ((nodecount--) != 0) { if (isLeafNode(nodecount)) { primitive_manager.get_primitive_box(getNodeData(nodecount), leafbox); setNodeBound(nodecount, leafbox); } else { //const BT_BVH_TREE_NODE * nodepointer = get_node_pointer(nodecount); //get left bound bound.invalidate(); int child_node = getLeftNode(nodecount); if (child_node != 0) { getNodeBound(child_node, temp_box); bound.merge(temp_box); } child_node = getRightNode(nodecount); if (child_node != 0) { getNodeBound(child_node, temp_box); bound.merge(temp_box); } setNodeBound(nodecount, bound); } } stack.leave(); } /** * This attemps to refit the box set. */ public void update() { refit(); } /** * This rebuild the entire set. */ public void buildSet() { // obtain primitive boxes BvhDataArray primitive_boxes = new BvhDataArray(); primitive_boxes.resize(primitive_manager.get_primitive_count()); Stack stack = Stack.enter(); AABB tmpAABB = stack.allocAABB(); for (int i = 0; i < primitive_boxes.size(); i++) { //primitive_manager.get_primitive_box(i,primitive_boxes[i].bound); primitive_manager.get_primitive_box(i, tmpAABB); primitive_boxes.setBound(i, tmpAABB); primitive_boxes.setData(i, i); } box_tree.build_tree(primitive_boxes); stack.leave(); } /** * Returns the indices of the primitives in the primitive_manager field. */ public boolean boxQuery(AABB box, IntArrayList collided_results) { int curIndex = 0; int numNodes = getNodeCount(); Stack stack = Stack.enter(); AABB bound = stack.allocAABB(); while (curIndex < numNodes) { getNodeBound(curIndex, bound); // catch bugs in tree data boolean aabbOverlap = bound.has_collision(box); boolean isleafnode = isLeafNode(curIndex); if (isleafnode && aabbOverlap) { collided_results.add(getNodeData(curIndex)); } if (aabbOverlap || isleafnode) { // next subnode curIndex++; } else { // skip node curIndex += getEscapeNodeIndex(curIndex); } } if (collided_results.size() > 0) { stack.leave(); return true; } stack.leave(); return false; } /** * Returns the indices of the primitives in the primitive_manager field. */ public boolean boxQueryTrans(AABB box, Transform transform, IntArrayList collided_results) { Stack stack = Stack.enter(); AABB transbox = stack.alloc(box); transbox.appy_transform(transform); boolean result = boxQuery(transbox, collided_results); stack.leave(); return result; } /** * Returns the indices of the primitives in the primitive_manager field. */ public boolean rayQuery(Vector3f ray_dir, Vector3f ray_origin, IntArrayList collided_results) { int curIndex = 0; int numNodes = getNodeCount(); Stack stack = Stack.enter(); AABB bound = stack.allocAABB(); while (curIndex < numNodes) { getNodeBound(curIndex, bound); // catch bugs in tree data boolean aabbOverlap = bound.collide_ray(ray_origin, ray_dir); boolean isleafnode = isLeafNode(curIndex); if (isleafnode && aabbOverlap) { collided_results.add(getNodeData(curIndex)); } if (aabbOverlap || isleafnode) { // next subnode curIndex++; } else { // skip node curIndex += getEscapeNodeIndex(curIndex); } } if (collided_results.size() > 0) { stack.leave(); return true; } stack.leave(); return false; } /** * Tells if this set has hierarchy. */ public boolean hasHierarchy() { return true; } /** * Tells if this set is a trimesh. */ public boolean isTrimesh() { return primitive_manager.is_trimesh(); } public int getNodeCount() { return box_tree.getNodeCount(); } /** * Tells if the node is a leaf. */ public boolean isLeafNode(int nodeindex) { return box_tree.isLeafNode(nodeindex); } public int getNodeData(int nodeindex) { return box_tree.getNodeData(nodeindex); } public void getNodeBound(int nodeindex, AABB bound) { box_tree.getNodeBound(nodeindex, bound); } public void setNodeBound(int nodeindex, AABB bound) { box_tree.setNodeBound(nodeindex, bound); } public int getLeftNode(int nodeindex) { return box_tree.getLeftNode(nodeindex); } public int getRightNode(int nodeindex) { return box_tree.getRightNode(nodeindex); } public int getEscapeNodeIndex(int nodeindex) { return box_tree.getEscapeNodeIndex(nodeindex); } public void getNodeTriangle(int nodeindex, PrimitiveTriangle triangle) { primitive_manager.get_primitive_triangle(getNodeData(nodeindex), triangle); } public BvhTreeNodeArray get_node_pointer() { return box_tree.get_node_pointer(); } private static boolean _node_collision(GImpactBvh boxset0, GImpactBvh boxset1, BoxBoxTransformCache trans_cache_1to0, int node0, int node1, boolean complete_primitive_tests) { Stack stack = Stack.enter(); AABB box0 = stack.allocAABB(); boxset0.getNodeBound(node0, box0); AABB box1 = stack.allocAABB(); boxset1.getNodeBound(node1, box1); boolean result = box0.overlapping_trans_cache(box1, trans_cache_1to0, complete_primitive_tests); //box1.appy_transform_trans_cache(trans_cache_1to0); //return box0.has_collision(box1); stack.leave(); return result; } /** * Stackless recursive collision routine. */ private static void _find_collision_pairs_recursive(GImpactBvh boxset0, GImpactBvh boxset1, PairSet collision_pairs, BoxBoxTransformCache trans_cache_1to0, int node0, int node1, boolean complete_primitive_tests) { if (_node_collision( boxset0, boxset1, trans_cache_1to0, node0, node1, complete_primitive_tests) == false) { return;//avoid colliding internal nodes } if (boxset0.isLeafNode(node0)) { if (boxset1.isLeafNode(node1)) { // collision result collision_pairs.push_pair(boxset0.getNodeData(node0), boxset1.getNodeData(node1)); return; } else { // collide left recursive _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, node0, boxset1.getLeftNode(node1), false); // collide right recursive _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, node0, boxset1.getRightNode(node1), false); } } else { if (boxset1.isLeafNode(node1)) { // collide left recursive _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, boxset0.getLeftNode(node0), node1, false); // collide right recursive _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, boxset0.getRightNode(node0), node1, false); } else { // collide left0 left1 _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, boxset0.getLeftNode(node0), boxset1.getLeftNode(node1), false); // collide left0 right1 _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, boxset0.getLeftNode(node0), boxset1.getRightNode(node1), false); // collide right0 left1 _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, boxset0.getRightNode(node0), boxset1.getLeftNode(node1), false); // collide right0 right1 _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, boxset0.getRightNode(node0), boxset1.getRightNode(node1), false); } // else if node1 is not a leaf } // else if node0 is not a leaf } //public static float getAverageTreeCollisionTime(); public static void find_collision(GImpactBvh boxset0, Transform trans0, GImpactBvh boxset1, Transform trans1, PairSet collision_pairs) { if (boxset0.getNodeCount() == 0 || boxset1.getNodeCount() == 0) { return; } Stack stack = Stack.enter(); BoxBoxTransformCache trans_cache_1to0 = stack.allocBoxBoxTransformCache(); trans_cache_1to0.calc_from_homogenic(trans0, trans1); //#ifdef TRI_COLLISION_PROFILING //bt_begin_gim02_tree_time(); //#endif //TRI_COLLISION_PROFILING _find_collision_pairs_recursive( boxset0, boxset1, collision_pairs, trans_cache_1to0, 0, 0, true); //#ifdef TRI_COLLISION_PROFILING //bt_end_gim02_tree_time(); //#endif //TRI_COLLISION_PROFILING stack.leave(); } }