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

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

There is a newer version: 8.2.0+mt
Show newest version
/*
 * Copyright (c) 2019-2022 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.PhysicsBody;
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.Vector3f;
import com.jme3.util.clone.Cloner;
import java.io.IOException;
import java.util.logging.Logger;
import jme3utilities.Validate;

/**
 * The abstract base class for rigid-body physics joints based on Bullet's
 * btTypedConstraint. A Constraint can be single-ended or double-ended:
 * 
    *
  • A single-ended Constraint constrains the motion of a dynamic rigid * body.
  • *
  • A double-ended Constraint connects 2 rigid bodies together in the same * PhysicsSpace. One or both of the bodies must be dynamic.
  • *
* Subclasses include: ConeJoint, GearJoint, HingeJoint, New6Dof, * Point2PointJoint, SixDofJoint, SixDofSpringJoint, and SliderJoint. * * @author Stephen Gold [email protected] * * Based on PhysicsJoint by normenhansen. */ abstract public class Constraint extends PhysicsJoint { // ************************************************************************* // constants and loggers /** * message logger for this class */ final public static Logger logger15 = Logger.getLogger(Constraint.class.getName()); /** * field names for serialization */ final private static String tagBreakingImpulse = "breakingImpulseThreshold"; final private static String tagIsCollision = "isCollisionBetweenLinkedBodies"; final private static String tagIsEnabled = "isEnabled"; final private static String tagIsFeedback = "isFeedback"; final private static String tagNumIterations = "numIterations"; final private static String tagPivotA = "pivotA"; final private static String tagPivotB = "pivotB"; // ************************************************************************* // fields /** * copy of the pivot location: in physics-space coordinates if bodyA is * null, or else in A's scaled local coordinates */ protected Vector3f pivotA; /** * copy of the pivot location: in physics-space coordinates if bodyB is * null, or else in B's scaled local coordinates */ protected Vector3f pivotB; // ************************************************************************* // constructors /** * No-argument constructor needed by SavableClassUtil. */ protected Constraint() { } /** * Instantiate an enabled, single-ended Constraint using the specified body * at the specified end. *

* To be effective, the Constraint must be added to the body's PhysicsSpace * and the body must be dynamic. * * @param body the body to constrain (not null, alias created) * @param bodyEnd at which end to attach the body (not null) * @param pivotInBody the pivot location in the body's scaled local * coordinates (not null, unaffected) */ protected Constraint( PhysicsRigidBody body, JointEnd bodyEnd, Vector3f pivotInBody) { Validate.nonNull(body, "body"); Validate.nonNull(bodyEnd, "body end"); Validate.nonNull(pivotInBody, "pivot in body"); switch (bodyEnd) { case A: setBodyA(body); this.pivotA = pivotInBody.clone(); this.pivotB = null; break; case B: setBodyB(body); this.pivotA = null; this.pivotB = pivotInBody.clone(); break; default: String message = "body end = " + bodyEnd; throw new IllegalArgumentException(message); } body.addJoint(this); } /** * Instantiate an enabled, single-ended Constraint using the specified body * at the specified end. *

* To be effective, the Constraint must be added to the body's PhysicsSpace * and the body must be dynamic. * * @param body the body to constrain (not null, alias created) * @param bodyEnd at which end to attach the body (not null) * @param pivotInBody the pivot location in the body's scaled local * coordinates (not null, unaffected) * @param pivotInWorld the pivot location in physics-space coordinates (not * null, unaffected) */ protected Constraint(PhysicsRigidBody body, JointEnd bodyEnd, Vector3f pivotInBody, Vector3f pivotInWorld) { Validate.nonNull(body, "body"); Validate.nonNull(bodyEnd, "body end"); Validate.finite(pivotInBody, "pivot in body"); Validate.finite(pivotInWorld, "pivot in world"); switch (bodyEnd) { case A: setBodyA(body); this.pivotA = pivotInBody.clone(); this.pivotB = pivotInWorld.clone(); break; case B: setBodyB(body); this.pivotA = pivotInWorld.clone(); this.pivotB = pivotInBody.clone(); break; default: String message = "body end = " + bodyEnd; throw new IllegalArgumentException(message); } body.addJoint(this); } /** * Instantiate an enabled, double-ended Constraint. *

* To be effective, the Constraint 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 bodyA the body for the A end (not null, alias created) * @param bodyB 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) */ protected Constraint(PhysicsBody bodyA, PhysicsBody bodyB, Vector3f pivotInA, Vector3f pivotInB) { Validate.nonNull(bodyA, "body A"); Validate.nonNull(bodyB, "body B"); if (bodyA == bodyB) { throw new IllegalArgumentException( "The jointed bodies must be distinct."); } setBodyA(bodyA); setBodyB(bodyB); this.pivotA = pivotInA.clone(); this.pivotB = pivotInB.clone(); bodyA.addJoint(this); bodyB.addJoint(this); } // ************************************************************************* // new methods exposed /** * Determine the magnitude of the applied impulse. Requires feedback. * * @return impulse magnitude (≥0) * @throws IllegalStateException if feedback is not enabled */ public float getAppliedImpulse() { if (!isFeedback()) { throw new IllegalStateException("Feedback is not enabled."); } long constraintId = nativeId(); float result = getAppliedImpulse(constraintId); assert result >= 0f : result; return result; } /** * Determine the breaking impulse threshold. * * @return the threshold value */ public float getBreakingImpulseThreshold() { long constraintId = nativeId(); float result = getBreakingImpulseThreshold(constraintId); return result; } /** * Determine the number of iterations used to solve this Constraint. * * @return the number of iterations (≥0) or -1 if using the solver's * default */ public int getOverrideIterations() { long constraintId = nativeId(); int result = getOverrideIterations(constraintId); return result; } /** * Copy the location of the specified connection point in the specified * body. * * @param end which end of the Constraint to access (not null) * @param storeResult storage for the result (modified if not null) * @return a location vector (in scaled local coordinates, either * storeResult or a new instance) */ public Vector3f getPivot(JointEnd end, Vector3f storeResult) { Validate.nonNull(end, "end"); Vector3f result; switch (end) { case A: result = getPivotA(storeResult); break; case B: result = getPivotB(storeResult); break; default: throw new IllegalArgumentException("end = " + end); } return result; } /** * Copy the location of the connection point in the body at the A end. * * @param storeResult storage for the result (modified if not null) * @return a location vector (in A's scaled local coordinates, either * storeResult or a new instance) */ public Vector3f getPivotA(Vector3f storeResult) { Vector3f result = (storeResult == null) ? new Vector3f() : storeResult; if (getBodyA() == null) { throw new IllegalArgumentException("No body at the A end."); } result.set(pivotA); return result; } /** * Copy the location of the connection point in the body at the B end. * * @param storeResult storage for the result (modified if not null) * @return a location vector (in B's scaled local coordinates, either * storeResult or a new instance) */ public Vector3f getPivotB(Vector3f storeResult) { Vector3f result = (storeResult == null) ? new Vector3f() : storeResult; if (getBodyB() == null) { throw new IllegalArgumentException("No body at the B end."); } result.set(pivotB); return result; } /** * Test whether collisions are handled between the ends. * * @return true if collisions are handled, otherwise false */ public boolean isCollisionBetweenLinkedBodies() { boolean result = true; if (countEnds() == 2) { PhysicsBody a = getBodyA(); PhysicsBody b = getBodyB(); result = !a.ignores(b); } return result; } /** * Test whether this Constraint has feedback enabled. * * @return true if enabled, otherwise false */ public boolean isFeedback() { long constraintId = nativeId(); boolean result = needsFeedback(constraintId); return result; } /** * Override the number of iterations used to solve this Constraint. * * @param numIterations the desired number of iterations (≥0) or -1 to * use the solver's default (default=-1) */ public void overrideIterations(int numIterations) { Validate.inRange(numIterations, "number of iterations", -1, Integer.MAX_VALUE); long constraintId = nativeId(); overrideIterations(constraintId, numIterations); } /** * Alter the breaking impulse threshold. * * @param desiredThreshold the desired value (default=MAX_VALUE with SP * library or +Infinity with DP library) */ public void setBreakingImpulseThreshold(float desiredThreshold) { long constraintId = nativeId(); setBreakingImpulseThreshold(constraintId, desiredThreshold); } /** * Handle/ignore collisions between the ends of a double-ended joint. * * @param allow true to handle collisions, false to ignore them * (default=true) */ public void setCollisionBetweenLinkedBodies(boolean allow) { if (countEnds() < 2) { String msg = "Can't configure collisions between linked bodies " + "for a single-ended constraint!"; throw new IllegalArgumentException(msg); } PhysicsBody a = getBodyA(); PhysicsBody b = getBodyB(); if (allow) { a.removeFromIgnoreList(b); } else { a.addToIgnoreList(b); } assert isCollisionBetweenLinkedBodies() == allow; } /** * Enable or disable this Constraint. * * @param enable true to enable, false to disable (default=true) */ public void setEnabled(boolean enable) { long constraintId = nativeId(); setEnabled(constraintId, enable); } /** * Enable or disable feedback for this Constraint. * * @param enable true to enable, false to disable (default=false) */ public void setFeedback(boolean enable) { long constraintId = nativeId(); enableFeedback(constraintId, enable); } // ************************************************************************* // new protected methods /** * Copy common properties from another constraint. Used during cloning. * * @param old (not null, unaffected) */ final protected void copyConstraintProperties(Constraint old) { assert old.hasAssignedNativeObject(); assert old.nativeId() != nativeId(); float bit = old.getBreakingImpulseThreshold(); setBreakingImpulseThreshold(bit); boolean enableConstraint = old.isEnabled(); setEnabled(enableConstraint); boolean enableFeedback = old.isFeedback(); setFeedback(enableFeedback); int numIterations = old.getOverrideIterations(); overrideIterations(numIterations); } /** * Read the constraint type. * * @param constraintId identifier of the {@code btTypedConstraint} (not * zero) * @return a btTypedConstraintType ordinal value (≥3) */ final native protected static int getConstraintType(long constraintId); /** * Read common properties from a capsule. * * @param capsule the input capsule (not null, modified) * @throws IOException from the importer */ final protected void readConstraintProperties(InputCapsule capsule) throws IOException { float breakingImpulse = capsule.readFloat(tagBreakingImpulse, Float.MAX_VALUE); setBreakingImpulseThreshold(breakingImpulse); if (countEnds() == 2) { boolean isCollision = capsule.readBoolean(tagIsCollision, true); setCollisionBetweenLinkedBodies(isCollision); } boolean isEnabled = capsule.readBoolean(tagIsEnabled, true); setEnabled(isEnabled); boolean isFeedback = capsule.readBoolean(tagIsFeedback, false); setFeedback(isFeedback); int numIterations = capsule.readInt(tagNumIterations, -1); overrideIterations(numIterations); } /** * Alter the pivot location in A's scaled local coordinates. The subclass is * responsible for updating the native object. * * @param location the desired location (not null, unaffected) */ protected void setPivotInA(Vector3f location) { Validate.nonNull(location, "location"); pivotA.set(location); } /** * Alter the pivot location in B's scaled local coordinates. The subclass is * responsible for updating the native object. * * @param location the desired location (not null, unaffected) */ protected void setPivotInB(Vector3f location) { Validate.nonNull(location, "location"); pivotB.set(location); } // ************************************************************************* // PhysicsJoint methods /** * Callback from {@link com.jme3.util.clone.Cloner} to convert this * shallow-cloned Constraint into a deep-cloned one, using the specified * Cloner and original to resolve copied fields. * * @param cloner the Cloner that's cloning this Constraint (not null) * @param original the instance from which this Constraint was * shallow-cloned (unused) */ @Override public void cloneFields(Cloner cloner, Object original) { assert !hasAssignedNativeObject(); Constraint old = (Constraint) original; assert old != this; assert old.hasAssignedNativeObject(); super.cloneFields(cloner, original); if (hasAssignedNativeObject()) { return; } this.pivotA = cloner.clone(pivotA); this.pivotB = cloner.clone(pivotB); /* * Each subclass must create the btTypedConstraint * and invoke setNativeId(). */ } /** * Access the rigid body at the A end. * * @return the pre-existing rigid body, or null if none */ @Override public PhysicsRigidBody getBodyA() { PhysicsBody a = super.getBodyA(); PhysicsRigidBody result = null; if (a instanceof PhysicsRigidBody) { result = (PhysicsRigidBody) a; } return result; } /** * Access the rigid body at the B end. * * @return the pre-existing body, or null if none */ @Override public PhysicsRigidBody getBodyB() { PhysicsBody b = super.getBodyB(); PhysicsRigidBody result = null; if (b instanceof PhysicsRigidBody) { result = (PhysicsRigidBody) b; } return result; } /** * Test whether this Constraint is enabled. * * @return true if enabled, otherwise false */ @Override public boolean isEnabled() { long constraintId = nativeId(); boolean result = isEnabled(constraintId); return result; } /** * De-serialize this Constraint 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.pivotA = (Vector3f) capsule.readSavable(tagPivotA, null); this.pivotB = (Vector3f) capsule.readSavable(tagPivotB, null); /* * Each subclass must create the btTypedConstraint and then * invoke readConstraintProperties(). */ } /** * Serialize this Constraint 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(pivotA, tagPivotA, null); capsule.write(pivotB, tagPivotB, null); capsule.write(getBreakingImpulseThreshold(), tagBreakingImpulse, Float.MAX_VALUE); capsule.write(isCollisionBetweenLinkedBodies(), tagIsCollision, true); capsule.write(isEnabled(), tagIsEnabled, true); capsule.write(isFeedback(), tagIsFeedback, false); capsule.write(getOverrideIterations(), tagNumIterations, -1); } // ************************************************************************* // Java private methods /** * Free the identified tracked native object. Invoked by reflection. * * @param constraintId the native identifier (not zero) */ private static void freeNativeObject(long constraintId) { assert constraintId != 0L; finalizeNative(constraintId); } // ************************************************************************* // native private methods native private static void enableFeedback(long constraintId, boolean enable); native private static void finalizeNative(long constraintId); native private static float getAppliedImpulse(long constraintId); native private static float getBreakingImpulseThreshold(long constraintId); native private static int getOverrideIterations(long constraintId); native private static boolean isEnabled(long constraintId); native private static boolean needsFeedback(long constraintId); native private static void overrideIterations(long constraintId, int numIterations); native private static void setBreakingImpulseThreshold( long constraintId, float desiredThreshold); native private static void setEnabled(long constraintId, boolean enable); }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy