/* 
 | 
 * 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.dynamics.constraintsolver; 
 | 
  
 | 
import com.bulletphysics.BulletGlobals; 
 | 
import com.bulletphysics.util.ObjectPool; 
 | 
import com.bulletphysics.collision.narrowphase.ManifoldPoint; 
 | 
import com.bulletphysics.dynamics.RigidBody; 
 | 
import com.bulletphysics.linearmath.Transform; 
 | 
import cz.advel.stack.Stack; 
 | 
import javax.vecmath.Matrix3f; 
 | 
import javax.vecmath.Vector3f; 
 | 
  
 | 
/** 
 | 
 * Functions for resolving contacts. 
 | 
 *  
 | 
 * @author jezek2 
 | 
 */ 
 | 
public class ContactConstraint { 
 | 
     
 | 
    public static final ContactSolverFunc resolveSingleCollision = new ContactSolverFunc() { 
 | 
        public float resolveContact(RigidBody body1, RigidBody body2, ManifoldPoint contactPoint, ContactSolverInfo info) { 
 | 
            return resolveSingleCollision(body1, body2, contactPoint, info); 
 | 
        } 
 | 
    }; 
 | 
  
 | 
    public static final ContactSolverFunc resolveSingleFriction = new ContactSolverFunc() { 
 | 
        public float resolveContact(RigidBody body1, RigidBody body2, ManifoldPoint contactPoint, ContactSolverInfo info) { 
 | 
            return resolveSingleFriction(body1, body2, contactPoint, info); 
 | 
        } 
 | 
    }; 
 | 
  
 | 
    public static final ContactSolverFunc resolveSingleCollisionCombined = new ContactSolverFunc() { 
 | 
        public float resolveContact(RigidBody body1, RigidBody body2, ManifoldPoint contactPoint, ContactSolverInfo info) { 
 | 
            return resolveSingleCollisionCombined(body1, body2, contactPoint, info); 
 | 
        } 
 | 
    }; 
 | 
     
