Package com.bulletphysics.dynamics.constraintsolver

Source Code of com.bulletphysics.dynamics.constraintsolver.ConeTwistConstraint

/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* Bullet Continuous Collision Detection and Physics Library
* btConeTwistConstraint is Copyright (c) 2007 Starbreeze Studios
*
* 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.
*
* Written by: Marcus Hennix
*/

package com.bulletphysics.dynamics.constraintsolver;

import com.bulletphysics.BulletGlobals;
import com.bulletphysics.dynamics.RigidBody;
import com.bulletphysics.linearmath.QuaternionUtil;
import com.bulletphysics.linearmath.ScalarUtil;
import com.bulletphysics.linearmath.Transform;
import com.bulletphysics.linearmath.TransformUtil;
import cz.advel.stack.Stack;
import javax.vecmath.Matrix3f;
import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;

/**
* ConeTwistConstraint can be used to simulate ragdoll joints (upper arm, leg etc).
*
* @author jezek2
*/
public class ConeTwistConstraint extends TypedConstraint {

  private JacobianEntry[] jac/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; //3 orthogonal linear constraints

  private final Transform rbAFrame = new Transform();
  private final Transform rbBFrame = new Transform();

  private float limitSoftness;
  private float biasFactor;
  private float relaxationFactor;

  private float swingSpan1;
  private float swingSpan2;
  private float twistSpan;

  private final Vector3f swingAxis = new Vector3f();
  private final Vector3f twistAxis = new Vector3f();

  private float kSwing;
  private float kTwist;

  private float twistLimitSign;
  private float swingCorrection;
  private float twistCorrection;

  private float accSwingLimitImpulse;
  private float accTwistLimitImpulse;

  private boolean angularOnly = false;
  private boolean solveTwistLimit;
  private boolean solveSwingLimit;
 
  public ConeTwistConstraint() {
    super(TypedConstraintType.CONETWIST_CONSTRAINT_TYPE);
  }

  public ConeTwistConstraint(RigidBody rbA, RigidBody rbB, Transform rbAFrame, Transform rbBFrame) {
    super(TypedConstraintType.CONETWIST_CONSTRAINT_TYPE, rbA, rbB);
    this.rbAFrame.set(rbAFrame);
    this.rbBFrame.set(rbBFrame);

    swingSpan1 = 1e30f;
    swingSpan2 = 1e30f;
    twistSpan = 1e30f;
    biasFactor = 0.3f;
    relaxationFactor = 1.0f;

    solveTwistLimit = false;
    solveSwingLimit = false;
  }

  public ConeTwistConstraint(RigidBody rbA, Transform rbAFrame) {
    super(TypedConstraintType.CONETWIST_CONSTRAINT_TYPE, rbA);
    this.rbAFrame.set(rbAFrame);
    this.rbBFrame.set(this.rbAFrame);

    swingSpan1 = 1e30f;
    swingSpan2 = 1e30f;
    twistSpan = 1e30f;
    biasFactor = 0.3f;
    relaxationFactor = 1.0f;

    solveTwistLimit = false;
    solveSwingLimit = false;
  }
 
