/* 
 | 
 * Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz> 
 | 
 * 
 | 
 * 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.narrowphase; 
 | 
  
 | 
import com.bulletphysics.BulletGlobals; 
 | 
import com.bulletphysics.BulletStats; 
 | 
import com.bulletphysics.linearmath.Transform; 
 | 
import com.bulletphysics.linearmath.VectorUtil; 
 | 
import cz.advel.stack.Stack; 
 | 
import javax.vecmath.Vector3f; 
 | 
import javax.vecmath.Vector4f; 
 | 
  
 | 
  
 | 
/** 
 | 
 * PersistentManifold is a contact point cache, it stays persistent as long as objects 
 | 
 * are overlapping in the broadphase. Those contact points are created by the collision 
 | 
 * narrow phase.<p> 
 | 
 *  
 | 
 * The cache can be empty, or hold 1, 2, 3 or 4 points. Some collision algorithms (GJK) 
 | 
 * might only add one point at a time, updates/refreshes old contact points, and throw 
 | 
 * them away if necessary (distance becomes too large).<p> 
 | 
 *  
 | 
 * Reduces the cache to 4 points, when more then 4 points are added, using following rules: 
 | 
 * the contact point with deepest penetration is always kept, and it tries to maximize the 
 | 
 * area covered by the points.<p> 
 | 
 *  
 | 
 * Note that some pairs of objects might have more then one contact manifold. 
 | 
 *  
 | 
 * @author jezek2 
 | 
 */ 
 | 
public class PersistentManifold  implements java.io.Serializable { 
 | 
  
 | 
    //protected final BulletStack stack = BulletStack.get(); 
 | 
     
 | 
    public static final int MANIFOLD_CACHE_SIZE = 4; 
 | 
     
 | 
    private final ManifoldPoint[] pointCache = new ManifoldPoint[MANIFOLD_CACHE_SIZE]; 
 | 
    /// this two body pointers can point to the physics rigidbody class. 
 | 
    /// void* will allow any rigidbody class 
 | 
    private Object body0; 
 | 
    private Object body1; 
 | 
    private int cachedPoints; 
 | 
     
 | 
    public int index1a; 
 | 
     
 | 
    { 
 | 
        for (int i=0; i<pointCache.length; i++) pointCache[i] = new ManifoldPoint(); 
 | 
    } 
 | 
  
 | 
    public PersistentManifold() { 
 | 
    } 
 | 
  
 | 
    public PersistentManifold(Object body0, Object body1, int bla) { 
 | 
        init(body0, body1, bla); 
 | 
    } 
 | 
  
 | 
    public void init(Object body0, Object body1, int bla) { 
 | 
        this.body0 = body0; 
 | 
        this.body1 = body1; 
 | 
        cachedPoints = 0; 
 | 
        index1a = 0; 
 | 
    } 
 | 
  