 | 
    /** 
 | 
     * Bilateral constraint between two dynamic objects. 
 | 
     */ 
 | 
    public static void resolveSingleBilateral(RigidBody body1, Vector3f pos1, 
 | 
            RigidBody body2, Vector3f pos2, 
 | 
            float distance, Vector3f normal, float[] impulse, float timeStep) { 
 | 
        float normalLenSqr = normal.lengthSquared(); 
 | 
        assert (Math.abs(normalLenSqr) < 1.1f); 
 | 
        if (normalLenSqr > 1.1f) { 
 | 
            impulse[0] = 0f; 
 | 
            return; 
 | 
        } 
 | 
  
 | 
        ObjectPool<JacobianEntry> jacobiansPool = ObjectPool.get(JacobianEntry.class); 
 | 
        Vector3f tmp = Stack.alloc(Vector3f.class); 
 | 
         
 | 
        Vector3f rel_pos1 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos1.sub(pos1, body1.getCenterOfMassPosition(tmp)); 
 | 
  
 | 
        Vector3f rel_pos2 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos2.sub(pos2, body2.getCenterOfMassPosition(tmp)); 
 | 
  
 | 
        //this jacobian entry could be re-used for all iterations 
 | 
  
 | 
        Vector3f vel1 = Stack.alloc(Vector3f.class); 
 | 
        body1.getVelocityInLocalPoint(rel_pos1, vel1); 
 | 
  
 | 
        Vector3f vel2 = Stack.alloc(Vector3f.class); 
 | 
        body2.getVelocityInLocalPoint(rel_pos2, vel2); 
 | 
  
 | 
        Vector3f vel = Stack.alloc(Vector3f.class); 
 | 
        vel.sub(vel1, vel2); 
 | 
  
 | 
        Matrix3f mat1 = body1.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis; 
 | 
        mat1.transpose(); 
 | 
  
 | 
        Matrix3f mat2 = body2.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis; 
 | 
        mat2.transpose(); 
 | 
  
 | 
        JacobianEntry jac = jacobiansPool.get(); 
 | 
        jac.init(mat1, mat2, 
 | 
                rel_pos1, rel_pos2, normal, 
 | 
                body1.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)), body1.getInvMass(), 
 | 
                body2.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)), body2.getInvMass()); 
 | 
  
 | 
        float jacDiagAB = jac.getDiagonal(); 
 | 
        float jacDiagABInv = 1f / jacDiagAB; 
 | 
  
 | 
        Vector3f tmp1 = body1.getAngularVelocity(Stack.alloc(Vector3f.class)); 
 | 
        mat1.transform(tmp1); 
 | 
  
 | 
        Vector3f tmp2 = body2.getAngularVelocity(Stack.alloc(Vector3f.class)); 
 | 
        mat2.transform(tmp2); 
 | 
  
 | 
        float rel_vel = jac.getRelativeVelocity( 
 | 
                body1.getLinearVelocity(Stack.alloc(Vector3f.class)), 
 | 
                tmp1, 
 | 
                body2.getLinearVelocity(Stack.alloc(Vector3f.class)), 
 | 
                tmp2); 
 | 
  
 | 
        jacobiansPool.release(jac); 
 | 
  
 | 
        float a; 
 | 
        a = jacDiagABInv; 
 | 
  
 | 
  
 | 
        rel_vel = normal.dot(vel); 
 | 
  
 | 
        // todo: move this into proper structure 
 | 
        float contactDamping = 0.2f; 
 | 
  
 | 
        //#ifdef ONLY_USE_LINEAR_MASS 
 | 
        //    btScalar massTerm = btScalar(1.) / (body1.getInvMass() + body2.getInvMass()); 
 | 
        //    impulse = - contactDamping * rel_vel * massTerm; 
 | 
        //#else     
 | 
        float velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; 
 | 
        impulse[0] = velocityImpulse; 
 | 
        //#endif 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Response between two dynamic objects with friction. 
 | 
     */ 
 | 
    public static float resolveSingleCollision( 
 | 
            RigidBody body1, 
 | 
            RigidBody body2, 
 | 
            ManifoldPoint contactPoint, 
 | 
            ContactSolverInfo solverInfo) { 
 | 
         
 | 
        Vector3f tmpVec = Stack.alloc(Vector3f.class); 
 | 
  
 | 
        Vector3f pos1_ = contactPoint.getPositionWorldOnA(Stack.alloc(Vector3f.class)); 
 | 
        Vector3f pos2_ = contactPoint.getPositionWorldOnB(Stack.alloc(Vector3f.class)); 
 | 
        Vector3f normal = contactPoint.normalWorldOnB; 
 | 
  
 | 
        // constant over all iterations 
 | 
        Vector3f rel_pos1 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos1.sub(pos1_, body1.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        Vector3f rel_pos2 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos2.sub(pos2_, body2.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        Vector3f vel1 = body1.getVelocityInLocalPoint(rel_pos1, Stack.alloc(Vector3f.class)); 
 | 
        Vector3f vel2 = body2.getVelocityInLocalPoint(rel_pos2, Stack.alloc(Vector3f.class)); 
 | 
        Vector3f vel = Stack.alloc(Vector3f.class); 
 | 
        vel.sub(vel1, vel2); 
 | 
  
 | 
        float rel_vel; 
 | 
        rel_vel = normal.dot(vel); 
 | 
  
 | 
        float Kfps = 1f / solverInfo.timeStep; 
 | 
  
 | 
        // btScalar damping = solverInfo.m_damping ; 
 | 
        float Kerp = solverInfo.erp; 
 | 
        float Kcor = Kerp * Kfps; 
 | 
  
 | 
        ConstraintPersistentData cpd = (ConstraintPersistentData) contactPoint.userPersistentData; 
 | 
        assert (cpd != null); 
 | 
        float distance = cpd.penetration; 
 | 
        float positionalError = Kcor * -distance; 
 | 
        float velocityError = cpd.restitution - rel_vel; // * damping; 
 | 
  
 | 
        float penetrationImpulse = positionalError * cpd.jacDiagABInv; 
 | 
  
 | 
        float velocityImpulse = velocityError * cpd.jacDiagABInv; 
 | 
  
 | 
        float normalImpulse = penetrationImpulse + velocityImpulse; 
 | 
  
 | 
        // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse 
 | 
        float oldNormalImpulse = cpd.appliedImpulse; 
 | 
        float sum = oldNormalImpulse + normalImpulse; 
 | 
        cpd.appliedImpulse = 0f > sum ? 0f : sum; 
 | 
  
 | 
        normalImpulse = cpd.appliedImpulse - oldNormalImpulse; 
 | 
  
 | 
        //#ifdef USE_INTERNAL_APPLY_IMPULSE 
 | 
        Vector3f tmp = Stack.alloc(Vector3f.class); 
 | 
        if (body1.getInvMass() != 0f) { 
 | 
            tmp.scale(body1.getInvMass(), contactPoint.normalWorldOnB); 
 | 
            body1.internalApplyImpulse(tmp, cpd.angularComponentA, normalImpulse); 
 | 
        } 
 | 
        if (body2.getInvMass() != 0f) { 
 | 
            tmp.scale(body2.getInvMass(), contactPoint.normalWorldOnB); 
 | 
            body2.internalApplyImpulse(tmp, cpd.angularComponentB, -normalImpulse); 
 | 
        } 
 | 
        //#else //USE_INTERNAL_APPLY_IMPULSE 
 | 
        //    body1.applyImpulse(normal*(normalImpulse), rel_pos1); 
 | 
        //    body2.applyImpulse(-normal*(normalImpulse), rel_pos2); 
 | 
        //#endif //USE_INTERNAL_APPLY_IMPULSE 
 | 
  
 | 
        return normalImpulse; 
 | 
    } 
 | 
     
 | 
    public static float resolveSingleFriction( 
 | 
            RigidBody body1, 
 | 
            RigidBody body2, 
 | 
            ManifoldPoint contactPoint, 
 | 
            ContactSolverInfo solverInfo) { 
 | 
         
 | 
        Vector3f tmpVec = Stack.alloc(Vector3f.class); 
 | 
         
 | 
        Vector3f pos1 = contactPoint.getPositionWorldOnA(Stack.alloc(Vector3f.class)); 
 | 
        Vector3f pos2 = contactPoint.getPositionWorldOnB(Stack.alloc(Vector3f.class)); 
 | 
  
 | 
        Vector3f rel_pos1 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos1.sub(pos1, body1.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        Vector3f rel_pos2 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos2.sub(pos2, body2.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        ConstraintPersistentData cpd = (ConstraintPersistentData) contactPoint.userPersistentData; 
 | 
        assert (cpd != null); 
 | 
  
 | 
        float combinedFriction = cpd.friction; 
 | 
  
 | 
        float limit = cpd.appliedImpulse * combinedFriction; 
 | 
  
 | 
        if (cpd.appliedImpulse > 0f) //friction 
 | 
        { 
 | 
            //apply friction in the 2 tangential directions 
 | 
  
 | 
            // 1st tangent 
 | 
            Vector3f vel1 = Stack.alloc(Vector3f.class); 
 | 
            body1.getVelocityInLocalPoint(rel_pos1, vel1); 
 | 
  
 | 
            Vector3f vel2 = Stack.alloc(Vector3f.class); 
 | 
            body2.getVelocityInLocalPoint(rel_pos2, vel2); 
 | 
  
 | 
            Vector3f vel = Stack.alloc(Vector3f.class); 
 | 
            vel.sub(vel1, vel2); 
 | 
  
 | 
            float j1, j2; 
 | 
  
 | 
            { 
 | 
                float vrel = cpd.frictionWorldTangential0.dot(vel); 
 | 
  
 | 
                // calculate j that moves us to zero relative velocity 
 | 
                j1 = -vrel * cpd.jacDiagABInvTangent0; 
 | 
                float oldTangentImpulse = cpd.accumulatedTangentImpulse0; 
 | 
                cpd.accumulatedTangentImpulse0 = oldTangentImpulse + j1; 
 | 
  
 | 
                cpd.accumulatedTangentImpulse0 = Math.min(cpd.accumulatedTangentImpulse0, limit); 
 | 
                cpd.accumulatedTangentImpulse0 = Math.max(cpd.accumulatedTangentImpulse0, -limit); 
 | 
                j1 = cpd.accumulatedTangentImpulse0 - oldTangentImpulse; 
 | 
            } 
 | 
            { 
 | 
                // 2nd tangent 
 | 
  
 | 
                float vrel = cpd.frictionWorldTangential1.dot(vel); 
 | 
  
 | 
                // calculate j that moves us to zero relative velocity 
 | 
                j2 = -vrel * cpd.jacDiagABInvTangent1; 
 | 
                float oldTangentImpulse = cpd.accumulatedTangentImpulse1; 
 | 
                cpd.accumulatedTangentImpulse1 = oldTangentImpulse + j2; 
 | 
  
 | 
                cpd.accumulatedTangentImpulse1 = Math.min(cpd.accumulatedTangentImpulse1, limit); 
 | 
                cpd.accumulatedTangentImpulse1 = Math.max(cpd.accumulatedTangentImpulse1, -limit); 
 | 
                j2 = cpd.accumulatedTangentImpulse1 - oldTangentImpulse; 
 | 
            } 
 | 
  
 | 
            //#ifdef USE_INTERNAL_APPLY_IMPULSE 
 | 
            Vector3f tmp = Stack.alloc(Vector3f.class); 
 | 
  
 | 
            if (body1.getInvMass() != 0f) { 
 | 
                tmp.scale(body1.getInvMass(), cpd.frictionWorldTangential0); 
 | 
                body1.internalApplyImpulse(tmp, cpd.frictionAngularComponent0A, j1); 
 | 
  
 | 
                tmp.scale(body1.getInvMass(), cpd.frictionWorldTangential1); 
 | 
                body1.internalApplyImpulse(tmp, cpd.frictionAngularComponent1A, j2); 
 | 
            } 
 | 
            if (body2.getInvMass() != 0f) { 
 | 
                tmp.scale(body2.getInvMass(), cpd.frictionWorldTangential0); 
 | 
                body2.internalApplyImpulse(tmp, cpd.frictionAngularComponent0B, -j1); 
 | 
  
 | 
                tmp.scale(body2.getInvMass(), cpd.frictionWorldTangential1); 
 | 
                body2.internalApplyImpulse(tmp, cpd.frictionAngularComponent1B, -j2); 
 | 
            } 
 | 
            //#else //USE_INTERNAL_APPLY_IMPULSE 
 | 
            //    body1.applyImpulse((j1 * cpd->m_frictionWorldTangential0)+(j2 * cpd->m_frictionWorldTangential1), rel_pos1); 
 | 
            //    body2.applyImpulse((j1 * -cpd->m_frictionWorldTangential0)+(j2 * -cpd->m_frictionWorldTangential1), rel_pos2); 
 | 
            //#endif //USE_INTERNAL_APPLY_IMPULSE 
 | 
        } 
 | 
        return cpd.appliedImpulse; 
 | 
    } 
 | 
     
 | 
    /** 
 | 
     * velocity + friction<br> 
 | 
     * response between two dynamic objects with friction 
 | 
     */ 
 | 
    public static float resolveSingleCollisionCombined( 
 | 
            RigidBody body1, 
 | 
            RigidBody body2, 
 | 
            ManifoldPoint contactPoint, 
 | 
            ContactSolverInfo solverInfo) { 
 | 
         
 | 
        Vector3f tmpVec = Stack.alloc(Vector3f.class); 
 | 
         
 | 
        Vector3f pos1 = contactPoint.getPositionWorldOnA(Stack.alloc(Vector3f.class)); 
 | 
        Vector3f pos2 = contactPoint.getPositionWorldOnB(Stack.alloc(Vector3f.class)); 
 | 
        Vector3f normal = contactPoint.normalWorldOnB; 
 | 
  
 | 
        Vector3f rel_pos1 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos1.sub(pos1, body1.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        Vector3f rel_pos2 = Stack.alloc(Vector3f.class); 
 | 
        rel_pos2.sub(pos2, body2.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        Vector3f vel1 = body1.getVelocityInLocalPoint(rel_pos1, Stack.alloc(Vector3f.class)); 
 | 
        Vector3f vel2 = body2.getVelocityInLocalPoint(rel_pos2, Stack.alloc(Vector3f.class)); 
 | 
        Vector3f vel = Stack.alloc(Vector3f.class); 
 | 
        vel.sub(vel1, vel2); 
 | 
  
 | 
        float rel_vel; 
 | 
        rel_vel = normal.dot(vel); 
 | 
  
 | 
        float Kfps = 1f / solverInfo.timeStep; 
 | 
  
 | 
        //btScalar damping = solverInfo.m_damping ; 
 | 
        float Kerp = solverInfo.erp; 
 | 
        float Kcor = Kerp * Kfps; 
 | 
  
 | 
        ConstraintPersistentData cpd = (ConstraintPersistentData) contactPoint.userPersistentData; 
 | 
        assert (cpd != null); 
 | 
        float distance = cpd.penetration; 
 | 
        float positionalError = Kcor * -distance; 
 | 
        float velocityError = cpd.restitution - rel_vel;// * damping; 
 | 
  
 | 
        float penetrationImpulse = positionalError * cpd.jacDiagABInv; 
 | 
  
 | 
        float velocityImpulse = velocityError * cpd.jacDiagABInv; 
 | 
  
 | 
        float normalImpulse = penetrationImpulse + velocityImpulse; 
 | 
  
 | 
        // See Erin Catto's GDC 2006 paper: Clamp the accumulated impulse 
 | 
        float oldNormalImpulse = cpd.appliedImpulse; 
 | 
        float sum = oldNormalImpulse + normalImpulse; 
 | 
        cpd.appliedImpulse = 0f > sum ? 0f : sum; 
 | 
  
 | 
        normalImpulse = cpd.appliedImpulse - oldNormalImpulse; 
 | 
  
 | 
  
 | 
        //#ifdef USE_INTERNAL_APPLY_IMPULSE 
 | 
        Vector3f tmp = Stack.alloc(Vector3f.class); 
 | 
        if (body1.getInvMass() != 0f) { 
 | 
            tmp.scale(body1.getInvMass(), contactPoint.normalWorldOnB); 
 | 
            body1.internalApplyImpulse(tmp, cpd.angularComponentA, normalImpulse); 
 | 
        } 
 | 
        if (body2.getInvMass() != 0f) { 
 | 
            tmp.scale(body2.getInvMass(), contactPoint.normalWorldOnB); 
 | 
            body2.internalApplyImpulse(tmp, cpd.angularComponentB, -normalImpulse); 
 | 
        } 
 | 
        //#else //USE_INTERNAL_APPLY_IMPULSE 
 | 
        //    body1.applyImpulse(normal*(normalImpulse), rel_pos1); 
 | 
        //    body2.applyImpulse(-normal*(normalImpulse), rel_pos2); 
 | 
        //#endif //USE_INTERNAL_APPLY_IMPULSE 
 | 
  
 | 
        { 
 | 
            //friction 
 | 
            body1.getVelocityInLocalPoint(rel_pos1, vel1); 
 | 
            body2.getVelocityInLocalPoint(rel_pos2, vel2); 
 | 
            vel.sub(vel1, vel2); 
 | 
  
 | 
            rel_vel = normal.dot(vel); 
 | 
  
 | 
            tmp.scale(rel_vel, normal); 
 | 
            Vector3f lat_vel = Stack.alloc(Vector3f.class); 
 | 
            lat_vel.sub(vel, tmp); 
 | 
            float lat_rel_vel = lat_vel.length(); 
 | 
  
 | 
            float combinedFriction = cpd.friction; 
 | 
  
 | 
            if (cpd.appliedImpulse > 0) { 
 | 
                if (lat_rel_vel > BulletGlobals.FLT_EPSILON) { 
 | 
                    lat_vel.scale(1f / lat_rel_vel); 
 | 
  
 | 
                    Vector3f temp1 = Stack.alloc(Vector3f.class); 
 | 
                    temp1.cross(rel_pos1, lat_vel); 
 | 
                    body1.getInvInertiaTensorWorld(Stack.alloc(Matrix3f.class)).transform(temp1); 
 | 
  
 | 
                    Vector3f temp2 = Stack.alloc(Vector3f.class); 
 | 
                    temp2.cross(rel_pos2, lat_vel); 
 | 
                    body2.getInvInertiaTensorWorld(Stack.alloc(Matrix3f.class)).transform(temp2); 
 | 
  
 | 
                    Vector3f java_tmp1 = Stack.alloc(Vector3f.class); 
 | 
                    java_tmp1.cross(temp1, rel_pos1); 
 | 
  
 | 
                    Vector3f java_tmp2 = Stack.alloc(Vector3f.class); 
 | 
                    java_tmp2.cross(temp2, rel_pos2); 
 | 
  
 | 
                    tmp.add(java_tmp1, java_tmp2); 
 | 
  
 | 
                    float friction_impulse = lat_rel_vel / 
 | 
                            (body1.getInvMass() + body2.getInvMass() + lat_vel.dot(tmp)); 
 | 
                    float normal_impulse = cpd.appliedImpulse * combinedFriction; 
 | 
  
 | 
                    friction_impulse = Math.min(friction_impulse, normal_impulse); 
 | 
                    friction_impulse = Math.max(friction_impulse, -normal_impulse); 
 | 
  
 | 
                    tmp.scale(-friction_impulse, lat_vel); 
 | 
                    body1.applyImpulse(tmp, rel_pos1); 
 | 
  
 | 
                    tmp.scale(friction_impulse, lat_vel); 
 | 
                    body2.applyImpulse(tmp, rel_pos2); 
 | 
                } 
 | 
            } 
 | 
        } 
 | 
  
 | 
        return normalImpulse; 
 | 
    } 
 | 
  
 | 
    public static float resolveSingleFrictionEmpty( 
 | 
            RigidBody body1, 
 | 
            RigidBody body2, 
 | 
            ManifoldPoint contactPoint, 
 | 
            ContactSolverInfo solverInfo) { 
 | 
        return 0f; 
 | 
    } 
 | 
     
 | 
} 
 |