Package com.bulletphysics.dynamics.constraintsolver

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

/*
* 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.
*/

/* Hinge Constraint by Dirk Gregorius. Limits added by Marcus Hennix at Starbreeze Studios */

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;

/**
* Hinge constraint between two rigid bodies each with a pivot point that descibes
* the axis location in local space. Axis defines the orientation of the hinge axis.
*
* @author jezek2
*/
public class HingeConstraint extends TypedConstraint {

  private JacobianEntry[] jac/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; // 3 orthogonal linear constraints
  private JacobianEntry[] jacAng/*[3]*/ = new JacobianEntry[] { new JacobianEntry(), new JacobianEntry(), new JacobianEntry() }; // 2 orthogonal angular constraints+ 1 for limit/motor

  private final Transform rbAFrame = new Transform(); // constraint axii. Assumes z is hinge axis.
  private final Transform rbBFrame = new Transform();

  private float motorTargetVelocity;
  private float maxMotorImpulse;

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

  private float lowerLimit; 
  private float upperLimit; 
 
  private float kHinge;

  private float limitSign;
  private float correction;

  private float accLimitImpulse;

  private boolean angularOnly;
  private boolean enableAngularMotor;
  private boolean solveLimit;

  public HingeConstraint() {
    super(TypedConstraintType.HINGE_CONSTRAINT_TYPE);
    enableAngularMotor = false;
  }

  public HingeConstraint(RigidBody rbA, RigidBody rbB, Vector3f pivotInA, Vector3f pivotInB, Vector3f axisInA, Vector3f axisInB) {
    super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA, rbB);
    angularOnly = false;
    enableAngularMotor = false;

    rbAFrame.origin.set(pivotInA);

    // since no frame is given, assume this to be zero angle and just pick rb transform axis
    Vector3f rbAxisA1 = Stack.alloc(Vector3f.class);
    Vector3f rbAxisA2 = Stack.alloc(Vector3f.class);
   