 | 
    /// sort cached points so most isolated points come first 
 | 
    private int sortCachedPoints(ManifoldPoint pt) { 
 | 
        //calculate 4 possible cases areas, and take biggest area 
 | 
        //also need to keep 'deepest' 
 | 
  
 | 
        int maxPenetrationIndex = -1; 
 | 
//#define KEEP_DEEPEST_POINT 1 
 | 
//#ifdef KEEP_DEEPEST_POINT 
 | 
        float maxPenetration = pt.getDistance(); 
 | 
        for (int i = 0; i < 4; i++) { 
 | 
            if (pointCache[i].getDistance() < maxPenetration) { 
 | 
                maxPenetrationIndex = i; 
 | 
                maxPenetration = pointCache[i].getDistance(); 
 | 
            } 
 | 
        } 
 | 
//#endif //KEEP_DEEPEST_POINT 
 | 
  
 | 
        float res0 = 0f, res1 = 0f, res2 = 0f, res3 = 0f; 
 | 
        if (maxPenetrationIndex != 0) { 
 | 
            Vector3f a0 = (Vector3f) Stack.alloc(pt.localPointA); 
 | 
            a0.sub(pointCache[1].localPointA); 
 | 
  
 | 
            Vector3f b0 = (Vector3f) Stack.alloc(pointCache[3].localPointA); 
 | 
            b0.sub(pointCache[2].localPointA); 
 | 
  
 | 
            Vector3f cross = Stack.alloc(Vector3f.class); 
 | 
            cross.cross(a0, b0); 
 | 
  
 | 
            res0 = cross.lengthSquared(); 
 | 
        } 
 | 
  
 | 
        if (maxPenetrationIndex != 1) { 
 | 
            Vector3f a1 = (Vector3f) Stack.alloc(pt.localPointA); 
 | 
            a1.sub(pointCache[0].localPointA); 
 | 
  
 | 
            Vector3f b1 = (Vector3f) Stack.alloc(pointCache[3].localPointA); 
 | 
            b1.sub(pointCache[2].localPointA); 
 | 
  
 | 
            Vector3f cross = Stack.alloc(Vector3f.class); 
 | 
            cross.cross(a1, b1); 
 | 
            res1 = cross.lengthSquared(); 
 | 
        } 
 | 
  
 | 
        if (maxPenetrationIndex != 2) { 
 | 
            Vector3f a2 = (Vector3f) Stack.alloc(pt.localPointA); 
 | 
            a2.sub(pointCache[0].localPointA); 
 | 
  
 | 
            Vector3f b2 = (Vector3f) Stack.alloc(pointCache[3].localPointA); 
 | 
            b2.sub(pointCache[1].localPointA); 
 | 
  
 | 
            Vector3f cross = Stack.alloc(Vector3f.class); 
 | 
            cross.cross(a2, b2); 
 | 
  
 | 
            res2 = cross.lengthSquared(); 
 | 
        } 
 | 
  
 | 
        if (maxPenetrationIndex != 3) { 
 | 
            Vector3f a3 = (Vector3f) Stack.alloc(pt.localPointA); 
 | 
            a3.sub(pointCache[0].localPointA); 
 | 
  
 | 
            Vector3f b3 = (Vector3f) Stack.alloc(pointCache[2].localPointA); 
 | 
            b3.sub(pointCache[1].localPointA); 
 | 
  
 | 
            Vector3f cross = Stack.alloc(Vector3f.class); 
 | 
            cross.cross(a3, b3); 
 | 
            res3 = cross.lengthSquared(); 
 | 
        } 
 | 
  
 | 
        Vector4f maxvec = Stack.alloc(Vector4f.class); 
 | 
        maxvec.set(res0, res1, res2, res3); 
 | 
        int biggestarea = VectorUtil.closestAxis4(maxvec); 
 | 
        return biggestarea; 
 | 
    } 
 | 
  
 | 
    //private int findContactPoint(ManifoldPoint unUsed, int numUnused, ManifoldPoint pt); 
 | 
  
 | 
    public Object getBody0() { 
 | 
        return body0; 
 | 
    } 
 | 
  
 | 
    public Object getBody1() { 
 | 
        return body1; 
 | 
    } 
 | 
  
 | 
    public void setBodies(Object body0, Object body1) { 
 | 
        this.body0 = body0; 
 | 
        this.body1 = body1; 
 | 
    } 
 | 
     
 | 
    public void clearUserCache(ManifoldPoint pt) { 
 | 
        Object oldPtr = pt.userPersistentData; 
 | 
        if (oldPtr != null) { 
 | 
//#ifdef DEBUG_PERSISTENCY 
 | 
//            int i; 
 | 
//            int occurance = 0; 
 | 
//            for (i = 0; i < cachedPoints; i++) { 
 | 
//                if (pointCache[i].userPersistentData == oldPtr) { 
 | 
//                    occurance++; 
 | 
//                    if (occurance > 1) { 
 | 
//                        throw new InternalError(); 
 | 
//                    } 
 | 
//                } 
 | 
//            } 
 | 
//            assert (occurance <= 0); 
 | 
//#endif //DEBUG_PERSISTENCY 
 | 
  
 | 
            if (pt.userPersistentData != null && BulletGlobals.getContactDestroyedCallback() != null) { 
 | 
                BulletGlobals.getContactDestroyedCallback().contactDestroyed(pt.userPersistentData); 
 | 
                pt.userPersistentData = null; 
 | 
            } 
 | 
  
 | 
//#ifdef DEBUG_PERSISTENCY 
 | 
//            DebugPersistency(); 
 | 
//#endif 
 | 
        } 
 | 
    } 
 | 
  
 | 
    public int getNumContacts() { 
 | 
        return cachedPoints; 
 | 
    } 
 | 
  
 | 
    public ManifoldPoint getContactPoint(int index) { 
 | 
        return pointCache[index]; 
 | 
    } 
 | 
  
 | 
    // todo: get this margin from the current physics / collision environment 
 | 
    public float getContactBreakingThreshold() { 
 | 
        return BulletGlobals.getContactBreakingThreshold(); 
 | 
    } 
 | 
  
