All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.bullet.joints.HingeJoint Maven / Gradle / Ivy

There is a newer version: 8.2.0+mt
Show newest version
/*
 * Copyright (c) 2009-2018 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.bullet.joints;

import com.jme3.bullet.objects.PhysicsRigidBody;
import com.jme3.export.InputCapsule;
import com.jme3.export.JmeExporter;
import com.jme3.export.JmeImporter;
import com.jme3.export.OutputCapsule;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.util.logging.Logger;
import jme3utilities.math.MyVector3f;

/**
 * A single degree-of-freedom joint based on Bullet's btHingeConstraint.
 * 

* From the Bullet manual:
* Hinge constraint, or revolute joint restricts two additional angular degrees * of freedom, so the body can only rotate around one axis, the hinge axis. This * can be useful to represent doors or wheels rotating around one axis. The user * can specify limits and motor for the hinge. * * @author normenhansen */ public class HingeJoint extends Constraint { // ************************************************************************* // constants and loggers /** * message logger for this class */ final public static Logger logger2 = Logger.getLogger(HingeJoint.class.getName()); /** * field names for serialization */ final private static String tagAngularOnly = "angularOnly"; final private static String tagAxisA = "axisA"; final private static String tagAxisB = "axisB"; final private static String tagBiasFactor = "biasFactor"; final private static String tagEnableAngularMotor = "enableAngularMotor"; final private static String tagLimitSoftness = "limitSoftness"; final private static String tagLowerLimit = "lowerLimit"; final private static String tagMaxMotorImpulse = "maxMotorImpulse"; final private static String tagRelaxationFactor = "relaxationFactor"; final private static String tagTargetVelocity = "targetVelocity"; final private static String tagUpperLimit = "upperLimit"; // ************************************************************************* // fields /** * copy of the angular-only flag */ private boolean angularOnly = false; /** * copy of the "use reference frame A" flag */ private boolean useReferenceFrameA = false; /** * copy of the limit's bias factor: how strictly position errors are * corrected */ private float biasFactor = 0.3f; /** * copy of the limit's softness: the range fraction at which velocity-error * correction starts operating */ private float limitSoftness = 0.9f; /** * copy of the limit's relaxation factor: the rate at which velocity errors * are corrected */ private float relaxationFactor = 1f; /** * copy of the joint axis in A's local coordinates (unit vector) */ private Vector3f axisA; /** * copy of the joint axis: in B's local coordinates for a double-ended * joint, or in physics-space coordinates for a single-ended joint (unit * vector) */ private Vector3f axisB; // ************************************************************************* // constructors /** * No-argument constructor needed by SavableClassUtil. */ protected HingeJoint() { } /** * Instantiate a single-ended HingeJoint. *

* To be effective, the joint must be added to the body's PhysicsSpace and * the body must be dynamic. * * @param rigidBodyA the body to constrain (not null, alias created) * @param pivotInA the pivot location in A's scaled local coordinates (not * null, unaffected) * @param pivotInWorld the pivot location in physics-space coordinates (not * null, unaffected) * @param axisInA the joint axis in A's local coordinates (unit vector, * unaffected) * @param axisInWorld the joint axis in physics-space coordinates (unit * vector, unaffected) * @param referenceFrame which end to use as the reference frame (not null) */ public HingeJoint(PhysicsRigidBody rigidBodyA, Vector3f pivotInA, Vector3f pivotInWorld, Vector3f axisInA, Vector3f axisInWorld, JointEnd referenceFrame) { super(rigidBodyA, JointEnd.A, pivotInA, pivotInWorld); assert axisInA.isUnitVector() : axisInA; assert axisInWorld.isUnitVector() : axisInWorld; this.axisA = axisInA.clone(); this.axisB = axisInWorld.clone(); this.useReferenceFrameA = (referenceFrame == JointEnd.A); createJoint(); // Synchronize the btHingeConstraint parameters with the local copies. long constraintId = super.nativeId(); setAngularOnly(constraintId, angularOnly); float low = getLowerLimit(); float high = getUpperLimit(); setLimit(constraintId, low, high, limitSoftness, biasFactor, relaxationFactor); } /** * Instantiate a double-ended HingeJoint. *

* To be effective, the joint must be added to the PhysicsSpace of both * bodies. Also, the bodies must be distinct and at least one of them must * be dynamic. * * @param rigidBodyA the body for the A end (not null, alias created) * @param rigidBodyB the body for the B end (not null, alias created) * @param pivotInA the pivot location in A's scaled local coordinates (not * null, unaffected) * @param pivotInB the pivot location in B's scaled local coordinates (not * null, unaffected) * @param axisInA the joint axis in A's local coordinates (unit vector, * unaffected) * @param axisInB the joint axis in B's local coordinates (unit vector, * unaffected) */ public HingeJoint(PhysicsRigidBody rigidBodyA, PhysicsRigidBody rigidBodyB, Vector3f pivotInA, Vector3f pivotInB, Vector3f axisInA, Vector3f axisInB) { super(rigidBodyA, rigidBodyB, pivotInA, pivotInB); assert axisInA.isUnitVector() : axisInA; assert axisInB.isUnitVector() : axisInB; this.axisA = axisInA.clone(); this.axisB = axisInB.clone(); createJoint(); // Synchronize btHingeConstraint parameters with local copies. long constraintId = super.nativeId(); setAngularOnly(constraintId, angularOnly); float low = getLowerLimit(); float high = getUpperLimit(); setLimit(constraintId, low, high, limitSoftness, biasFactor, relaxationFactor); } // ************************************************************************* // new methods exposed /** * Enable or disable this joint's motor. * * @param enable true to enable, false to disable (default=false) * @param targetVelocity the desired target velocity * @param maxMotorImpulse the desired maximum rotational force */ public void enableMotor( boolean enable, float targetVelocity, float maxMotorImpulse) { long constraintId = nativeId(); enableMotor(constraintId, enable, targetVelocity, maxMotorImpulse); } /** * Read this joint's bias factor. * * @return the magnitude of the position correction: how strictly position * errors (drift) are corrected */ public float getBiasFactor() { return biasFactor; } /** * Test whether this joint's motor is enabled. * * @return true if enabled, otherwise false */ public boolean getEnableMotor() { long constraintId = nativeId(); boolean result = getEnableAngularMotor(constraintId); return result; } /** * Copy the joint's frame transform relative to the specified end. * * @param end which end (not null) * @param storeResult storage for the result (modified if not null) * @return the transform of the constraint space relative to the end */ public Transform getFrameTransform(JointEnd end, Transform storeResult) { Transform result = (storeResult == null) ? new Transform() : storeResult; long constraintId = nativeId(); switch (end) { case A: getFrameOffsetA(constraintId, result); break; case B: getFrameOffsetB(constraintId, result); break; default: String message = "end = " + end; throw new IllegalArgumentException(message); } return result; } /** * Read the hinge angle. * * @return the angle (in radians) */ public float getHingeAngle() { long constraintId = nativeId(); float result = getHingeAngle(constraintId); return result; } /** * Read this joint's limit softness. * * @return the range fraction at which velocity-error correction starts * operating */ public float getLimitSoftness() { return limitSoftness; } /** * Read the lower limit of the hinge angle. * * @return the angle (in radians) */ final public float getLowerLimit() { long constraintId = nativeId(); float result = getLowerLimit(constraintId); return result; } /** * Read the motor's target velocity. * * @return velocity */ public float getMotorTargetVelocity() { long constraintId = nativeId(); float result = getMotorTargetVelocity(constraintId); return result; } /** * Read the motor's maximum impulse. * * @return impulse */ public float getMaxMotorImpulse() { long constraintId = nativeId(); float result = getMaxMotorImpulse(constraintId); return result; } /** * Read this joint's relaxation factor. * * @return the rate at which velocity errors are corrected */ public float getRelaxationFactor() { return relaxationFactor; } /** * Read the upper limit of the hinge angle. * * @return angle (in radians) */ final public float getUpperLimit() { long constraintId = nativeId(); float result = getUpperLimit(constraintId); return result; } /** * Test whether this joint is angular-only, meaning no constraints on * translation. * * @return true if angular-only, otherwise false */ public boolean isAngularOnly() { return angularOnly; } /** * Alter whether this joint is angular-only, meaning no constraints on * translation. * * @param angularOnly true→translation is free, false→translation * is constrained (default=false) */ public void setAngularOnly(boolean angularOnly) { this.angularOnly = angularOnly; long constraintId = nativeId(); setAngularOnly(constraintId, angularOnly); } /** * Alter the angular limits for this joint. * * @param low the desired lower limit of the hinge angle (in radians, * default=1) * @param high the desired upper limit of the hinge angle (in radians, * default=-1) */ public void setLimit(float low, float high) { long constraintId = nativeId(); setLimit(constraintId, low, high, limitSoftness, biasFactor, relaxationFactor); } /** * Alter the angular limits for this joint. *

* If you're above the softness, velocities that would shoot through the * actual limit are slowed down. The bias should be in the range of 0.2 - * 0.5. * * @param low the desired lower limit of the hinge angle (in radians, * default=1) * @param high the desired upper limit of the hinge angle (in radians, * default=-1) * @param softness the desired range fraction at which velocity-error * correction starts operating. A softness of 0.9 means that the correction * starts at 90% of the limit range. (default=0.9) * @param bias the desired magnitude of the position correction: how * strictly position errors (drift) are corrected (default=0.3) * @param relaxation the desired rate at which velocity errors are * corrected. This can be seen as the strength of the limits. A low value * will make the limits more spongy. (default=1) */ public void setLimit(float low, float high, float softness, float bias, float relaxation) { long constraintId = nativeId(); this.biasFactor = bias; this.relaxationFactor = relaxation; this.limitSoftness = softness; setLimit(constraintId, low, high, softness, bias, relaxation); } // ************************************************************************* // Constraint methods /** * Callback from {@link com.jme3.util.clone.Cloner} to convert this * shallow-cloned object into a deep-cloned one, using the specified Cloner * and original to resolve copied fields. * * @param cloner the Cloner that's cloning this joint (not null) * @param original the instance from which this joint was shallow-cloned * (not null, unaffected) */ @Override public void cloneFields(Cloner cloner, Object original) { assert !hasAssignedNativeObject(); HingeJoint old = (HingeJoint) original; assert old != this; assert old.hasAssignedNativeObject(); assert !hasAssignedNativeObject(); super.cloneFields(cloner, original); if (hasAssignedNativeObject()) { return; } this.axisA = cloner.clone(axisA); this.axisB = cloner.clone(axisB); createJoint(); setAngularOnly(angularOnly); copyConstraintProperties(old); float low = old.getLowerLimit(); float high = old.getUpperLimit(); setLimit(low, high, limitSoftness, biasFactor, relaxationFactor); boolean enable = old.getEnableMotor(); float targetVelocity = old.getMotorTargetVelocity(); float maxImpulse = old.getMaxMotorImpulse(); enableMotor(enable, targetVelocity, maxImpulse); } /** * De-serialize this joint from the specified importer, for example when * loading from a J3O file. * * @param importer (not null) * @throws IOException from the importer */ @Override public void read(JmeImporter importer) throws IOException { super.read(importer); InputCapsule capsule = importer.getCapsule(this); this.axisA = (Vector3f) capsule.readSavable(tagAxisA, new Vector3f()); this.axisB = (Vector3f) capsule.readSavable(tagAxisB, new Vector3f()); this.angularOnly = capsule.readBoolean(tagAngularOnly, false); float lowerLimit = capsule.readFloat(tagLowerLimit, 1e30f); float upperLimit = capsule.readFloat(tagUpperLimit, -1e30f); this.biasFactor = capsule.readFloat(tagBiasFactor, 0.3f); this.relaxationFactor = capsule.readFloat(tagRelaxationFactor, 1f); this.limitSoftness = capsule.readFloat(tagLimitSoftness, 0.9f); createJoint(); readConstraintProperties(capsule); boolean enableAngularMotor = capsule.readBoolean(tagEnableAngularMotor, false); float targetVelocity = capsule.readFloat(tagTargetVelocity, 0f); float maxMotorImpulse = capsule.readFloat(tagMaxMotorImpulse, 0f); enableMotor(enableAngularMotor, targetVelocity, maxMotorImpulse); setAngularOnly(angularOnly); setLimit(lowerLimit, upperLimit, limitSoftness, biasFactor, relaxationFactor); } /** * Serialize this joint to the specified exporter, for example when saving * to a J3O file. * * @param exporter (not null) * @throws IOException from the exporter */ @Override public void write(JmeExporter exporter) throws IOException { super.write(exporter); OutputCapsule capsule = exporter.getCapsule(this); capsule.write(axisA, tagAxisA, null); capsule.write(axisB, tagAxisB, null); capsule.write(angularOnly, tagAngularOnly, false); capsule.write(getLowerLimit(), tagLowerLimit, 1e30f); capsule.write(getUpperLimit(), tagUpperLimit, -1e30f); capsule.write(biasFactor, tagBiasFactor, 0.3f); capsule.write(relaxationFactor, tagRelaxationFactor, 1f); capsule.write(limitSoftness, tagLimitSoftness, 0.9f); capsule.write(getEnableMotor(), tagEnableAngularMotor, false); capsule.write(getMotorTargetVelocity(), tagTargetVelocity, 0f); capsule.write(getMaxMotorImpulse(), tagMaxMotorImpulse, 0f); } // ************************************************************************* // Java private methods /** * Create the configured joint in Bullet. */ private void createJoint() { PhysicsRigidBody a = getBodyA(); long aId = a.nativeId(); assert pivotA != null; assert axisA.isUnitVector() : axisA; assert pivotB != null; assert axisB.isUnitVector() : axisB; PhysicsRigidBody b = getBodyB(); long constraintId; if (b == null) { /* * Create a single-ended joint. Bullet assumes single-ended * btHingeConstraints are satisfied at creation, so we * temporarily re-position the body to satisfy the constraint. */ Vector3f saveLocation = a.getPhysicsLocation(null); Quaternion saveRotation = a.getPhysicsRotation(null); Vector3f cross = axisB.cross(axisA); float sinAngle = cross.length(); float cosAngle = axisB.dot(axisA); float angle = FastMath.atan2(sinAngle, cosAngle); MyVector3f.normalizeLocal(cross); Quaternion rotation = new Quaternion(); rotation.fromAngleNormalAxis(angle, cross); a.setPhysicsRotation(rotation); Vector3f offset = pivotB.subtract(pivotA); a.setPhysicsLocation(offset); constraintId = createJoint1(aId, pivotA, axisA, useReferenceFrameA); a.setPhysicsLocation(saveLocation); a.setPhysicsRotation(saveRotation); } else { // Create a double-ended joint. assert !useReferenceFrameA; long bId = b.nativeId(); constraintId = createJoint(aId, bId, pivotA, axisA, pivotB, axisB); } assert getConstraintType(constraintId) == 4; setNativeId(constraintId); } // ************************************************************************* // native private methods native private static long createJoint(long objectIdA, long objectIdB, Vector3f pivotInA, Vector3f axisInA, Vector3f pivotInB, Vector3f axisInB); native private static long createJoint1(long objectIdA, Vector3f pivotInA, Vector3f axisInA, boolean useReferenceFrameA); native private static void enableMotor(long jointId, boolean enable, float targetVelocity, float maxMotorImpulse); native private static boolean getEnableAngularMotor(long jointId); native private static void getFrameOffsetA(long jointId, Transform frameInA); native private static void getFrameOffsetB(long jointId, Transform frameInB); native private static float getHingeAngle(long jointId); native private static float getLowerLimit(long jointId); native private static float getMaxMotorImpulse(long jointId); native private static float getMotorTargetVelocity(long jointId); native private static float getUpperLimit(long jointId); native private static void setAngularOnly(long jointId, boolean angularOnly); native private static void setLimit(long jointId, float low, float high, float softness, float biasFactor, float relaxationFactor); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy