/* 
 | 
 * 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. 
 | 
 */ 
 | 
  
 | 
/* 
 | 
2007-09-09 
 | 
btGeneric6DofConstraint Refactored by Francisco Le�n 
 | 
email: projectileman@yahoo.com 
 | 
http://gimpact.sf.net 
 | 
*/ 
 | 
  
 | 
package com.bulletphysics.dynamics.constraintsolver; 
 | 
  
 | 
import com.bulletphysics.BulletGlobals; 
 | 
import com.bulletphysics.dynamics.RigidBody; 
 | 
import com.bulletphysics.linearmath.MatrixUtil; 
 | 
import com.bulletphysics.linearmath.Transform; 
 | 
  
 | 
  
 | 
///  
 | 
import com.bulletphysics.linearmath.VectorUtil; 
 | 
import cz.advel.stack.Stack; 
 | 
import javax.vecmath.Matrix3f; 
 | 
import javax.vecmath.Vector3f; 
 | 
/*! 
 | 
  
 | 
*/ 
 | 
/** 
 | 
 * Generic6DofConstraint between two rigidbodies each with a pivot point that descibes 
 | 
 * the axis location in local space.<p> 
 | 
 *  
 | 
 * Generic6DofConstraint can leave any of the 6 degree of freedom "free" or "locked". 
 | 
 * Currently this limit supports rotational motors.<br> 
 | 
 *  
 | 
 * <ul> 
 | 
 * <li>For linear limits, use {@link #setLinearUpperLimit}, {@link #setLinearLowerLimit}. 
 | 
 * You can set the parameters with the {@link TranslationalLimitMotor} structure accsesible 
 | 
 * through the {@link #getTranslationalLimitMotor} method. 
 | 
 * At this moment translational motors are not supported. May be in the future.</li> 
 | 
 *  
 | 
 * <li>For angular limits, use the {@link RotationalLimitMotor} structure for configuring 
 | 
 * the limit. This is accessible through {@link #getRotationalLimitMotor} method, 
 | 
 * this brings support for limit parameters and motors.</li> 
 | 
 *  
 | 
 * <li>Angulars limits have these possible ranges: 
 | 
 * <table border="1"> 
 | 
 * <tr> 
 | 
 *     <td><b>AXIS</b></td> 
 | 
 *     <td><b>MIN ANGLE</b></td> 
 | 
 *     <td><b>MAX ANGLE</b></td> 
 | 
 * </tr><tr> 
 | 
 *     <td>X</td> 
 | 
 *         <td>-PI</td> 
 | 
 *         <td>PI</td> 
 | 
 * </tr><tr> 
 | 
 *     <td>Y</td> 
 | 
 *         <td>-PI/2</td> 
 | 
 *         <td>PI/2</td> 
 | 
 * </tr><tr> 
 | 
 *     <td>Z</td> 
 | 
 *         <td>-PI/2</td> 
 | 
 *         <td>PI/2</td> 
 | 
 * </tr> 
 | 
 * </table> 
 | 
 * </li> 
 | 
 * </ul> 
 | 
 *  
 | 
 * @author jezek2 
 | 
 */ 
 | 
public class Generic6DofConstraint extends TypedConstraint { 
 | 
  
 | 
    protected final Transform frameInA = new Transform(); //!< the constraint space w.r.t body A 
 | 
    protected final Transform frameInB = new Transform(); //!< the constraint space w.r.t body B 
 | 
  
 | 
    protected final JacobianEntry[] jacLinear/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; //!< 3 orthogonal linear constraints 
 | 
    protected final JacobianEntry[] jacAng/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; //!< 3 orthogonal angular constraints 
 | 
  
 | 
    protected final TranslationalLimitMotor linearLimits = new TranslationalLimitMotor(); 
 | 
  
 | 
    protected final RotationalLimitMotor[] angularLimits/*[3]*/ = new RotationalLimitMotor[] { new RotationalLimitMotor(), new RotationalLimitMotor(), new RotationalLimitMotor() }; 
 | 
  
 | 
    protected float timeStep; 
 | 
    protected final Transform calculatedTransformA = new Transform(); 
 | 
    protected final Transform calculatedTransformB = new Transform(); 
 | 
    protected final Vector3f calculatedAxisAngleDiff = new Vector3f(); 
 | 
    protected final Vector3f[] calculatedAxis/*[3]*/ = new Vector3f[] { new Vector3f(), new Vector3f(), new Vector3f() }; 
 | 
     
 | 
    protected final Vector3f anchorPos = new Vector3f(); // point betwen pivots of bodies A and B to solve linear axes 
 | 
     
 | 
    protected boolean useLinearReferenceFrameA; 
 | 
  
 | 
    public Generic6DofConstraint() { 
 | 
        super(TypedConstraintType.D6_CONSTRAINT_TYPE); 
 | 
        useLinearReferenceFrameA = true; 
 | 
    } 
 | 
  
 | 
    public Generic6DofConstraint(RigidBody rbA, RigidBody rbB, Transform frameInA, Transform frameInB, boolean useLinearReferenceFrameA) { 
 | 
        super(TypedConstraintType.D6_CONSTRAINT_TYPE, rbA, rbB); 
 | 
        this.frameInA.set(frameInA); 
 | 
        this.frameInB.set(frameInB); 
 | 
        this.useLinearReferenceFrameA = useLinearReferenceFrameA; 
 | 
    } 
 | 
  
 | 
    private static float getMatrixElem(Matrix3f mat, int index) { 
 | 
        int i = index % 3; 
 | 
        int j = index / 3; 
 | 
        return mat.getElement(i, j); 
 | 
    } 
 | 
     