 | 
    public int getCacheEntry(ManifoldPoint newPoint) { 
 | 
        float shortestDist = getContactBreakingThreshold() * getContactBreakingThreshold(); 
 | 
        int size = getNumContacts(); 
 | 
        int nearestPoint = -1; 
 | 
        Vector3f diffA = Stack.alloc(Vector3f.class); 
 | 
        for (int i = 0; i < size; i++) { 
 | 
            ManifoldPoint mp = pointCache[i]; 
 | 
  
 | 
            diffA.sub(mp.localPointA, newPoint.localPointA); 
 | 
  
 | 
            float distToManiPoint = diffA.dot(diffA); 
 | 
            if (distToManiPoint < shortestDist) { 
 | 
                shortestDist = distToManiPoint; 
 | 
                nearestPoint = i; 
 | 
            } 
 | 
        } 
 | 
        return nearestPoint; 
 | 
    } 
 | 
  
 | 
    public int addManifoldPoint(ManifoldPoint newPoint) { 
 | 
        assert (validContactDistance(newPoint)); 
 | 
  
 | 
        int insertIndex = getNumContacts(); 
 | 
        if (insertIndex == MANIFOLD_CACHE_SIZE) { 
 | 
            //#if MANIFOLD_CACHE_SIZE >= 4 
 | 
            if (MANIFOLD_CACHE_SIZE >= 4) { 
 | 
                //sort cache so best points come first, based on area 
 | 
                insertIndex = sortCachedPoints(newPoint); 
 | 
            } 
 | 
            else { 
 | 
                //#else 
 | 
                insertIndex = 0; 
 | 
            } 
 | 
            //#endif 
 | 
             
 | 
            clearUserCache(pointCache[insertIndex]); 
 | 
        } 
 | 
        else { 
 | 
            cachedPoints++; 
 | 
        } 
 | 
        assert (pointCache[insertIndex].userPersistentData == null); 
 | 
        pointCache[insertIndex].set(newPoint); 
 | 
        return insertIndex; 
 | 
    } 
 | 
  
 | 
    public void removeContactPoint(int index) { 
 | 
        clearUserCache(pointCache[index]); 
 | 
  
 | 
        int lastUsedIndex = getNumContacts() - 1; 
 | 
//        m_pointCache[index] = m_pointCache[lastUsedIndex]; 
 | 
        if (index != lastUsedIndex) { 
 | 
            // TODO: possible bug 
 | 
            pointCache[index].set(pointCache[lastUsedIndex]); 
 | 
            //get rid of duplicated userPersistentData pointer 
 | 
            pointCache[lastUsedIndex].userPersistentData = null; 
 | 
            pointCache[lastUsedIndex].appliedImpulse = 0f; 
 | 
            pointCache[lastUsedIndex].lateralFrictionInitialized = false; 
 | 
            pointCache[lastUsedIndex].appliedImpulseLateral1 = 0f; 
 | 
            pointCache[lastUsedIndex].appliedImpulseLateral2 = 0f; 
 | 
            pointCache[lastUsedIndex].lifeTime = 0; 
 | 
        } 
 | 
  
 | 
        assert (pointCache[lastUsedIndex].userPersistentData == null); 
 | 
        cachedPoints--; 
 | 
    } 
 | 
  
 | 
    public void replaceContactPoint(ManifoldPoint newPoint, int insertIndex) { 
 | 
        assert (validContactDistance(newPoint)); 
 | 
  
 | 
//#define MAINTAIN_PERSISTENCY 1 
 | 
//#ifdef MAINTAIN_PERSISTENCY 
 | 
        int lifeTime = pointCache[insertIndex].getLifeTime(); 
 | 
        float appliedImpulse = pointCache[insertIndex].appliedImpulse; 
 | 
        float appliedLateralImpulse1 = pointCache[insertIndex].appliedImpulseLateral1; 
 | 
        float appliedLateralImpulse2 = pointCache[insertIndex].appliedImpulseLateral2; 
 | 
  
 | 
        assert (lifeTime >= 0); 
 | 
        Object cache = pointCache[insertIndex].userPersistentData; 
 | 
  
 | 
        pointCache[insertIndex].set(newPoint); 
 | 
  
 | 
        pointCache[insertIndex].userPersistentData = cache; 
 | 
        pointCache[insertIndex].appliedImpulse = appliedImpulse; 
 | 
        pointCache[insertIndex].appliedImpulseLateral1 = appliedLateralImpulse1; 
 | 
        pointCache[insertIndex].appliedImpulseLateral2 = appliedLateralImpulse2; 
 | 
  
 | 
        pointCache[insertIndex].lifeTime = lifeTime; 
 | 
//#else 
 | 
//        clearUserCache(m_pointCache[insertIndex]); 
 | 
//        m_pointCache[insertIndex] = newPoint; 
 | 
//#endif 
 | 
    } 
 | 
  
