/* * Java port of Bullet (c) 2008 Martin Dvorak * * Bullet Continuous Collision Detection and Physics Library * Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.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.collision.dispatch; import com.bulletphysics.BulletStats; import java.util.Comparator; import com.bulletphysics.collision.broadphase.BroadphasePair; import com.bulletphysics.collision.broadphase.Dispatcher; import com.bulletphysics.collision.narrowphase.PersistentManifold; import com.bulletphysics.linearmath.MiscUtil; import com.bulletphysics.util.ObjectArrayList; /** * SimulationIslandManager creates and handles simulation islands, using {@link UnionFind}. * * @author jezek2 */ public class SimulationIslandManager implements java.io.Serializable { private final UnionFind unionFind = new UnionFind(); private final ObjectArrayList islandmanifold = new ObjectArrayList(); private final ObjectArrayList islandBodies = new ObjectArrayList(); public void initUnionFind(int n) { unionFind.reset(n); } public UnionFind getUnionFind() { return unionFind; } public void findUnions(Dispatcher dispatcher, CollisionWorld colWorld) { ObjectArrayList pairPtr = colWorld.getPairCache().getOverlappingPairArray(); for (int i=0; i= 0? rcolObj0.getIslandTag() : rcolObj1.getIslandTag(); return islandId; } public void buildIslands(Dispatcher dispatcher, ObjectArrayList collisionObjects) { BulletStats.pushProfile("islandUnionFindAndQuickSort"); try { islandmanifold.clear(); // we are going to sort the unionfind array, and store the element id in the size // afterwards, we clean unionfind, to make sure no-one uses it anymore getUnionFind().sortIslands(); int numElem = getUnionFind().getNumElements(); int endIslandIndex = 1; int startIslandIndex; // update the sleeping state for bodies, if all are sleeping for (startIslandIndex = 0; startIslandIndex < numElem; startIslandIndex = endIslandIndex) { int islandId = getUnionFind().getElement(startIslandIndex).id; for (endIslandIndex = startIslandIndex + 1; (endIslandIndex < numElem) && (getUnionFind().getElement(endIslandIndex).id == islandId); endIslandIndex++) { } //int numSleeping = 0; boolean allSleeping = true; int idx; for (idx = startIslandIndex; idx < endIslandIndex; idx++) { int i = getUnionFind().getElement(idx).sz; CollisionObject colObj0 = collisionObjects.getQuick(i); if ((colObj0.getIslandTag() != islandId) && (colObj0.getIslandTag() != -1)) { //System.err.println("error in island management\n"); } assert ((colObj0.getIslandTag() == islandId) || (colObj0.getIslandTag() == -1)); if (colObj0.getIslandTag() == islandId) { if (colObj0.getActivationState() == CollisionObject.ACTIVE_TAG) { allSleeping = false; } if (colObj0.getActivationState() == CollisionObject.DISABLE_DEACTIVATION) { allSleeping = false; } } } if (allSleeping) { //int idx; for (idx = startIslandIndex; idx < endIslandIndex; idx++) { int i = getUnionFind().getElement(idx).sz; CollisionObject colObj0 = collisionObjects.getQuick(i); if ((colObj0.getIslandTag() != islandId) && (colObj0.getIslandTag() != -1)) { //System.err.println("error in island management\n"); } assert ((colObj0.getIslandTag() == islandId) || (colObj0.getIslandTag() == -1)); if (colObj0.getIslandTag() == islandId) { colObj0.setActivationState(CollisionObject.ISLAND_SLEEPING); } } } else { //int idx; for (idx = startIslandIndex; idx < endIslandIndex; idx++) { int i = getUnionFind().getElement(idx).sz; CollisionObject colObj0 = collisionObjects.getQuick(i); if ((colObj0.getIslandTag() != islandId) && (colObj0.getIslandTag() != -1)) { //System.err.println("error in island management\n"); } assert ((colObj0.getIslandTag() == islandId) || (colObj0.getIslandTag() == -1)); if (colObj0.getIslandTag() == islandId) { if (colObj0.getActivationState() == CollisionObject.ISLAND_SLEEPING) { colObj0.setActivationState(CollisionObject.WANTS_DEACTIVATION); } } } } } int i; int maxNumManifolds = dispatcher.getNumManifolds(); //#define SPLIT_ISLANDS 1 //#ifdef SPLIT_ISLANDS //#endif //SPLIT_ISLANDS for (i = 0; i < maxNumManifolds; i++) { PersistentManifold manifold = dispatcher.getManifoldByIndexInternal(i); CollisionObject colObj0 = (CollisionObject) manifold.getBody0(); CollisionObject colObj1 = (CollisionObject) manifold.getBody1(); // todo: check sleeping conditions! if (((colObj0 != null) && colObj0.getActivationState() != CollisionObject.ISLAND_SLEEPING) || ((colObj1 != null) && colObj1.getActivationState() != CollisionObject.ISLAND_SLEEPING)) { // kinematic objects don't merge islands, but wake up all connected objects if (colObj0.isKinematicObject() && colObj0.getActivationState() != CollisionObject.ISLAND_SLEEPING) { colObj1.activate(); } if (colObj1.isKinematicObject() && colObj1.getActivationState() != CollisionObject.ISLAND_SLEEPING) { colObj0.activate(); } //#ifdef SPLIT_ISLANDS //filtering for response if (dispatcher.needsResponse(colObj0, colObj1)) { islandmanifold.add(manifold); } //#endif //SPLIT_ISLANDS } } } finally { BulletStats.popProfile(); } } public void buildAndProcessIslands(Dispatcher dispatcher, ObjectArrayList collisionObjects, IslandCallback callback) { buildIslands(dispatcher, collisionObjects); int endIslandIndex = 1; int startIslandIndex; int numElem = getUnionFind().getNumElements(); BulletStats.pushProfile("processIslands"); try { //#ifndef SPLIT_ISLANDS //btPersistentManifold** manifold = dispatcher->getInternalManifoldPointer(); // //callback->ProcessIsland(&collisionObjects[0],collisionObjects.size(),manifold,maxNumManifolds, -1); //#else // Sort manifolds, based on islands // Sort the vector using predicate and std::sort //std::sort(islandmanifold.begin(), islandmanifold.end(), btPersistentManifoldSortPredicate); int numManifolds = islandmanifold.size(); // we should do radix sort, it it much faster (O(n) instead of O (n log2(n)) //islandmanifold.heapSort(btPersistentManifoldSortPredicate()); // JAVA NOTE: memory optimized sorting with caching of temporary array //Collections.sort(islandmanifold, persistentManifoldComparator); MiscUtil.quickSort(islandmanifold, persistentManifoldComparator); // now process all active islands (sets of manifolds for now) int startManifoldIndex = 0; int endManifoldIndex = 1; //int islandId; //printf("Start Islands\n"); // traverse the simulation islands, and call the solver, unless all objects are sleeping/deactivated for (startIslandIndex = 0; startIslandIndex < numElem; startIslandIndex = endIslandIndex) { int islandId = getUnionFind().getElement(startIslandIndex).id; boolean islandSleeping = false; for (endIslandIndex = startIslandIndex; (endIslandIndex < numElem) && (getUnionFind().getElement(endIslandIndex).id == islandId); endIslandIndex++) { int i = getUnionFind().getElement(endIslandIndex).sz; CollisionObject colObj0 = collisionObjects.getQuick(i); islandBodies.add(colObj0); if (!colObj0.isActive()) { islandSleeping = true; } } // find the accompanying contact manifold for this islandId int numIslandManifolds = 0; //ObjectArrayList startManifold = null; int startManifold_idx = -1; if (startManifoldIndex < numManifolds) { int curIslandId = getIslandId(islandmanifold.getQuick(startManifoldIndex)); if (curIslandId == islandId) { //startManifold = &m_islandmanifold[startManifoldIndex]; //startManifold = islandmanifold.subList(startManifoldIndex, islandmanifold.size()); startManifold_idx = startManifoldIndex; for (endManifoldIndex = startManifoldIndex + 1; (endManifoldIndex < numManifolds) && (islandId == getIslandId(islandmanifold.getQuick(endManifoldIndex))); endManifoldIndex++) { } // Process the actual simulation, only if not sleeping/deactivated numIslandManifolds = endManifoldIndex - startManifoldIndex; } } if (!islandSleeping) { callback.processIsland(islandBodies, islandBodies.size(), islandmanifold, startManifold_idx, numIslandManifolds, islandId); //printf("Island callback of size:%d bodies, %d manifolds\n",islandBodies.size(),numIslandManifolds); } if (numIslandManifolds != 0) { startManifoldIndex = endManifoldIndex; } islandBodies.clear(); } //#endif //SPLIT_ISLANDS } finally { BulletStats.popProfile(); } } //////////////////////////////////////////////////////////////////////////// public static abstract class IslandCallback implements java.io.Serializable { public abstract void processIsland(ObjectArrayList bodies, int numBodies, ObjectArrayList manifolds, int manifolds_offset, int numManifolds, int islandId); } private static final Comparator persistentManifoldComparator = new Comparator() { public int compare(PersistentManifold lhs, PersistentManifold rhs) { return getIslandId(lhs) < getIslandId(rhs)? -1 : +1; } }; }