 | 
    /** 
 | 
     * MatrixToEulerXYZ from http://www.geometrictools.com/LibFoundation/Mathematics/Wm4Matrix3.inl.html 
 | 
     */ 
 | 
    private static boolean matrixToEulerXYZ(Matrix3f mat, Vector3f xyz) { 
 | 
        //    // rot =  cy*cz          -cy*sz           sy 
 | 
        //    //        cz*sx*sy+cx*sz  cx*cz-sx*sy*sz -cy*sx 
 | 
        //    //       -cx*cz*sy+sx*sz  cz*sx+cx*sy*sz  cx*cy 
 | 
        // 
 | 
  
 | 
        if (getMatrixElem(mat, 2) < 1.0f) { 
 | 
            if (getMatrixElem(mat, 2) > -1.0f) { 
 | 
                xyz.x = (float) Math.atan2(-getMatrixElem(mat, 5), getMatrixElem(mat, 8)); 
 | 
                xyz.y = (float) Math.asin(getMatrixElem(mat, 2)); 
 | 
                xyz.z = (float) Math.atan2(-getMatrixElem(mat, 1), getMatrixElem(mat, 0)); 
 | 
                return true; 
 | 
            } 
 | 
            else { 
 | 
                // WARNING.  Not unique.  XA - ZA = -atan2(r10,r11) 
 | 
                xyz.x = -(float) Math.atan2(getMatrixElem(mat, 3), getMatrixElem(mat, 4)); 
 | 
                xyz.y = -BulletGlobals.SIMD_HALF_PI; 
 | 
                xyz.z = 0.0f; 
 | 
                return false; 
 | 
            } 
 | 
        } 
 | 
        else { 
 | 
            // WARNING.  Not unique.  XAngle + ZAngle = atan2(r10,r11) 
 | 
            xyz.x = (float) Math.atan2(getMatrixElem(mat, 3), getMatrixElem(mat, 4)); 
 | 
            xyz.y = BulletGlobals.SIMD_HALF_PI; 
 | 
            xyz.z = 0.0f; 
 | 
        } 
 | 
  
 | 
        return false; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Calcs the euler angles between the two bodies. 
 | 
     */ 
 | 
    protected void calculateAngleInfo() { 
 | 
        Matrix3f mat = Stack.alloc(Matrix3f.class); 
 | 
  
 | 
        Matrix3f relative_frame = Stack.alloc(Matrix3f.class); 
 | 
        mat.set(calculatedTransformA.basis); 
 | 
        MatrixUtil.invert(mat); 
 | 
        relative_frame.mul(mat, calculatedTransformB.basis); 
 | 
  
 | 
        matrixToEulerXYZ(relative_frame, calculatedAxisAngleDiff); 
 | 
  
 | 
        // in euler angle mode we do not actually constrain the angular velocity 
 | 
        // along the axes axis[0] and axis[2] (although we do use axis[1]) : 
 | 
        // 
 | 
        //    to get            constrain w2-w1 along        ...not 
 | 
        //    ------            ---------------------        ------ 
 | 
        //    d(angle[0])/dt = 0    ax[1] x ax[2]            ax[0] 
 | 
        //    d(angle[1])/dt = 0    ax[1] 
 | 
        //    d(angle[2])/dt = 0    ax[0] x ax[1]            ax[2] 
 | 
        // 
 | 
        // constraining w2-w1 along an axis 'a' means that a'*(w2-w1)=0. 
 | 
        // to prove the result for angle[0], write the expression for angle[0] from 
 | 
        // GetInfo1 then take the derivative. to prove this for angle[2] it is 
 | 
        // easier to take the euler rate expression for d(angle[2])/dt with respect 
 | 
        // to the components of w and set that to 0. 
 | 
  
 | 
        Vector3f axis0 = Stack.alloc(Vector3f.class); 
 | 
        calculatedTransformB.basis.getColumn(0, axis0); 
 | 
  
 | 
        Vector3f axis2 = Stack.alloc(Vector3f.class); 
 | 
        calculatedTransformA.basis.getColumn(2, axis2); 
 | 
  
 | 
        calculatedAxis[1].cross(axis2, axis0); 
 | 
        calculatedAxis[0].cross(calculatedAxis[1], axis2); 
 | 
        calculatedAxis[2].cross(axis0, calculatedAxis[1]); 
 | 
  
 | 
        //    if(m_debugDrawer) 
 | 
        //    { 
 | 
        // 
 | 
        //        char buff[300]; 
 | 
        //        sprintf(buff,"\n X: %.2f ; Y: %.2f ; Z: %.2f ", 
 | 
        //        m_calculatedAxisAngleDiff[0], 
 | 
        //        m_calculatedAxisAngleDiff[1], 
 | 
        //        m_calculatedAxisAngleDiff[2]); 
 | 
        //        m_debugDrawer->reportErrorWarning(buff); 
 | 
        //    } 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Calcs global transform of the offsets.<p> 
 | 
     * Calcs the global transform for the joint offset for body A an B, and also calcs the agle differences between the bodies. 
 | 
     *  
 | 
     * See also: Generic6DofConstraint.getCalculatedTransformA, Generic6DofConstraint.getCalculatedTransformB, Generic6DofConstraint.calculateAngleInfo 
 | 
     */ 
 | 
    public void calculateTransforms() { 
 | 
        rbA.getCenterOfMassTransform(calculatedTransformA); 
 | 
        calculatedTransformA.mul(frameInA); 
 | 
  
 | 
        rbB.getCenterOfMassTransform(calculatedTransformB); 
 | 
        calculatedTransformB.mul(frameInB); 
 | 
  
 | 
        calculateAngleInfo(); 
 | 
    } 
 | 
     
 | 
    protected void buildLinearJacobian(/*JacobianEntry jacLinear*/int jacLinear_index, Vector3f normalWorld, Vector3f pivotAInW, Vector3f pivotBInW) { 
 | 
        Matrix3f mat1 = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis; 
 | 
        mat1.transpose(); 
 | 
  
 | 
        Matrix3f mat2 = rbB.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis; 
 | 
        mat2.transpose(); 
 | 
  
 | 
        Vector3f tmpVec = Stack.alloc(Vector3f.class); 
 | 
         
 | 
        Vector3f tmp1 = Stack.alloc(Vector3f.class); 
 | 
        tmp1.sub(pivotAInW, rbA.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        Vector3f tmp2 = Stack.alloc(Vector3f.class); 
 | 
        tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition(tmpVec)); 
 | 
  
 | 
        jacLinear[jacLinear_index].init( 
 | 
                mat1, 
 | 
                mat2, 
 | 
                tmp1, 
 | 
                tmp2, 
 | 
                normalWorld, 
 | 
                rbA.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)), 
 | 
                rbA.getInvMass(), 
 | 
                rbB.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)), 
 | 
                rbB.getInvMass()); 
 | 
    } 
 | 
  
 | 
    protected void buildAngularJacobian(/*JacobianEntry jacAngular*/int jacAngular_index, Vector3f jointAxisW) { 
 | 
        Matrix3f mat1 = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis; 
 | 
        mat1.transpose(); 
 | 
  
 | 
        Matrix3f mat2 = rbB.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis; 
 | 
        mat2.transpose(); 
 | 
  
 | 
        jacAng[jacAngular_index].init(jointAxisW, 
 | 
                mat1, 
 | 
                mat2, 
 | 
                rbA.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)), 
 | 
                rbB.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class))); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Test angular limit.<p> 
 | 
     * Calculates angular correction and returns true if limit needs to be corrected. 
 | 
     * Generic6DofConstraint.buildJacobian must be called previously. 
 | 
     */ 
 | 
    public boolean testAngularLimitMotor(int axis_index) { 
 | 
        float angle = VectorUtil.getCoord(calculatedAxisAngleDiff, axis_index); 
 | 
  
 | 
        // test limits 
 | 
        angularLimits[axis_index].testLimitValue(angle); 
 | 
        return angularLimits[axis_index].needApplyTorques(); 
 | 
    } 
 | 
     
 | 
    @Override 
 | 
    public void buildJacobian() { 
 | 
        // Clear accumulated impulses for the next simulation step 
 | 
        linearLimits.accumulatedImpulse.set(0f, 0f, 0f); 
 | 
        for (int i=0; i<3; i++) { 
 | 
            angularLimits[i].accumulatedImpulse = 0f; 
 | 
        } 
 | 
         
 | 
        // calculates transform 
 | 
        calculateTransforms(); 
 | 
         
 | 
        Vector3f tmpVec = Stack.alloc(Vector3f.class); 
 | 
  
 | 
        //  const btVector3& pivotAInW = m_calculatedTransformA.getOrigin(); 
 | 
        //  const btVector3& pivotBInW = m_calculatedTransformB.getOrigin(); 
 | 
        calcAnchorPos(); 
 | 
        Vector3f pivotAInW = (Vector3f) Stack.alloc(anchorPos); 
 | 
        Vector3f pivotBInW = (Vector3f) Stack.alloc(anchorPos); 
 | 
         
 | 
        // not used here 
 | 
        //    btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); 
 | 
        //    btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); 
 | 
  
 | 
        Vector3f normalWorld = Stack.alloc(Vector3f.class); 
 | 
        // linear part 
 | 
        for (int i=0; i<3; i++) { 
 | 
            if (linearLimits.isLimited(i)) { 
 | 
                if (useLinearReferenceFrameA) { 
 | 
                    calculatedTransformA.basis.getColumn(i, normalWorld); 
 | 
                } 
 | 
                else { 
 | 
                    calculatedTransformB.basis.getColumn(i, normalWorld); 
 | 
                } 
 | 
  
 | 
                buildLinearJacobian( 
 | 
                        /*jacLinear[i]*/i, normalWorld, 
 | 
                        pivotAInW, pivotBInW); 
 | 
  
 | 
            } 
 | 
        } 
 | 
  
 | 
        // angular part 
 | 
        for (int i=0; i<3; i++) { 
 | 
            // calculates error angle 
 | 
            if (testAngularLimitMotor(i)) { 
 | 
                this.getAxis(i, normalWorld); 
 | 
                // Create angular atom 
 | 
                buildAngularJacobian(/*jacAng[i]*/i, normalWorld); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
  
 | 
    @Override 
 | 
    public void solveConstraint(float timeStep) { 
 | 
        this.timeStep = timeStep; 
 | 
  
 | 
        //calculateTransforms(); 
 | 
  
 | 
        int i; 
 | 
  
 | 
        // linear 
 | 
  
 | 
        Vector3f pointInA = (Vector3f) Stack.alloc(calculatedTransformA.origin); 
 | 
        Vector3f pointInB = (Vector3f) Stack.alloc(calculatedTransformB.origin); 
 | 
  
 | 
        float jacDiagABInv; 
 | 
        Vector3f linear_axis = Stack.alloc(Vector3f.class); 
 | 
        for (i = 0; i < 3; i++) { 
 | 
            if (linearLimits.isLimited(i)) { 
 | 
                jacDiagABInv = 1f / jacLinear[i].getDiagonal(); 
 | 
  
 | 
                if (useLinearReferenceFrameA) { 
 | 
                    calculatedTransformA.basis.getColumn(i, linear_axis); 
 | 
                } 
 | 
                else { 
 | 
                    calculatedTransformB.basis.getColumn(i, linear_axis); 
 | 
                } 
 | 
  
 | 
                linearLimits.solveLinearAxis( 
 | 
                        this.timeStep, 
 | 
                        jacDiagABInv, 
 | 
                        rbA, pointInA, 
 | 
                        rbB, pointInB, 
 | 
                        i, linear_axis, anchorPos); 
 | 
  
 | 
            } 
 | 
        } 
 | 
  
 | 
        // angular 
 | 
        Vector3f angular_axis = Stack.alloc(Vector3f.class); 
 | 
        float angularJacDiagABInv; 
 | 
        for (i = 0; i < 3; i++) { 
 | 
            if (angularLimits[i].needApplyTorques()) { 
 | 
                // get axis 
 | 
                getAxis(i, angular_axis); 
 | 
  
 | 
                angularJacDiagABInv = 1f / jacAng[i].getDiagonal(); 
 | 
  
 | 
                angularLimits[i].solveAngularLimits(this.timeStep, angular_axis, angularJacDiagABInv, rbA, rbB); 
 | 
            } 
 | 
        } 
 | 
    } 
 | 
     
 | 
  
 | 
    public void updateRHS(float timeStep) { 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Get the rotation axis in global coordinates. 
 | 
     * Generic6DofConstraint.buildJacobian must be called previously. 
 | 
     */ 
 | 
    public Vector3f getAxis(int axis_index, Vector3f out) { 
 | 
        out.set(calculatedAxis[axis_index]); 
 | 
        return out; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Get the relative Euler angle. 
 | 
     * Generic6DofConstraint.buildJacobian must be called previously. 
 | 
     */ 
 | 
    public float getAngle(int axis_index) { 
 | 
        return VectorUtil.getCoord(calculatedAxisAngleDiff, axis_index); 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Gets the global transform of the offset for body A.<p> 
 | 
     * See also: Generic6DofConstraint.getFrameOffsetA, Generic6DofConstraint.getFrameOffsetB, Generic6DofConstraint.calculateAngleInfo. 
 | 
     */ 
 | 
    public Transform getCalculatedTransformA(Transform out) { 
 | 
        out.set(calculatedTransformA); 
 | 
        return out; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Gets the global transform of the offset for body B.<p> 
 | 
     * See also: Generic6DofConstraint.getFrameOffsetA, Generic6DofConstraint.getFrameOffsetB, Generic6DofConstraint.calculateAngleInfo. 
 | 
     */ 
 | 
    public Transform getCalculatedTransformB(Transform out) { 
 | 
        out.set(calculatedTransformB); 
 | 
        return out; 
 | 
    } 
 | 
  
 | 
    public Transform getFrameOffsetA(Transform out) { 
 | 
        out.set(frameInA); 
 | 
        return out; 
 | 
    } 
 | 
  
 | 
    public Transform getFrameOffsetB(Transform out) { 
 | 
        out.set(frameInB); 
 | 
        return out; 
 | 
    } 
 | 
     
 | 
    public void setLinearLowerLimit(Vector3f linearLower) { 
 | 
        linearLimits.lowerLimit.set(linearLower); 
 | 
    } 
 | 
  
 | 
    public void setLinearUpperLimit(Vector3f linearUpper) { 
 | 
        linearLimits.upperLimit.set(linearUpper); 
 | 
    } 
 | 
  
 | 
    public void setAngularLowerLimit(Vector3f angularLower) { 
 | 
        angularLimits[0].loLimit = angularLower.x; 
 | 
        angularLimits[1].loLimit = angularLower.y; 
 | 
        angularLimits[2].loLimit = angularLower.z; 
 | 
    } 
 | 
  
 | 
    public void setAngularUpperLimit(Vector3f angularUpper) { 
 | 
        angularLimits[0].hiLimit = angularUpper.x; 
 | 
        angularLimits[1].hiLimit = angularUpper.y; 
 | 
        angularLimits[2].hiLimit = angularUpper.z; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Retrieves the angular limit informacion. 
 | 
     */ 
 | 
    public RotationalLimitMotor getRotationalLimitMotor(int index) { 
 | 
        return angularLimits[index]; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Retrieves the limit informacion. 
 | 
     */ 
 | 
    public TranslationalLimitMotor getTranslationalLimitMotor() { 
 | 
        return linearLimits; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * first 3 are linear, next 3 are angular 
 | 
     */ 
 | 
    public void setLimit(int axis, float lo, float hi) { 
 | 
        if (axis < 3) { 
 | 
            VectorUtil.setCoord(linearLimits.lowerLimit, axis, lo); 
 | 
            VectorUtil.setCoord(linearLimits.upperLimit, axis, hi); 
 | 
        } 
 | 
        else { 
 | 
            angularLimits[axis - 3].loLimit = lo; 
 | 
            angularLimits[axis - 3].hiLimit = hi; 
 | 
        } 
 | 
    } 
 | 
     
 | 
    /** 
 | 
     * Test limit.<p> 
 | 
     * - free means upper < lower,<br> 
 | 
     * - locked means upper == lower<br> 
 | 
     * - limited means upper > lower<br> 
 | 
     * - limitIndex: first 3 are linear, next 3 are angular 
 | 
     */ 
 | 
    public boolean isLimited(int limitIndex) { 
 | 
        if (limitIndex < 3) { 
 | 
            return linearLimits.isLimited(limitIndex); 
 | 
  
 | 
        } 
 | 
        return angularLimits[limitIndex - 3].isLimited(); 
 | 
    } 
 | 
     
 | 
    // overridable 
 | 
    public void calcAnchorPos() { 
 | 
        float imA = rbA.getInvMass(); 
 | 
        float imB = rbB.getInvMass(); 
 | 
        float weight; 
 | 
        if (imB == 0f) { 
 | 
            weight = 1f; 
 | 
        } 
 | 
        else { 
 | 
            weight = imA / (imA + imB); 
 | 
        } 
 | 
        Vector3f pA = calculatedTransformA.origin; 
 | 
        Vector3f pB = calculatedTransformB.origin; 
 | 
  
 | 
        Vector3f tmp1 = Stack.alloc(Vector3f.class); 
 | 
        Vector3f tmp2 = Stack.alloc(Vector3f.class); 
 | 
  
 | 
        tmp1.scale(weight, pA); 
 | 
        tmp2.scale(1f - weight, pB); 
 | 
        anchorPos.add(tmp1, tmp2); 
 | 
    } 
 | 
     
 | 
} 
 |