 | 
    private boolean validContactDistance(ManifoldPoint pt) { 
 | 
        return pt.distance1 <= getContactBreakingThreshold(); 
 | 
    } 
 | 
  
 | 
    /// calculated new worldspace coordinates and depth, and reject points that exceed the collision margin 
 | 
    public void refreshContactPoints(Transform trA, Transform trB) { 
 | 
        Vector3f tmp = Stack.alloc(Vector3f.class); 
 | 
        int i; 
 | 
//#ifdef DEBUG_PERSISTENCY 
 | 
//    printf("refreshContactPoints posA = (%f,%f,%f) posB = (%f,%f,%f)\n", 
 | 
//        trA.getOrigin().getX(), 
 | 
//        trA.getOrigin().getY(), 
 | 
//        trA.getOrigin().getZ(), 
 | 
//        trB.getOrigin().getX(), 
 | 
//        trB.getOrigin().getY(), 
 | 
//        trB.getOrigin().getZ()); 
 | 
//#endif //DEBUG_PERSISTENCY 
 | 
        // first refresh worldspace positions and distance 
 | 
        for (i = getNumContacts() - 1; i >= 0; i--) { 
 | 
            ManifoldPoint manifoldPoint = pointCache[i]; 
 | 
  
 | 
            manifoldPoint.positionWorldOnA.set(manifoldPoint.localPointA); 
 | 
            trA.transform(manifoldPoint.positionWorldOnA); 
 | 
  
 | 
            manifoldPoint.positionWorldOnB.set(manifoldPoint.localPointB); 
 | 
            trB.transform(manifoldPoint.positionWorldOnB); 
 | 
  
 | 
            tmp.set(manifoldPoint.positionWorldOnA); 
 | 
            tmp.sub(manifoldPoint.positionWorldOnB); 
 | 
            manifoldPoint.distance1 = tmp.dot(manifoldPoint.normalWorldOnB); 
 | 
  
 | 
            manifoldPoint.lifeTime++; 
 | 
        } 
 | 
  
 | 
        // then  
 | 
        float distance2d; 
 | 
        Vector3f projectedDifference = Stack.alloc(Vector3f.class), projectedPoint = Stack.alloc(Vector3f.class); 
 | 
  
 | 
        for (i = getNumContacts() - 1; i >= 0; i--) { 
 | 
  
 | 
            ManifoldPoint manifoldPoint = pointCache[i]; 
 | 
            // contact becomes invalid when signed distance exceeds margin (projected on contactnormal direction) 
 | 
            if (!validContactDistance(manifoldPoint)) { 
 | 
                removeContactPoint(i); 
 | 
            } 
 | 
            else { 
 | 
                // contact also becomes invalid when relative movement orthogonal to normal exceeds margin 
 | 
                tmp.scale(manifoldPoint.distance1, manifoldPoint.normalWorldOnB); 
 | 
                projectedPoint.sub(manifoldPoint.positionWorldOnA, tmp); 
 | 
                projectedDifference.sub(manifoldPoint.positionWorldOnB, projectedPoint); 
 | 
                distance2d = projectedDifference.dot(projectedDifference); 
 | 
                if (distance2d > getContactBreakingThreshold() * getContactBreakingThreshold()) { 
 | 
                    removeContactPoint(i); 
 | 
                } 
 | 
                else { 
 | 
                    // contact point processed callback 
 | 
                    if (BulletGlobals.getContactProcessedCallback() != null) { 
 | 
                        BulletGlobals.getContactProcessedCallback().contactProcessed(manifoldPoint, body0, body1); 
 | 
                    } 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
//#ifdef DEBUG_PERSISTENCY 
 | 
//    DebugPersistency(); 
 | 
//#endif // 
 | 
    } 
 | 
  
 | 
    public void clearManifold() { 
 | 
        int i; 
 | 
        for (i = 0; i < cachedPoints; i++) { 
 | 
            clearUserCache(pointCache[i]); 
 | 
        } 
 | 
        cachedPoints = 0; 
 | 
    } 
 | 
     
 | 
} 
 |