    Transform centerOfMassA = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class));
    centerOfMassA.basis.getColumn(0, rbAxisA1);
    float projection = axisInA.dot(rbAxisA1);

    if (projection >= 1.0f - BulletGlobals.SIMD_EPSILON) {
      centerOfMassA.basis.getColumn(2, rbAxisA1);
      rbAxisA1.negate();
      centerOfMassA.basis.getColumn(1, rbAxisA2);
    } else if (projection <= -1.0f + BulletGlobals.SIMD_EPSILON) {          
      centerOfMassA.basis.getColumn(2, rbAxisA1);                           
      centerOfMassA.basis.getColumn(1, rbAxisA2);
    } else {
      rbAxisA2.cross(axisInA, rbAxisA1);                                                               
      rbAxisA1.cross(rbAxisA2, axisInA);                                                                                           
    }

    rbAFrame.basis.setRow(0, rbAxisA1.x, rbAxisA2.x, axisInA.x);
    rbAFrame.basis.setRow(1, rbAxisA1.y, rbAxisA2.y, axisInA.y);
    rbAFrame.basis.setRow(2, rbAxisA1.z, rbAxisA2.z, axisInA.z);

    Quat4f rotationArc = QuaternionUtil.shortestArcQuat(axisInA, axisInB, Stack.alloc(Quat4f.class));
    Vector3f rbAxisB1 = QuaternionUtil.quatRotate(rotationArc, rbAxisA1, Stack.alloc(Vector3f.class));
    Vector3f rbAxisB2 = Stack.alloc(Vector3f.class);
    rbAxisB2.cross(axisInB, rbAxisB1);

    rbBFrame.origin.set(pivotInB);
    rbBFrame.basis.setRow(0, rbAxisB1.x, rbAxisB2.x, -axisInB.x);
    rbBFrame.basis.setRow(1, rbAxisB1.y, rbAxisB2.y, -axisInB.y);
    rbBFrame.basis.setRow(2, rbAxisB1.z, rbAxisB2.z, -axisInB.z);     

    // start with free
    lowerLimit = 1e30f;
    upperLimit = -1e30f;
    biasFactor = 0.3f;
    relaxationFactor = 1.0f;
    limitSoftness = 0.9f;
    solveLimit = false;
  }

  public HingeConstraint(RigidBody rbA, Vector3f pivotInA, Vector3f axisInA) {
    super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA);
    angularOnly = false;
    enableAngularMotor = false;

    // since no frame is given, assume this to be zero angle and just pick rb transform axis
    // fixed axis in worldspace
    Vector3f rbAxisA1 = Stack.alloc(Vector3f.class);
    Transform centerOfMassA = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class));
    centerOfMassA.basis.getColumn(0, rbAxisA1);

    float projection = rbAxisA1.dot(axisInA);
    if (projection > BulletGlobals.FLT_EPSILON) {
      rbAxisA1.scale(projection);
      rbAxisA1.sub(axisInA);
    }
    else {
      centerOfMassA.basis.getColumn(1, rbAxisA1);
    }

    Vector3f rbAxisA2 = Stack.alloc(Vector3f.class);
    rbAxisA2.cross(axisInA, rbAxisA1);

    rbAFrame.origin.set(pivotInA);
    rbAFrame.basis.setRow(0, rbAxisA1.x, rbAxisA2.x, axisInA.x);
    rbAFrame.basis.setRow(1, rbAxisA1.y, rbAxisA2.y, axisInA.y);
    rbAFrame.basis.setRow(2, rbAxisA1.z, rbAxisA2.z, axisInA.z);

    Vector3f axisInB = Stack.alloc(Vector3f.class);
    axisInB.negate(axisInA);
    centerOfMassA.basis.transform(axisInB);

    Quat4f rotationArc = QuaternionUtil.shortestArcQuat(axisInA, axisInB, Stack.alloc(Quat4f.class));
    Vector3f rbAxisB1 = QuaternionUtil.quatRotate(rotationArc, rbAxisA1, Stack.alloc(Vector3f.class));
    Vector3f rbAxisB2 = Stack.alloc(Vector3f.class);
    rbAxisB2.cross(axisInB, rbAxisB1);

    rbBFrame.origin.set(pivotInA);
    centerOfMassA.transform(rbBFrame.origin);
    rbBFrame.basis.setRow(0, rbAxisB1.x, rbAxisB2.x, axisInB.x);
    rbBFrame.basis.setRow(1, rbAxisB1.y, rbAxisB2.y, axisInB.y);
    rbBFrame.basis.setRow(2, rbAxisB1.z, rbAxisB2.z, axisInB.z);

    // start with free
    lowerLimit = 1e30f;
    upperLimit = -1e30f;
    biasFactor = 0.3f;
    relaxationFactor = 1.0f;
    limitSoftness = 0.9f;
    solveLimit = false;
  }

  public HingeConstraint(RigidBody rbA, RigidBody rbB, Transform rbAFrame, Transform rbBFrame) {
    super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA, rbB);
    this.rbAFrame.set(rbAFrame);
    this.rbBFrame.set(rbBFrame);
    angularOnly = false;
    enableAngularMotor = false;

    // flip axis
    this.rbBFrame.basis.m02 *= -1f;
    this.rbBFrame.basis.m12 *= -1f;
    this.rbBFrame.basis.m22 *= -1f;

    // start with free
    lowerLimit = 1e30f;
    upperLimit = -1e30f;
    biasFactor = 0.3f;
    relaxationFactor = 1.0f;
    limitSoftness = 0.9f;
    solveLimit = false;
  }

  public HingeConstraint(RigidBody rbA, Transform rbAFrame) {
    super(TypedConstraintType.HINGE_CONSTRAINT_TYPE, rbA);
    this.rbAFrame.set(rbAFrame);
    this.rbBFrame.set(rbAFrame);
    angularOnly = false;
    enableAngularMotor = false;

    // not providing rigidbody B means implicitly using worldspace for body B

    // flip axis
    this.rbBFrame.basis.m02 *= -1f;
    this.rbBFrame.basis.m12 *= -1f;
    this.rbBFrame.basis.m22 *= -1f;

    this.rbBFrame.origin.set(this.rbAFrame.origin);
    rbA.getCenterOfMassTransform(Stack.alloc(Transform.class)).transform(this.rbBFrame.origin);

    // start with free
    lowerLimit = 1e30f;
    upperLimit = -1e30f;
    biasFactor = 0.3f;
    relaxationFactor = 1.0f;
    limitSoftness = 0.9f;
    solveLimit = false;
  }
 
  @Override
  public void buildJacobian() {
    Vector3f tmp = Stack.alloc(Vector3f.class);
    Vector3f tmp1 = Stack.alloc(Vector3f.class);
    Vector3f tmp2 = Stack.alloc(Vector3f.class);
    Vector3f tmpVec = Stack.alloc(Vector3f.class);
    Matrix3f mat1 = Stack.alloc(Matrix3f.class);
    Matrix3f mat2 = Stack.alloc(Matrix3f.class);
   
    Transform centerOfMassA = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class));
    Transform centerOfMassB = rbB.getCenterOfMassTransform(Stack.alloc(Transform.class));

    appliedImpulse = 0f;

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

      Vector3f pivotBInW = Stack.alloc(rbBFrame.origin);
      centerOfMassB.transform(pivotBInW);

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

      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].set(relPos);
        normal[0].normalize();
      }
      else {
        normal[0].set(1f, 0f, 0f);
      }

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

      for (int i = 0; i < 3; i++) {
        mat1.transpose(centerOfMassA.basis);
        mat2.transpose(centerOfMassB.basis);

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

        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());
      }
    }

    // calculate two perpendicular jointAxis, orthogonal to hingeAxis
    // these two jointAxis require equal angular velocities for both bodies

    // this is unused for now, it's a todo
    Vector3f jointAxis0local = Stack.alloc(Vector3f.class);
    Vector3f jointAxis1local = Stack.alloc(Vector3f.class);

    rbAFrame.basis.getColumn(2, tmp);
    TransformUtil.planeSpace1(tmp, jointAxis0local, jointAxis1local);

    // TODO: check this
    //getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2);

    Vector3f jointAxis0 = Stack.alloc(jointAxis0local);
    centerOfMassA.basis.transform(jointAxis0);

    Vector3f jointAxis1 = Stack.alloc(jointAxis1local);
    centerOfMassA.basis.transform(jointAxis1);

    Vector3f hingeAxisWorld = Stack.alloc(Vector3f.class);
    rbAFrame.basis.getColumn(2, hingeAxisWorld);
    centerOfMassA.basis.transform(hingeAxisWorld);

    mat1.transpose(centerOfMassA.basis);
    mat2.transpose(centerOfMassB.basis);
    jacAng[0].init(jointAxis0,
        mat1,
        mat2,
        rbA.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)),
        rbB.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)));

    // JAVA NOTE: reused mat1 and mat2, as recomputation is not needed
    jacAng[1].init(jointAxis1,
        mat1,
        mat2,
        rbA.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)),
        rbB.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)));

    // JAVA NOTE: reused mat1 and mat2, as recomputation is not needed
    jacAng[2].init(hingeAxisWorld,
        mat1,
        mat2,
        rbA.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)),
        rbB.getInvInertiaDiagLocal(Stack.alloc(Vector3f.class)));

    // Compute limit information
    float hingeAngle = getHingeAngle();

    //set bias, sign, clear accumulator
    correction = 0f;
    limitSign = 0f;
    solveLimit = false;
    accLimitImpulse = 0f;

    if (lowerLimit < upperLimit) {
      if (hingeAngle <= lowerLimit * limitSoftness) {
        correction = (lowerLimit - hingeAngle);
        limitSign = 1.0f;
        solveLimit = true;
      }
      else if (hingeAngle >= upperLimit * limitSoftness) {
        correction = upperLimit - hingeAngle;
        limitSign = -1.0f;
        solveLimit = true;
      }
    }

    // Compute K = J*W*J' for hinge axis
    Vector3f axisA = Stack.alloc(Vector3f.class);
    rbAFrame.basis.getColumn(2, axisA);
    centerOfMassA.basis.transform(axisA);

    kHinge = 1.0f / (getRigidBodyA().computeAngularImpulseDenominator(axisA) +
        getRigidBodyB().computeAngularImpulseDenominator(axisA));
  }

  @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 centerOfMassA = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class));
    Transform centerOfMassB = rbB.getCenterOfMassTransform(Stack.alloc(Transform.class));
   
    Vector3f pivotAInW = Stack.alloc(rbAFrame.origin);
    centerOfMassA.transform(pivotAInW);

    Vector3f pivotBInW = Stack.alloc(rbBFrame.origin);
    centerOfMassB.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

      // get axes in world space
      Vector3f axisA = Stack.alloc(Vector3f.class);
      rbAFrame.basis.getColumn(2, axisA);
      centerOfMassA.basis.transform(axisA);

      Vector3f axisB = Stack.alloc(Vector3f.class);
      rbBFrame.basis.getColumn(2, axisB);
      centerOfMassB.basis.transform(axisB);

      Vector3f angVelA = getRigidBodyA().getAngularVelocity(Stack.alloc(Vector3f.class));
      Vector3f angVelB = getRigidBodyB().getAngularVelocity(Stack.alloc(Vector3f.class));

      Vector3f angVelAroundHingeAxisA = Stack.alloc(Vector3f.class);
      angVelAroundHingeAxisA.scale(axisA.dot(angVelA), axisA);

      Vector3f angVelAroundHingeAxisB = Stack.alloc(Vector3f.class);
      angVelAroundHingeAxisB.scale(axisB.dot(angVelB), axisB);

      Vector3f angAorthog = Stack.alloc(Vector3f.class);
      angAorthog.sub(angVelA, angVelAroundHingeAxisA);

      Vector3f angBorthog = Stack.alloc(Vector3f.class);
      angBorthog.sub(angVelB, angVelAroundHingeAxisB);

      Vector3f velrelOrthog = Stack.alloc(Vector3f.class);
      velrelOrthog.sub(angAorthog, angBorthog);

      {
        // solve orthogonal angular velocity correction
        float relaxation = 1f;
        float len = velrelOrthog.length();
        if (len > 0.00001f) {
          Vector3f normal = Stack.alloc(Vector3f.class);
          normal.normalize(velrelOrthog);

          float denom = getRigidBodyA().computeAngularImpulseDenominator(normal) +
              getRigidBodyB().computeAngularImpulseDenominator(normal);
          // scale for mass and relaxation
          // todo:  expose this 0.9 factor to developer
          velrelOrthog.scale((1f / denom) * relaxationFactor);
        }

        // solve angular positional correction
        // TODO: check
        //Vector3f angularError = -axisA.cross(axisB) *(btScalar(1.)/timeStep);
        Vector3f angularError = Stack.alloc(Vector3f.class);
        angularError.cross(axisA, axisB);
        angularError.negate();
        angularError.scale(1f / timeStep);
        float len2 = angularError.length();
        if (len2 > 0.00001f) {
          Vector3f normal2 = Stack.alloc(Vector3f.class);
          normal2.normalize(angularError);

          float denom2 = getRigidBodyA().computeAngularImpulseDenominator(normal2) +
              getRigidBodyB().computeAngularImpulseDenominator(normal2);
          angularError.scale((1f / denom2) * relaxation);
        }

        tmp.negate(velrelOrthog);
        tmp.add(angularError);
        rbA.applyTorqueImpulse(tmp);

        tmp.sub(velrelOrthog, angularError);
        rbB.applyTorqueImpulse(tmp);

        // solve limit
        if (solveLimit) {
          tmp.sub(angVelB, angVelA);
          float amplitude = ((tmp).dot(axisA) * relaxationFactor + correction * (1f / timeStep) * biasFactor) * limitSign;

          float impulseMag = amplitude * kHinge;

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

          Vector3f impulse = Stack.alloc(Vector3f.class);
          impulse.scale(impulseMag * limitSign, axisA);

          rbA.applyTorqueImpulse(impulse);

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

      // apply motor
      if (enableAngularMotor) {
        // todo: add limits too
        Vector3f angularLimit = Stack.alloc(Vector3f.class);
        angularLimit.set(0f, 0f, 0f);

        Vector3f velrel = Stack.alloc(Vector3f.class);
        velrel.sub(angVelAroundHingeAxisA, angVelAroundHingeAxisB);
        float projRelVel = velrel.dot(axisA);

        float desiredMotorVel = motorTargetVelocity;
        float motor_relvel = desiredMotorVel - projRelVel;

        float unclippedMotorImpulse = kHinge * motor_relvel;
        // todo: should clip against accumulated impulse
        float clippedMotorImpulse = unclippedMotorImpulse > maxMotorImpulse ? maxMotorImpulse : unclippedMotorImpulse;
        clippedMotorImpulse = clippedMotorImpulse < -maxMotorImpulse ? -maxMotorImpulse : clippedMotorImpulse;
        Vector3f motorImp = Stack.alloc(Vector3f.class);
        motorImp.scale(clippedMotorImpulse, axisA);

        tmp.add(motorImp, angularLimit);
        rbA.applyTorqueImpulse(tmp);

        tmp.negate(motorImp);
        tmp.sub(angularLimit);
        rbB.applyTorqueImpulse(tmp);
      }
    }
  }

  public void updateRHS(float timeStep) {
  }

  public float getHingeAngle() {
    Transform centerOfMassA = rbA.getCenterOfMassTransform(Stack.alloc(Transform.class));
    Transform centerOfMassB = rbB.getCenterOfMassTransform(Stack.alloc(Transform.class));
   
    Vector3f refAxis0 = Stack.alloc(Vector3f.class);
    rbAFrame.basis.getColumn(0, refAxis0);
    centerOfMassA.basis.transform(refAxis0);

    Vector3f refAxis1 = Stack.alloc(Vector3f.class);
    rbAFrame.basis.getColumn(1, refAxis1);
    centerOfMassA.basis.transform(refAxis1);

    Vector3f swingAxis = Stack.alloc(Vector3f.class);
    rbBFrame.basis.getColumn(1, swingAxis);
    centerOfMassB.basis.transform(swingAxis);

    return ScalarUtil.atan2Fast(swingAxis.dot(refAxis0), swingAxis.dot(refAxis1));
  }
 
  public void setAngularOnly(boolean angularOnly) {
    this.angularOnly = angularOnly;
  }

  public void enableAngularMotor(boolean enableMotor, float targetVelocity, float maxMotorImpulse) {
    this.enableAngularMotor = enableMotor;
    this.motorTargetVelocity = targetVelocity;
    this.maxMotorImpulse = maxMotorImpulse;
  }

  public void setLimit(float low, float high) {
    setLimit(low, high, 0.9f, 0.3f, 1.0f);
  }

  public void setLimit(float low, float high, float _softness, float _biasFactor, float _relaxationFactor) {
    lowerLimit = low;
    upperLimit = high;

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

  public float getLowerLimit() {
    return lowerLimit;
  }

  public float getUpperLimit() {
    return upperLimit;
  }

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

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

  public boolean getSolveLimit() {
    return solveLimit;
  }

  public float getLimitSign() {
    return limitSign;
  }

  public boolean getAngularOnly() {
    return angularOnly;
  }

  public boolean getEnableAngularMotor() {
    return enableAngularMotor;
  }

  public float getMotorTargetVelosity() {
    return motorTargetVelocity;
  }

  public float getMaxMotorImpulse() {
    return maxMotorImpulse;
  }
 
}
TOP

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

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.