  @Override
  public void buildJacobian() {
    Vector3f tmp = Stack.alloc(Vector3f.class);
    Vector3f tmp1 = Stack.alloc(Vector3f.class);
    Vector3f tmp2 = Stack.alloc(Vector3f.class);

    Transform tmpTrans = Stack.alloc(Transform.class);

    appliedImpulse = 0f;

    // set bias, sign, clear accumulator
    swingCorrection = 0f;
    twistLimitSign = 0f;
    solveTwistLimit = false;
    solveSwingLimit = false;
    accTwistLimitImpulse = 0f;
    accSwingLimitImpulse = 0f;

    if (!angularOnly) {
      Vector3f pivotAInW = Stack.alloc(rbAFrame.origin);
      rbA.getCenterOfMassTransform(tmpTrans).transform(pivotAInW);

      Vector3f pivotBInW = Stack.alloc(rbBFrame.origin);
      rbB.getCenterOfMassTransform(tmpTrans).transform(pivotBInW);

      Vector3f relPos = Stack.alloc(Vector3f.class);
      relPos.sub(pivotBInW, pivotAInW);

      // TODO: stack
      Vector3f[] normal/*[3]*/ = new Vector3f[]{Stack.alloc(Vector3f.class), Stack.alloc(Vector3f.class), Stack.alloc(Vector3f.class)};
      if (relPos.lengthSquared() > BulletGlobals.FLT_EPSILON) {
        normal[0].normalize(relPos);
      }
      else {
        normal[0].set(1f, 0f, 0f);
      }

      TransformUtil.planeSpace1(normal[0], normal[1], normal[2]);

      for (int i = 0; i < 3; i++) {
        Matrix3f mat1 = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis;
        mat1.transpose();

        Matrix3f mat2 = rbB.getCenterOfMassTransform(Stack.alloc(Transform.class)).basis;
        mat2.transpose();

        tmp1.sub(pivotAInW, rbA.getCenterOfMassPosition(tmp));
        tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition(tmp));

        jac[i].init(
            mat1,
            mat2,
            tmp1,
            tmp2,
            normal[i],
            rbA.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)),
            rbA.getInvMass(),
            rbB.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)),
            rbB.getInvMass());
      }
    }

    Vector3f b1Axis1 = Stack.alloc(Vector3f.class), b1Axis2 = Stack.alloc(Vector3f.class), b1Axis3 = Stack.alloc(Vector3f.class);
    Vector3f b2Axis1 = Stack.alloc(Vector3f.class), b2Axis2 = Stack.alloc(Vector3f.class);

    rbAFrame.basis.getColumn(0, b1Axis1);
    getRigidBodyA().getCenterOfMassTransform(tmpTrans).basis.transform(b1Axis1);

    rbBFrame.basis.getColumn(0, b2Axis1);
    getRigidBodyB().getCenterOfMassTransform(tmpTrans).basis.transform(b2Axis1);

    float swing1 = 0f, swing2 = 0f;

    float swx = 0f, swy = 0f;
    float thresh = 10f;
    float fact;

    // Get Frame into world space
    if (swingSpan1 >= 0.05f) {
      rbAFrame.basis.getColumn(1, b1Axis2);
      getRigidBodyA().getCenterOfMassTransform(tmpTrans).basis.transform(b1Axis2);
//      swing1 = ScalarUtil.atan2Fast(b2Axis1.dot(b1Axis2), b2Axis1.dot(b1Axis1));
      swx = b2Axis1.dot(b1Axis1);
      swy = b2Axis1.dot(b1Axis2);
      swing1 = ScalarUtil.atan2Fast(swy, swx);
      fact = (swy*swy + swx*swx) * thresh * thresh;
      fact = fact / (fact + 1f);
      swing1 *= fact;
    }

    if (swingSpan2 >= 0.05f) {
      rbAFrame.basis.getColumn(2, b1Axis3);
      getRigidBodyA().getCenterOfMassTransform(tmpTrans).basis.transform(b1Axis3);
//      swing2 = ScalarUtil.atan2Fast(b2Axis1.dot(b1Axis3), b2Axis1.dot(b1Axis1));
      swx = b2Axis1.dot(b1Axis1);
      swy = b2Axis1.dot(b1Axis3);
      swing2 = ScalarUtil.atan2Fast(swy, swx);
      fact = (swy*swy + swx*swx) * thresh * thresh;
      fact = fact / (fact + 1f);
      swing2 *= fact;
    }

    float RMaxAngle1Sq = 1.0f / (swingSpan1 * swingSpan1);
    float RMaxAngle2Sq = 1.0f / (swingSpan2 * swingSpan2);
    float EllipseAngle = Math.abs(swing1*swing1) * RMaxAngle1Sq + Math.abs(swing2*swing2) * RMaxAngle2Sq;

    if (EllipseAngle > 1.0f) {
      swingCorrection = EllipseAngle - 1.0f;
      solveSwingLimit = true;

      // Calculate necessary axis & factors
      tmp1.scale(b2Axis1.dot(b1Axis2), b1Axis2);
      tmp2.scale(b2Axis1.dot(b1Axis3), b1Axis3);
      tmp.add(tmp1, tmp2);
      swingAxis.cross(b2Axis1, tmp);
      swingAxis.normalize();

      float swingAxisSign = (b2Axis1.dot(b1Axis1) >= 0.0f) ? 1.0f : -1.0f;
      swingAxis.scale(swingAxisSign);

      kSwing = 1f / (getRigidBodyA().computeAngularImpulseDenominator(swingAxis) +
          getRigidBodyB().computeAngularImpulseDenominator(swingAxis));

    }

    // Twist limits
    if (twistSpan >= 0f) {
      //Vector3f b2Axis2 = Stack.alloc(Vector3f.class);
      rbBFrame.basis.getColumn(1, b2Axis2);
      getRigidBodyB().getCenterOfMassTransform(tmpTrans).basis.transform(b2Axis2);

      Quat4f rotationArc = QuaternionUtil.shortestArcQuat(b2Axis1, b1Axis1, Stack.alloc(Quat4f.class));
      Vector3f TwistRef = QuaternionUtil.quatRotate(rotationArc, b2Axis2, Stack.alloc(Vector3f.class));
      float twist = ScalarUtil.atan2Fast(TwistRef.dot(b1Axis3), TwistRef.dot(b1Axis2));

      float lockedFreeFactor = (twistSpan > 0.05f) ? limitSoftness : 0f;
      if (twist <= -twistSpan * lockedFreeFactor) {
        twistCorrection = -(twist + twistSpan);
        solveTwistLimit = true;

        twistAxis.add(b2Axis1, b1Axis1);
        twistAxis.scale(0.5f);
        twistAxis.normalize();
        twistAxis.scale(-1.0f);

        kTwist = 1f / (getRigidBodyA().computeAngularImpulseDenominator(twistAxis) +
            getRigidBodyB().computeAngularImpulseDenominator(twistAxis));

      }
      else if (twist > twistSpan * lockedFreeFactor) {
        twistCorrection = (twist - twistSpan);
        solveTwistLimit = true;

        twistAxis.add(b2Axis1, b1Axis1);
        twistAxis.scale(0.5f);
        twistAxis.normalize();

        kTwist = 1f / (getRigidBodyA().computeAngularImpulseDenominator(twistAxis) +
            getRigidBodyB().computeAngularImpulseDenominator(twistAxis));
      }
    }
  }

  @Override
  public void solveConstraint(float timeStep) {
    Vector3f tmp = Stack.alloc(Vector3f.class);
    Vector3f tmp2 = Stack.alloc(Vector3f.class);

    Vector3f tmpVec = Stack.alloc(Vector3f.class);
    Transform tmpTrans = Stack.alloc(Transform.class);

    Vector3f pivotAInW = Stack.alloc(rbAFrame.origin);
    rbA.getCenterOfMassTransform(tmpTrans).transform(pivotAInW);

    Vector3f pivotBInW = Stack.alloc(rbBFrame.origin);
    rbB.getCenterOfMassTransform(tmpTrans).transform(pivotBInW);

    float tau = 0.3f;

    // linear part
    if (!angularOnly) {
      Vector3f rel_pos1 = Stack.alloc(Vector3f.class);
      rel_pos1.sub(pivotAInW, rbA.getCenterOfMassPosition(tmpVec));

      Vector3f rel_pos2 = Stack.alloc(Vector3f.class);
      rel_pos2.sub(pivotBInW, rbB.getCenterOfMassPosition(tmpVec));

      Vector3f vel1 = rbA.getVelocityInLocalPoint(rel_pos1, Stack.alloc(Vector3f.class));
      Vector3f vel2 = rbB.getVelocityInLocalPoint(rel_pos2, Stack.alloc(Vector3f.class));
      Vector3f vel = Stack.alloc(Vector3f.class);
      vel.sub(vel1, vel2);

      for (int i = 0; i < 3; i++) {
        Vector3f normal = jac[i].linearJointAxis;
        float jacDiagABInv = 1f / jac[i].getDiagonal();

        float rel_vel;
        rel_vel = normal.dot(vel);
        // positional error (zeroth order error)
        tmp.sub(pivotAInW, pivotBInW);
        float depth = -(tmp).dot(normal); // this is the error projected on the normal
        float impulse = depth * tau / timeStep * jacDiagABInv - rel_vel * jacDiagABInv;
        appliedImpulse += impulse;
        Vector3f impulse_vector = Stack.alloc(Vector3f.class);
        impulse_vector.scale(impulse, normal);

        tmp.sub(pivotAInW, rbA.getCenterOfMassPosition(tmpVec));
        rbA.applyImpulse(impulse_vector, tmp);

        tmp.negate(impulse_vector);
        tmp2.sub(pivotBInW, rbB.getCenterOfMassPosition(tmpVec));
        rbB.applyImpulse(tmp, tmp2);
      }
    }

    {
      // solve angular part
      Vector3f angVelA = getRigidBodyA().getAngularVelocity(Stack.alloc(Vector3f.class));
      Vector3f angVelB = getRigidBodyB().getAngularVelocity(Stack.alloc(Vector3f.class));

      // solve swing limit
      if (solveSwingLimit) {
        tmp.sub(angVelB, angVelA);
        float amplitude = ((tmp).dot(swingAxis) * relaxationFactor * relaxationFactor + swingCorrection * (1f / timeStep) * biasFactor);
        float impulseMag = amplitude * kSwing;

        // Clamp the accumulated impulse
        float temp = accSwingLimitImpulse;
        accSwingLimitImpulse = Math.max(accSwingLimitImpulse + impulseMag, 0.0f);
        impulseMag = accSwingLimitImpulse - temp;

        Vector3f impulse = Stack.alloc(Vector3f.class);
        impulse.scale(impulseMag, swingAxis);

        rbA.applyTorqueImpulse(impulse);

        tmp.negate(impulse);
        rbB.applyTorqueImpulse(tmp);
      }

      // solve twist limit
      if (solveTwistLimit) {
        tmp.sub(angVelB, angVelA);
        float amplitude = ((tmp).dot(twistAxis) * relaxationFactor * relaxationFactor + twistCorrection * (1f / timeStep) * biasFactor);
        float impulseMag = amplitude * kTwist;

        // Clamp the accumulated impulse
        float temp = accTwistLimitImpulse;
        accTwistLimitImpulse = Math.max(accTwistLimitImpulse + impulseMag, 0.0f);
        impulseMag = accTwistLimitImpulse - temp;

        Vector3f impulse = Stack.alloc(Vector3f.class);
        impulse.scale(impulseMag, twistAxis);

        rbA.applyTorqueImpulse(impulse);

        tmp.negate(impulse);
        rbB.applyTorqueImpulse(tmp);
      }
    }
  }

  public void updateRHS(float timeStep) {
  }

  public void setAngularOnly(boolean angularOnly) {
    this.angularOnly = angularOnly;
  }

  public void setLimit(float _swingSpan1, float _swingSpan2, float _twistSpan) {
    setLimit(_swingSpan1, _swingSpan2, _twistSpan, 0.8f, 0.3f, 1.0f);
  }

  public void setLimit(float _swingSpan1, float _swingSpan2, float _twistSpan, float _softness, float _biasFactor, float _relaxationFactor) {
    swingSpan1 = _swingSpan1;
    swingSpan2 = _swingSpan2;
    twistSpan = _twistSpan;

    limitSoftness = _softness;
    biasFactor = _biasFactor;
    relaxationFactor = _relaxationFactor;
  }

  public Transform getAFrame(Transform out) {
    out.set(rbAFrame);
    return out;
  }

  public Transform getBFrame(Transform out) {
    out.set(rbBFrame);
    return out;
  }

  public boolean getSolveTwistLimit() {
    return solveTwistLimit;
  }

  public boolean getSolveSwingLimit() {
    return solveTwistLimit;
  }

  public float getTwistLimitSign() {
    return twistLimitSign;
  }
 
}
TOP

Related Classes of com.bulletphysics.dynamics.constraintsolver.ConeTwistConstraint

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.