diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java b/jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java index 55b614904..9fb5b85f1 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/BulletAppState.java @@ -43,66 +43,136 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * BulletAppState allows using bullet physics in an Application. + * An app state to manage a single Bullet physics space. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class BulletAppState implements AppState, PhysicsTickListener { + /** + * true if-and-only-if the physics simulation is running (started but not + * yet stopped) + */ protected boolean initialized = false; protected Application app; + /** + * manager that manages this state, set during attach + */ protected AppStateManager stateManager; + /** + * executor service for physics tasks, or null if parallel simulation is not + * running + */ protected ScheduledThreadPoolExecutor executor; + /** + * physics space managed by this state, or null if no simulation running + */ protected PhysicsSpace pSpace; + /** + * threading mode to use (not null) + */ protected ThreadingType threadingType = ThreadingType.SEQUENTIAL; + /** + * broadphase collision-detection algorithm for the physics space to use + * (not null) + */ protected BroadphaseType broadphaseType = BroadphaseType.DBVT; + /** + * minimum coordinate values for the physics space when using AXIS_SWEEP + * broadphase algorithms (not null) + */ protected Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f); + /** + * maximum coordinate values for the physics space when using AXIS_SWEEP + * broadphase algorithms (not null) + */ protected Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f); + /** + * simulation speed multiplier (default=1, paused=0) + */ protected float speed = 1; + /** + * true if-and-only-if this state is enabled + */ protected boolean active = true; + /** + * true if-and-only-if debug visualization is enabled + */ protected boolean debugEnabled = false; + /** + * app state to manage the debug visualization, or null if none + */ protected BulletDebugAppState debugAppState; + /** + * time interval between frames (in seconds) from the most recent update + */ protected float tpf; + /** + * current physics task, or null if none + */ protected Future physicsFuture; /** - * Creates a new BulletAppState running a PhysicsSpace for physics - * simulation, use getStateManager().attach(bulletAppState) to enable - * physics for an Application. + * Instantiate an app state to manage a new PhysicsSpace with DBVT collision + * detection. + *

+ * Use getStateManager().addState(bulletAppState) to start physics. */ public BulletAppState() { } /** - * Creates a new BulletAppState running a PhysicsSpace for physics - * simulation, use getStateManager().attach(bulletAppState) to enable - * physics for an Application. + * Instantiate an app state to manage a new PhysicsSpace. + *

+ * Use getStateManager().addState(bulletAppState) to start physics. * - * @param broadphaseType The type of broadphase collision detection, - * BroadphaseType.DVBT is the default + * @param broadphaseType which broadphase collision-detection algorithm to + * use (not null) */ public BulletAppState(BroadphaseType broadphaseType) { this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType); } /** - * Creates a new BulletAppState running a PhysicsSpace for physics - * simulation, use getStateManager().attach(bulletAppState) to enable - * physics for an Application. An AxisSweep broadphase is used. + * Instantiate an app state to manage a new PhysicsSpace with AXIS_SWEEP_3 + * collision detection. + *

+ * Use getStateManager().addState(bulletAppState) to start physics. * - * @param worldMin The minimum world extent - * @param worldMax The maximum world extent + * @param worldMin the desired minimum coordinate values (not null, + * unaffected, default=-10k,-10k,-10k) + * @param worldMax the desired maximum coordinate values (not null, + * unaffected, default=10k,10k,10k) */ public BulletAppState(Vector3f worldMin, Vector3f worldMax) { this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3); } + /** + * Instantiate an app state to manage a new PhysicsSpace. + *

+ * Use getStateManager().addState(bulletAppState) to enable physics. + * + * @param worldMin the desired minimum coordinate values (not null, + * unaffected, default=-10k,-10k,-10k) + * @param worldMax the desired maximum coordinate values (not null, + * unaffected, default=10k,10k,10k) + * @param broadphaseType which broadphase collision-detection algorithm to + * use (not null) + */ public BulletAppState(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) { this.worldMin.set(worldMin); this.worldMax.set(worldMax); this.broadphaseType = broadphaseType; } + /** + * Allocate the physics space and start physics for ThreadingType.PARALLEL. + * + * @return true if successful, otherwise false + */ private boolean startPhysicsOnExecutor() { if (executor != null) { executor.shutdown(); @@ -145,6 +215,12 @@ public class BulletAppState implements AppState, PhysicsTickListener { } }; + /** + * Access the PhysicsSpace managed by this state. Normally there is none + * until the state is attached. + * + * @return the pre-existing instance, or null if no simulation running + */ public PhysicsSpace getPhysicsSpace() { return pSpace; } @@ -179,6 +255,9 @@ public class BulletAppState implements AppState, PhysicsTickListener { initialized = true; } + /** + * Stop physics after this state is detached. + */ public void stopPhysics() { if(!initialized){ return; @@ -192,32 +271,72 @@ public class BulletAppState implements AppState, PhysicsTickListener { initialized = false; } + /** + * Initialize this state prior to its 1st update. Should be invoked only by + * a subclass or by the AppStateManager. + * + * @param stateManager the manager for this state (not null) + * @param app the application which owns this state (not null) + */ public void initialize(AppStateManager stateManager, Application app) { this.app = app; this.stateManager = stateManager; startPhysics(); } + /** + * Test whether the physics simulation is running (started but not yet + * stopped). + * + * @return true if running, otherwise false + */ public boolean isInitialized() { return initialized; } + /** + * Enable or disable this state. + * + * @param enabled true → enable, false → disable + */ public void setEnabled(boolean enabled) { this.active = enabled; } + /** + * Test whether this state is enabled. + * + * @return true if enabled, otherwise false + */ public boolean isEnabled() { return active; } + /** + * Alter whether debug visualization is enabled. + * + * @param debugEnabled true → enable, false → disable + */ public void setDebugEnabled(boolean debugEnabled) { this.debugEnabled = debugEnabled; } + + /** + * Test whether debug visualization is enabled. + * + * @return true if enabled, otherwise false + */ public boolean isDebugEnabled() { return debugEnabled; } + /** + * Transition this state from detached to initializing. Should be invoked + * only by a subclass or by the AppStateManager. + * + * @param stateManager (not null) + */ public void stateAttached(AppStateManager stateManager) { if (!initialized) { startPhysics(); @@ -231,9 +350,22 @@ public class BulletAppState implements AppState, PhysicsTickListener { } } + /** + * Transition this state from running to terminating. Should be invoked only + * by a subclass or by the AppStateManager. + * + * @param stateManager (not null) + */ public void stateDetached(AppStateManager stateManager) { } + /** + * Update this state prior to rendering. Should be invoked only by a + * subclass or by the AppStateManager. Invoked once per frame, provided the + * state is attached and enabled. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ public void update(float tpf) { if (debugEnabled && debugAppState == null && pSpace != null) { debugAppState = new BulletDebugAppState(pSpace); @@ -249,6 +381,13 @@ public class BulletAppState implements AppState, PhysicsTickListener { this.tpf = tpf; } + /** + * Render this state. Should be invoked only by a subclass or by the + * AppStateManager. Invoked once per frame, provided the state is attached + * and enabled. + * + * @param rm the render manager (not null) + */ public void render(RenderManager rm) { if (!active) { return; @@ -261,6 +400,11 @@ public class BulletAppState implements AppState, PhysicsTickListener { } } + /** + * Update this state after all rendering commands are flushed. Should be + * invoked only by a subclass or by the AppStateManager. Invoked once per + * frame, provided the state is attached and enabled. + */ public void postRender() { if (physicsFuture != null) { try { @@ -274,6 +418,12 @@ public class BulletAppState implements AppState, PhysicsTickListener { } } + /** + * Transition this state from terminating to detached. Should be invoked + * only by a subclass or by the AppStateManager. Invoked once for each time + * {@link #initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application)} + * is invoked. + */ public void cleanup() { if (debugAppState != null) { stateManager.detach(debugAppState); @@ -283,67 +433,106 @@ public class BulletAppState implements AppState, PhysicsTickListener { } /** - * @return the threadingType + * Read which type of threading this app state uses. + * + * @return the threadingType (not null) */ public ThreadingType getThreadingType() { return threadingType; } /** - * Use before attaching state + * Alter which type of threading this app state uses. Not allowed after + * attaching the app state. * - * @param threadingType the threadingType to set + * @param threadingType the desired type (not null, default=SEQUENTIAL) */ public void setThreadingType(ThreadingType threadingType) { this.threadingType = threadingType; } /** - * Use before attaching state + * Alter the broadphase type the physics space will use. Not allowed after + * attaching the app state. + * + * @param broadphaseType an enum value (not null, default=DBVT) */ public void setBroadphaseType(BroadphaseType broadphaseType) { this.broadphaseType = broadphaseType; } /** - * Use before attaching state + * Alter the coordinate range. Not allowed after attaching the app state. + * + * @param worldMin the desired minimum coordinate values when using + * AXIS_SWEEP broadphase algorithms (not null, alias created, + * default=-10k,-10k,-10k) */ public void setWorldMin(Vector3f worldMin) { this.worldMin = worldMin; } /** - * Use before attaching state + * Alter the coordinate range. Not allowed after attaching the app state. + * + * @param worldMax the desired maximum coordinate values when using + * AXIS_SWEEP broadphase algorithms (not null, alias created, + * default=10k,10k,10k) */ public void setWorldMax(Vector3f worldMax) { this.worldMax = worldMax; } + /** + * Read the simulation speed. + * + * @return speed (≥0, default=1) + */ public float getSpeed() { return speed; } + /** + * Alter the simulation speed. + * + * @param speed the desired speed (≥0, default=1) + */ public void setSpeed(float speed) { this.speed = speed; } - + /** + * Callback from Bullet, invoked just before the physics is stepped. A good + * time to clear/apply forces. + * + * @param space the space that is about to be stepped (not null) + * @param f the time per physics step (in seconds, ≥0) + */ public void prePhysicsTick(PhysicsSpace space, float f) { } + /** + * Callback from Bullet, invoked just after the physics is stepped. A good + * time to clear/apply forces. + * + * @param space the space that is about to be stepped (not null) + * @param f the time per physics step (in seconds, ≥0) + */ public void physicsTick(PhysicsSpace space, float f) { } + /** + * Enumerate threading modes. + */ public enum ThreadingType { /** - * Default mode; user update, physics update and rendering happen - * sequentially (single threaded) + * Default mode: user update, physics update, and rendering happen + * sequentially. (single threaded) */ SEQUENTIAL, /** - * Parallel threaded mode; physics update and rendering are executed in - * parallel, update order is kept.
Multiple BulletAppStates will - * execute in parallel in this mode. + * Parallel threaded mode: physics update and rendering are executed in + * parallel, update order is maintained. */ PARALLEL, } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/PhysicsTickListener.java b/jme3-bullet/src/common/java/com/jme3/bullet/PhysicsTickListener.java index 0a52a5119..7bbd704aa 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/PhysicsTickListener.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/PhysicsTickListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,22 +32,29 @@ package com.jme3.bullet; /** - * Implement this interface to be called from the physics thread on a physics update. + * Callback interface from the physics thread, used to clear/apply forces. + *

+ * This interface is shared between JBullet and Native Bullet. + * * @author normenhansen */ public interface PhysicsTickListener { /** - * Called before the physics is actually stepped, use to apply forces etc. - * @param space the physics space - * @param tpf the time per frame in seconds + * Callback from Bullet, invoked just before the physics is stepped. A good + * time to clear/apply forces. + * + * @param space the space that is about to be stepped (not null) + * @param tpf the time per physics step (in seconds, ≥0) */ public void prePhysicsTick(PhysicsSpace space, float tpf); /** - * Called after the physics has been stepped, use to check for forces etc. - * @param space the physics space - * @param tpf the time per frame in seconds + * Callback from Bullet, invoked just after the physics has been stepped, + * use to check for forces etc. + * + * @param space the space that was just stepped (not null) + * @param tpf the time per physics step (in seconds, ≥0) */ public void physicsTick(PhysicsSpace space, float tpf); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java b/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java index d5d010d87..4a7a0432d 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionGroupListener.java @@ -32,18 +32,25 @@ package com.jme3.bullet.collision; /** + * Interface to receive notifications whenever an object in a particular + * collision group is about to collide. + *

+ * This interface is shared between JBullet and Native Bullet. * * @author normenhansen */ public interface PhysicsCollisionGroupListener { /** - * Called when two physics objects of the registered group are about to collide, called from physics thread.
- * This is only called when the collision will happen based on the collisionGroup and collideWithGroups - * settings in the PhysicsCollisionObject. That is the case when one of the parties has the - * collisionGroup of the other in its collideWithGroups set.
- * @param nodeA CollisionObject #1 - * @param nodeB CollisionObject #2 + * Invoked when two physics objects of the registered group are about to + * collide. invoked on the physics thread.
+ * This is only invoked when the collision will happen based on the + * collisionGroup and collideWithGroups settings in the + * PhysicsCollisionObject. That is the case when one of the parties + * has the collisionGroup of the other in its collideWithGroups set. + * + * @param nodeA collision object #1 + * @param nodeB collision object #2 * @return true if the collision should happen, false otherwise */ public boolean collide(PhysicsCollisionObject nodeA, PhysicsCollisionObject nodeB); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionListener.java b/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionListener.java index b49e3627f..8bcaf4e22 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionListener.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/collision/PhysicsCollisionListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,16 +32,24 @@ package com.jme3.bullet.collision; /** - * Interface for Objects that want to be informed about collision events in the physics space + * Interface to receive notifications whenever an object in a particular physics + * space collides. + *

+ * This interface is shared between JBullet and Native Bullet. + * * @author normenhansen */ public interface PhysicsCollisionListener { /** - * Called when a collision happened in the PhysicsSpace, called from render thread. - * - * Do not store the event object as it will be cleared after the method has finished. - * @param event the CollisionEvent + * Invoked when a collision happened in the PhysicsSpace. Invoked on the + * render thread. + *

+ * Do not retain the event object, as it will be reused after the + * collision() method returns. Copy any data you need during the collide() + * method. + * + * @param event the event that occurred (not null, reusable) */ public void collision(PhysicsCollisionEvent event); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/collision/RagdollCollisionListener.java b/jme3-bullet/src/common/java/com/jme3/bullet/collision/RagdollCollisionListener.java index cb00aeff3..84e50cdae 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/collision/RagdollCollisionListener.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/collision/RagdollCollisionListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,11 +34,22 @@ package com.jme3.bullet.collision; import com.jme3.animation.Bone; /** + * Interface to receive notifications whenever a KinematicRagdollControl + * collides with another physics object. + *

+ * This interface is shared between JBullet and Native Bullet. * * @author Nehon */ public interface RagdollCollisionListener { + /** + * Invoked when a collision involving a KinematicRagdollControl occurs. + * + * @param bone the ragdoll bone that collided (not null) + * @param object the collision object that collided with the bone (not null) + * @param event other event details (not null) + */ public void collide(Bone bone, PhysicsCollisionObject object, PhysicsCollisionEvent event); } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java b/jme3-bullet/src/common/java/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java index ac7196258..1ca8bc37c 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/collision/shapes/infos/ChildCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,24 +39,56 @@ import com.jme3.math.Vector3f; import java.io.IOException; /** + * An element of a CompoundCollisionShape, consisting of a (non-compound) child + * shape, offset and rotated with respect to its parent. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class ChildCollisionShape implements Savable { + /** + * translation relative to parent shape (not null) + */ public Vector3f location; + /** + * rotation relative to parent shape (not null) + */ public Matrix3f rotation; + /** + * base shape (not null, not a compound shape) + */ public CollisionShape shape; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public ChildCollisionShape() { } + /** + * Instantiate a child shape for use in a compound shape. + * + * @param location translation relative to the parent (not null, alias + * created) + * @param rotation rotation relative to the parent (not null, alias created) + * @param shape the base shape (not null, not a compound shape, alias + * created) + */ public ChildCollisionShape(Vector3f location, Matrix3f rotation, CollisionShape shape) { this.location = location; this.rotation = rotation; this.shape = shape; } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { OutputCapsule capsule = ex.getCapsule(this); capsule.write(location, "location", new Vector3f()); @@ -64,6 +96,12 @@ public class ChildCollisionShape implements Savable { capsule.write(shape, "shape", new BoxCollisionShape(new Vector3f(1, 1, 1))); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { InputCapsule capsule = im.getCapsule(this); location = (Vector3f) capsule.readSavable("location", new Vector3f()); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/AbstractPhysicsControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/AbstractPhysicsControl.java index 2f17aed29..3d68c5c66 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/AbstractPhysicsControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/AbstractPhysicsControl.java @@ -47,82 +47,113 @@ import com.jme3.util.clone.JmeCloneable; import java.io.IOException; /** - * AbstractPhysicsControl manages the lifecycle of a physics object that is - * attached to a spatial in the SceneGraph. + * Manage the life cycle of a physics object linked to a spatial in a scene + * graph. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeCloneable { + /** + * temporary storage during calculations + */ private final Quaternion tmp_inverseWorldRotation = new Quaternion(); + /** + * spatial to which this control is added, or null if none + */ protected Spatial spatial; + /** + * true→control is enabled, false→control is disabled + */ protected boolean enabled = true; + /** + * true→body is added to the physics space, false→not added + */ protected boolean added = false; + /** + * space to which the physics object is (or would be) added + */ protected PhysicsSpace space = null; + /** + * true → physics coordinates match local transform, false → + * physics coordinates match world transform + */ protected boolean applyLocal = false; /** - * Called when the control is added to a new spatial, create any - * spatial-dependent data here. + * Create spatial-dependent data. Invoked when this control is added to a + * spatial. * - * @param spat The new spatial, guaranteed not to be null + * @param spat the controlled spatial (not null) */ protected abstract void createSpatialData(Spatial spat); /** - * Called when the control is removed from a spatial, remove any - * spatial-dependent data here. + * Destroy spatial-dependent data. Invoked when this control is removed from + * a spatial. * - * @param spat The old spatial, guaranteed not to be null + * @param spat the previously controlled spatial (not null) */ protected abstract void removeSpatialData(Spatial spat); /** - * Called when the physics object is supposed to move to the spatial - * position. + * Translate the physics object to the specified location. * - * @param vec + * @param vec desired location (not null, unaffected) */ protected abstract void setPhysicsLocation(Vector3f vec); /** - * Called when the physics object is supposed to move to the spatial - * rotation. + * Rotate the physics object to the specified orientation. * - * @param quat + * @param quat desired orientation (not null, unaffected) */ protected abstract void setPhysicsRotation(Quaternion quat); /** - * Called when the physics object is supposed to add all objects it needs to - * manage to the physics space. + * Add all managed physics objects to the specified space. * - * @param space + * @param space which physics space to add to (not null) */ protected abstract void addPhysics(PhysicsSpace space); /** - * Called when the physics object is supposed to remove all objects added to - * the physics space. + * Remove all managed physics objects from the specified space. * - * @param space + * @param space which physics space to remove from (not null) */ protected abstract void removePhysics(PhysicsSpace space); + /** + * Test whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @return true if matching local coordinates, false if matching world + * coordinates + */ public boolean isApplyPhysicsLocal() { return applyLocal; } /** - * When set to true, the physics coordinates will be applied to the local - * translation of the Spatial + * Alter whether physics-space coordinates should match the spatial's local + * coordinates. * - * @param applyPhysicsLocal + * @param applyPhysicsLocal true→match local coordinates, + * false→match world coordinates (default=false) */ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) { applyLocal = applyPhysicsLocal; } + /** + * Access whichever spatial translation corresponds to the physics location. + * + * @return the pre-existing location vector (in physics-space coordinates, + * not null) + */ protected Vector3f getSpatialTranslation() { if (applyLocal) { return spatial.getLocalTranslation(); @@ -130,6 +161,12 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone return spatial.getWorldTranslation(); } + /** + * Access whichever spatial rotation corresponds to the physics rotation. + * + * @return the pre-existing quaternion (in physics-space coordinates, not + * null) + */ protected Quaternion getSpatialRotation() { if (applyLocal) { return spatial.getLocalRotation(); @@ -138,10 +175,12 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone } /** - * Applies a physics transform to the spatial + * Apply a physics transform to the spatial. * - * @param worldLocation - * @param worldRotation + * @param worldLocation location vector (in physics-space coordinates, not + * null, unaffected) + * @param worldRotation orientation (in physics-space coordinates, not null, + * unaffected) */ protected void applyPhysicsTransform(Vector3f worldLocation, Quaternion worldRotation) { if (enabled && spatial != null) { @@ -170,12 +209,28 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone throw new UnsupportedOperationException(); } + /** + * Callback from {@link com.jme3.util.clone.Cloner} to convert this + * shallow-cloned control into a deep-cloned one, using the specified cloner + * and original to resolve copied fields. + * + * @param cloner the cloner currently cloning this control (not null) + * @param original the control from which this control was shallow-cloned + * (unused) + */ @Override public void cloneFields( Cloner cloner, Object original ) { this.spatial = cloner.clone(spatial); createSpatialData(this.spatial); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ public void setSpatial(Spatial spatial) { if (this.spatial != null && this.spatial != spatial) { removeSpatialData(this.spatial); @@ -191,6 +246,15 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone setPhysicsRotation(getSpatialRotation()); } + /** + * Enable or disable this control. + *

+ * When the control is disabled, the physics object is removed from physics + * space. When the control is enabled again, the physics object is moved to + * the spatial's location and then added to the physics space. + * + * @param enabled true→enable the control, false→disable it + */ public void setEnabled(boolean enabled) { this.enabled = enabled; if (space != null) { @@ -208,6 +272,11 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone } } + /** + * Test whether this control is enabled. + * + * @return true if enabled, otherwise false + */ public boolean isEnabled() { return enabled; } @@ -221,7 +290,7 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone /** * If enabled, add this control's physics object to the specified physics * space. If not enabled, alter where the object would be added. The object - * is removed from any other space it's currently in. + * is removed from any other space it's in. * * @param newSpace where to add, or null to simply remove */ @@ -245,10 +314,21 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone space = newSpace; } + /** + * Access the physics space to which the object is (or would be) added. + * + * @return the pre-existing space, or null for none + */ public PhysicsSpace getPhysicsSpace() { return space; } + /** + * Serialize this object, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); @@ -257,6 +337,13 @@ public abstract class AbstractPhysicsControl implements PhysicsControl, JmeClone oc.write(spatial, "spatial", null); } + /** + * De-serialize this control from the specified importer, for example when + * loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { InputCapsule ic = im.getCapsule(this); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java index 660b7f559..d9437e50b 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/BetterCharacterControl.java @@ -56,15 +56,18 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * This is intended to be a replacement for the internal bullet character class. - * A RigidBody with cylinder collision shape is used and its velocity is set - * continuously, a ray test is used to check if the character is on the ground. - * - * The character keeps his own local coordinate system which adapts based on the - * gravity working on the character so the character will always stand upright. - * - * Forces in the local x/z plane are dampened while those in the local y - * direction are applied fully (e.g. jumping, falling). + * This class is intended to replace the CharacterControl class. + *

+ * A rigid body with cylinder collision shape is used and its velocity is set + * continuously. A ray test is used to test whether the character is on the + * ground. + *

+ * The character keeps their own local coordinate system which adapts based on + * the gravity working on the character so they will always stand upright. + *

+ * Motion in the local X-Z plane is damped. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ @@ -74,10 +77,16 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph protected PhysicsRigidBody rigidBody; protected float radius; protected float height; + /** + * mass of this character (>0) + */ protected float mass; + /** + * relative height when ducked (1=full height) + */ protected float duckedFactor = 0.6f; /** - * Local up direction, derived from gravity. + * local up direction, derived from gravity */ protected final Vector3f localUp = new Vector3f(0, 1, 0); /** @@ -94,22 +103,27 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph */ protected final Quaternion localForwardRotation = new Quaternion(Quaternion.DIRECTION_Z); /** - * Is a z-forward vector based on the view direction and the current local - * x/z plane. + * a Z-forward vector based on the view direction and the local X-Z plane. */ protected final Vector3f viewDirection = new Vector3f(0, 0, 1); /** - * Stores final spatial location, corresponds to RigidBody location. + * spatial location, corresponds to RigidBody location. */ protected final Vector3f location = new Vector3f(); /** - * Stores final spatial rotation, is a z-forward rotation based on the view - * direction and the current local x/z plane. See also rotatedViewDirection. + * spatial rotation, a Z-forward rotation based on the view direction and + * local X-Z plane. + * + * @see #rotatedViewDirection */ protected final Quaternion rotation = new Quaternion(Quaternion.DIRECTION_Z); protected final Vector3f rotatedViewDirection = new Vector3f(0, 0, 1); protected final Vector3f walkDirection = new Vector3f(); protected final Vector3f jumpForce; + /** + * X-Z motion damping factor (0→no damping, 1=no external forces, + * default=0.9) + */ protected float physicsDamping = 0.9f; protected final Vector3f scale = new Vector3f(1, 1, 1); protected final Vector3f velocity = new Vector3f(); @@ -119,20 +133,23 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph protected boolean wantToUnDuck = false; /** - * Only used for serialization, do not use this constructor. + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! */ public BetterCharacterControl() { jumpForce = new Vector3f(); } /** - * Creates a new character with the given properties. Note that to avoid - * issues the final height when ducking should be larger than 2x radius. The - * jumpForce will be set to an upwards force of 5x mass. + * Instantiate an enabled control with the specified properties. + *

+ * The final height when ducking must be larger than 2x radius. The + * jumpForce will be set to an upward force of 5x mass. * - * @param radius - * @param height - * @param mass + * @param radius the radius of the character's collision shape (>0) + * @param height the height of the character's collision shape + * (>2*radius) + * @param mass the character's mass (≥0) */ public BetterCharacterControl(float radius, float height, float mass) { this.radius = radius; @@ -143,6 +160,13 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph rigidBody.setAngularFactor(0); } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is added to a scene graph. Do not invoke + * directly from user code. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ @Override public void update(float tpf) { super.update(tpf); @@ -151,16 +175,24 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph applyPhysicsTransform(location, rotation); } + /** + * Render this control. Invoked once per view port per frame, provided the + * control is added to a scene. Should be invoked only by a subclass or by + * the RenderManager. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ @Override public void render(RenderManager rm, ViewPort vp) { super.render(rm, vp); } /** - * Used internally, don't call manually + * Callback from Bullet, invoked just before the physics is stepped. * - * @param space - * @param tpf + * @param space the space that is about to be stepped (not null) + * @param tpf the time per physics step (in seconds, ≥0) */ public void prePhysicsTick(PhysicsSpace space, float tpf) { checkOnGround(); @@ -172,8 +204,8 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph TempVars vars = TempVars.get(); Vector3f currentVelocity = vars.vect2.set(velocity); - - // dampen existing x/z forces + + // Attenuate any existing X-Z motion. float existingLeftVelocity = velocity.dot(localLeft); float existingForwardVelocity = velocity.dot(localForward); Vector3f counter = vars.vect1; @@ -208,20 +240,20 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Used internally, don't call manually + * Callback from Bullet, invoked just after the physics has been stepped. * - * @param space - * @param tpf + * @param space the space that was just stepped (not null) + * @param tpf the time per physics step (in seconds, ≥0) */ public void physicsTick(PhysicsSpace space, float tpf) { rigidBody.getLinearVelocity(velocity); } /** - * Move the character somewhere. Note the character also takes the location - * of any spatial its being attached to in the moment it is attached. + * Move the character somewhere. Note the character also warps to the + * location of the spatial when the control is added. * - * @param vec The new character location. + * @param vec the desired character location (not null) */ public void warp(Vector3f vec) { setPhysicsLocation(vec); @@ -239,21 +271,20 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Set the jump force as a Vector3f. The jump force is local to the - * characters coordinate system, which normally is always z-forward (in - * world coordinates, parent coordinates when set to applyLocalPhysics) + * Alter the jump force. The jump force is local to the character's + * coordinate system, which normally is always z-forward (in world + * coordinates, parent coordinates when set to applyLocalPhysics) * - * @param jumpForce The new jump force + * @param jumpForce the desired jump force (not null, unaffected) */ public void setJumpForce(Vector3f jumpForce) { this.jumpForce.set(jumpForce); } /** - * Gets the current jump force. The default is 5 * character mass in y - * direction. + * Access the jump force. The default is 5 * character mass in Y direction. * - * @return + * @return the pre-existing vector (not null) */ public Vector3f getJumpForce() { return jumpForce; @@ -264,7 +295,7 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph * in the center of the character and might return false even if the * character is not falling yet. * - * @return + * @return true if on the ground, otherwise false */ public boolean isOnGround() { return onGround; @@ -277,7 +308,7 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph * can in fact unduck and only do so when its possible. You can check the * state of the unducking by checking isDucked(). * - * @param enabled + * @param enabled true→duck, false→unduck */ public void setDucked(boolean enabled) { if (enabled) { @@ -298,33 +329,33 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph * Check if the character is ducking, either due to user input or due to * unducking being impossible at the moment (obstacle above). * - * @return + * @return true if ducking, otherwise false */ public boolean isDucked() { return ducked; } /** - * Sets the height multiplication factor for ducking. + * Alter the height multiplier for ducking. * - * @param factor The factor by which the height should be multiplied when - * ducking + * @param factor the factor by which the height should be multiplied when + * ducking (≥0, ≤1) */ public void setDuckedFactor(float factor) { duckedFactor = factor; } /** - * Gets the height multiplication factor for ducking. + * Read the height multiplier for ducking. * - * @return + * @return the factor (≥0, ≤1) */ public float getDuckedFactor() { return duckedFactor; } /** - * Sets the walk direction of the character. This parameter is framerate + * Alter the character's the walk direction. This parameter is framerate * independent and the character will move continuously in the direction * given by the vector with the speed given by the vector length in m/s. * @@ -335,20 +366,19 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Gets the current walk direction and speed of the character. The length of - * the vector defines the speed. + * Read the walk velocity. The length of the vector defines the speed. * - * @return + * @return the pre-existing vector (not null) */ public Vector3f getWalkDirection() { return walkDirection; } /** - * Sets the view direction for the character. Note this only defines the - * rotation of the spatial in the local x/z plane of the character. + * Alter the character's view direction. Note this only defines the + * orientation in the local X-Z plane. * - * @param vec + * @param vec a direction vector (not null, unaffected) */ public void setViewDirection(Vector3f vec) { viewDirection.set(vec); @@ -356,10 +386,10 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Gets the current view direction, note this doesn't need to correspond - * with the spatials forward direction. + * Access the view direction. This need not agree with the spatial's forward + * direction. * - * @return + * @return the pre-existing vector (not null) */ public Vector3f getViewDirection() { return viewDirection; @@ -367,15 +397,15 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph /** * Realign the local forward vector to given direction vector, if null is - * supplied Vector3f.UNIT_Z is used. Input vector has to be perpendicular to - * current gravity vector. This normally only needs to be called when the + * supplied Vector3f.UNIT_Z is used. The input vector must be perpendicular + * to gravity vector. This normally only needs to be invoked when the * gravity direction changed continuously and the local forward vector is * off due to drift. E.g. after walking around on a sphere "planet" for a - * while and then going back to a y-up coordinate system the local z-forward - * might not be 100% alinged with Z axis. + * while and then going back to a Y-up coordinate system the local Z-forward + * might not be 100% aligned with the Z axis. * - * @param vec The new forward vector, has to be perpendicular to the current - * gravity vector! + * @param vec the desired forward vector (perpendicular to the gravity + * vector, may be null, default=0,0,1) */ public void resetForward(Vector3f vec) { if (vec == null) { @@ -386,23 +416,21 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Get the current linear velocity along the three axes of the character. - * This is prepresented in world coordinates, parent coordinates when the - * control is set to applyLocalPhysics. + * Access the character's linear velocity in physics-space coordinates. * - * @return The current linear velocity of the character + * @return the pre-existing vector (not null) */ public Vector3f getVelocity() { return velocity; } /** - * Set the gravity for this character. Note that this also realigns the - * local coordinate system of the character so that continuous changes in - * gravity direction are possible while maintaining a sensible control over - * the character. + * Alter the gravity acting on this character. Note that this also realigns + * the local coordinate system of the character so that continuous changes + * in gravity direction are possible while maintaining a sensible control + * over the character. * - * @param gravity + * @param gravity an acceleration vector (not null, unaffected) */ public void setGravity(Vector3f gravity) { rigidBody.setGravity(gravity); @@ -411,46 +439,48 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Get the current gravity of the character. + * Copy the character's gravity vector. * - * @return + * @return a new acceleration vector (not null) */ public Vector3f getGravity() { return rigidBody.getGravity(); } /** - * Get the current gravity of the character. + * Copy the character's gravity vector. * - * @param store The vector to store the result in - * @return + * @param store storage for the result (modified if not null) + * @return an acceleration vector (either the provided storage or a new + * vector, not null) */ public Vector3f getGravity(Vector3f store) { return rigidBody.getGravity(store); } /** - * Sets how much the physics forces in the local x/z plane should be - * dampened. - * @param physicsDamping The dampening value, 0 = no dampening, 1 = no external force, default = 0.9 + * Alter how much motion in the local X-Z plane is damped. + * + * @param physicsDamping the desired damping factor (0→no damping, 1=no + * external forces, default=0.9) */ public void setPhysicsDamping(float physicsDamping) { this.physicsDamping = physicsDamping; } /** - * Gets how much the physics forces in the local x/z plane should be - * dampened. + * Read how much motion in the local X-Z plane is damped. + * + * @return the damping factor (0→no damping, 1=no external forces) */ public float getPhysicsDamping() { return physicsDamping; } /** - * This actually sets a new collision shape to the character to change the - * height of the capsule. + * Alter the height of collision shape. * - * @param percent + * @param percent the desired height, as a percentage of the full height */ protected void setHeightPercent(float percent) { scale.setY(percent); @@ -458,7 +488,7 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * This checks if the character is on the ground by doing a ray test. + * Test whether the character is on the ground, by means of a ray test. */ protected void checkOnGround() { TempVars vars = TempVars.get(); @@ -499,12 +529,10 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Gets a new collision shape based on the current scale parameter. The - * created collisionshape is a capsule collision shape that is attached to a - * compound collision shape with an offset to set the object center at the - * bottom of the capsule. + * Create a collision shape based on the scale parameter. The new shape is a + * compound shape containing an offset capsule. * - * @return + * @return a new compound shape (not null) */ protected CollisionShape getShape() { //TODO: cleanup size mess.. @@ -516,18 +544,18 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Gets the scaled height. + * Calculate the character's scaled height. * - * @return + * @return the height */ protected float getFinalHeight() { return height * scale.getY(); } /** - * Gets the scaled radius. + * Calculate the character's scaled radius. * - * @return + * @return the radius */ protected float getFinalRadius() { return radius * scale.getZ(); @@ -536,7 +564,7 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph /** * Updates the local coordinate system from the localForward and localUp * vectors, adapts localForward, sets localForwardRotation quaternion to - * local z-forward rotation. + * local Z-forward rotation. */ protected void updateLocalCoordinateSystem() { //gravity vector has possibly changed, calculate new world forward (UNIT_Z) @@ -547,8 +575,8 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * Updates the local x/z-flattened view direction and the corresponding - * rotation quaternion for the spatial. + * Updates the local X-Z view direction and the corresponding rotation + * quaternion for the spatial. */ protected void updateLocalViewDirection() { //update local rotation quaternion to use for view rotation @@ -568,7 +596,6 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph * set to the new direction * @param worldUpVector The up vector to use, the result direction will be * perpendicular to this - * @return */ protected final void calculateNewForward(Quaternion rotation, Vector3f direction, Vector3f worldUpVector) { if (direction == null) { @@ -600,10 +627,9 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * This is implemented from AbstractPhysicsControl and called when the - * spatial is attached for example. + * Translate the character to the specified location. * - * @param vec + * @param vec desired location (not null, unaffected) */ @Override protected void setPhysicsLocation(Vector3f vec) { @@ -612,12 +638,12 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * This is implemented from AbstractPhysicsControl and called when the - * spatial is attached for example. We don't set the actual physics rotation - * but the view rotation here. It might actually be altered by the - * calculateNewForward method. + * Rotate the physics object to the specified orientation. + *

+ * We don't set the actual physics rotation but the view rotation here. It + * might actually be altered by the calculateNewForward method. * - * @param quat + * @param quat desired orientation (not null, unaffected) */ @Override protected void setPhysicsRotation(Quaternion quat) { @@ -627,10 +653,9 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * This is implemented from AbstractPhysicsControl and called when the - * control is supposed to add all objects to the physics space. + * Add all managed physics objects to the specified space. * - * @param space + * @param space which physics space to add to (not null) */ @Override protected void addPhysics(PhysicsSpace space) { @@ -642,10 +667,9 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph } /** - * This is implemented from AbstractPhysicsControl and called when the - * control is supposed to remove all objects from the physics space. + * Remove all managed physics objects from the specified space. * - * @param space + * @param space which physics space to remove from (not null) */ @Override protected void removePhysics(PhysicsSpace space) { @@ -653,16 +677,33 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph space.removeTickListener(this); } + /** + * Create spatial-dependent data. Invoked when this control is added to a + * spatial. + * + * @param spat the controlled spatial (not null, alias created) + */ @Override protected void createSpatialData(Spatial spat) { rigidBody.setUserObject(spatial); } + /** + * Destroy spatial-dependent data. Invoked when this control is removed from + * a spatial. + * + * @param spat the previously controlled spatial (not null) + */ @Override protected void removeSpatialData(Spatial spat) { rigidBody.setUserObject(null); } + /** + * Create a shallow clone for the JME cloner. + * + * @return a new control (not null) + */ @Override public Object jmeClone() { BetterCharacterControl control = new BetterCharacterControl(radius, height, mass); @@ -671,6 +712,12 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph return control; } + /** + * Serialize this control, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -682,6 +729,12 @@ public class BetterCharacterControl extends AbstractPhysicsControl implements Ph oc.write(physicsDamping, "physicsDamping", 0.9f); } + /** + * De-serialize this control, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/GhostControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/GhostControl.java index d69ddd2ef..28f33b294 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/GhostControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/GhostControl.java @@ -49,38 +49,82 @@ import com.jme3.util.clone.JmeCloneable; import java.io.IOException; /** - * A GhostControl moves with the spatial it is attached to and can be used to check - * overlaps with other physics objects (e.g. aggro radius). + * A physics control to link a PhysicsGhostObject to a spatial. + *

+ * The ghost object moves with the spatial it is attached to and can be used to + * detect overlaps with other physics objects (e.g. aggro radius). + *

+ * This class is shared between JBullet and Native Bullet. + * * @author normenhansen */ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, JmeCloneable { + /** + * spatial to which this control is added, or null if none + */ protected Spatial spatial; + /** + * true→control is enabled, false→control is disabled + */ protected boolean enabled = true; + /** + * true→body is added to the physics space, false→not added + */ protected boolean added = false; + /** + * space to which the ghost object is (or would be) added + */ protected PhysicsSpace space = null; + /** + * true → physics coordinates match local transform, false → + * physics coordinates match world transform + */ protected boolean applyLocal = false; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public GhostControl() { } + /** + * Instantiate an enabled control with the specified shape. + * + * @param shape (not null) + */ public GhostControl(CollisionShape shape) { super(shape); } + /** + * Test whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @return true if matching local coordinates, false if matching world + * coordinates + */ public boolean isApplyPhysicsLocal() { return applyLocal; } /** - * When set to true, the physics coordinates will be applied to the local - * translation of the Spatial - * @param applyPhysicsLocal + * Alter whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @param applyPhysicsLocal true→match local coordinates, + * false→match world coordinates (default=false) */ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) { applyLocal = applyPhysicsLocal; } + /** + * Access whichever spatial translation corresponds to the physics location. + * + * @return the pre-existing vector (not null) + */ private Vector3f getSpatialTranslation() { if (applyLocal) { return spatial.getLocalTranslation(); @@ -88,6 +132,11 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, return spatial.getWorldTranslation(); } + /** + * Access whichever spatial rotation corresponds to the physics rotation. + * + * @return the pre-existing quaternion (not null) + */ private Quaternion getSpatialRotation() { if (applyLocal) { return spatial.getLocalRotation(); @@ -95,12 +144,23 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, return spatial.getWorldRotation(); } + /** + * Clone this control for a different spatial. No longer used as of JME 3.1. + * + * @param spatial the spatial for the clone to control (or null) + * @return a new control (not null) + */ @Deprecated @Override public Control cloneForSpatial(Spatial spatial) { throw new UnsupportedOperationException(); } + /** + * Create a shallow clone for the JME cloner. + * + * @return a new control (not null) + */ @Override public Object jmeClone() { GhostControl control = new GhostControl(collisionShape); @@ -115,11 +175,27 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, return control; } + /** + * Callback from {@link com.jme3.util.clone.Cloner} to convert this + * shallow-cloned control into a deep-cloned one, using the specified cloner + * and original to resolve copied fields. + * + * @param cloner the cloner currently cloning this control (not null) + * @param original the control from which this control was shallow-cloned + * (unused) + */ @Override public void cloneFields( Cloner cloner, Object original ) { this.spatial = cloner.clone(spatial); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ public void setSpatial(Spatial spatial) { this.spatial = spatial; setUserObject(spatial); @@ -130,6 +206,15 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, setPhysicsRotation(getSpatialRotation()); } + /** + * Enable or disable this control. + *

+ * When the control is disabled, the ghost object is removed from physics + * space. When the control is enabled again, the object is moved to the + * current location of the spatial and then added to the physics space. + * + * @param enabled true→enable the control, false→disable it + */ public void setEnabled(boolean enabled) { this.enabled = enabled; if (space != null) { @@ -147,10 +232,22 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, } } + /** + * Test whether this control is enabled. + * + * @return true if enabled, otherwise false + */ public boolean isEnabled() { return enabled; } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is added to a scene. Do not invoke directly + * from user code. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ public void update(float tpf) { if (!enabled) { return; @@ -159,6 +256,14 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, setPhysicsRotation(getSpatialRotation()); } + /** + * Render this control. Invoked once per view port per frame, provided the + * control is added to a scene. Should be invoked only by a subclass or by + * the RenderManager. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ public void render(RenderManager rm, ViewPort vp) { } @@ -189,10 +294,22 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, space = newSpace; } + /** + * Access the physics space to which the ghost object is (or would be) + * added. + * + * @return the pre-existing space, or null for none + */ public PhysicsSpace getPhysicsSpace() { return space; } + /** + * Serialize this control, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -202,6 +319,12 @@ public class GhostControl extends PhysicsGhostObject implements PhysicsControl, oc.write(spatial, "spatial", null); } + /** + * De-serialize this control, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java index 49c0e0f34..b2465b5c6 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java @@ -58,32 +58,39 @@ import java.util.logging.Logger; * use this control you need a model with an AnimControl and a * SkeletonControl.
This should be the case if you imported an animated * model from Ogre or blender.
Note enabling/disabling the control - * add/removes it from the physics space

This control creates collision - * shapes for each bones of the skeleton when you call - * spatial.addControl(ragdollControl).

There are 2 modes for this control :

+ * add/removes it from the physics space
+ *

+ * This control creates collision shapes for each bones of the skeleton when you + * invoke spatial.addControl(ragdollControl).

+ *

+ * There are 2 modes for this control :

+ *

+ * This class is shared between JBullet and Native Bullet. * - * @author Normen Hansen and Rémy Bouquet (Nehon) - * - * TODO this needs to be redone with the new animation system + * @author Normen Hansen and Rémy Bouquet (Nehon) */ @Deprecated public class KinematicRagdollControl extends AbstractPhysicsControl implements PhysicsCollisionListener, JmeCloneable { + /** + * list of registered collision listeners + */ protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName()); protected List listeners; protected final Set boneList = new TreeSet(); @@ -91,7 +98,13 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P protected final Vector3f modelPosition = new Vector3f(); protected final Quaternion modelRotation = new Quaternion(); protected final PhysicsRigidBody baseRigidBody; + /** + * model being controlled + */ protected Spatial targetModel; + /** + * skeleton being controlled + */ protected Skeleton skeleton; protected RagdollPreset preset = new HumanoidRagdollPreset(); protected Vector3f initScale; @@ -100,23 +113,52 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P protected boolean blendedControl = false; protected float weightThreshold = -1.0f; protected float blendStart = 0.0f; + /** + * blending interval for animations (in seconds, ≥0) + */ protected float blendTime = 1.0f; protected float eventDispatchImpulseThreshold = 10; protected float rootMass = 15; + /** + * accumulate total mass of ragdoll when control is added to a scene + */ protected float totalMass = 0; private Map ikTargets = new HashMap(); private Map ikChainDepth = new HashMap(); + /** + * rotational speed for inverse kinematics (radians per second, default=7) + */ private float ikRotSpeed = 7f; + /** + * viscous limb-damping ratio (0→no damping, 1→critically damped, + * default=0.6) + */ private float limbDampening = 0.6f; - + /** + * distance threshold for inverse kinematics (default=0.1) + */ private float IKThreshold = 0.1f; + /** + * Enumerate joint-control modes for this control. + */ public static enum Mode { - + /** + * collision shapes follow the movements of bones in the skeleton + */ Kinematic, + /** + * skeleton is controlled by Bullet physics (gravity and collisions) + */ Ragdoll, + /** + * skeleton is controlled by inverse-kinematic targets + */ IK } + /** + * Link a bone to a jointed rigid body. + */ public class PhysicsBoneLink implements Savable { protected PhysicsRigidBody rigidBody; @@ -126,17 +168,36 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P protected Quaternion startBlendingRot = new Quaternion(); protected Vector3f startBlendingPos = new Vector3f(); + /** + * Instantiate an uninitialized link. + */ public PhysicsBoneLink() { } + /** + * Access the linked bone. + * + * @return the pre-existing instance or null + */ public Bone getBone() { return bone; } + /** + * Access the linked body. + * + * @return the pre-existing instance or null + */ public PhysicsRigidBody getRigidBody() { return rigidBody; } + /** + * Serialize this bone link, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { OutputCapsule oc = ex.getCapsule(this); oc.write(rigidBody, "rigidBody", null); @@ -147,6 +208,13 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P oc.write(startBlendingPos, "startBlendingPos", new Vector3f()); } + /** + * De-serialize this bone link, for example when loading from a J3O + * file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { InputCapsule ic = im.getCapsule(this); rigidBody = (PhysicsRigidBody) ic.readSavable("rigidBody", null); @@ -159,29 +227,53 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * construct a KinematicRagdollControl + * Instantiate an enabled control. */ public KinematicRagdollControl() { baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1); baseRigidBody.setKinematic(mode == Mode.Kinematic); } + /** + * Instantiate an enabled control with the specified weight threshold. + * + * @param weightThreshold (>0, <1) + */ public KinematicRagdollControl(float weightThreshold) { this(); this.weightThreshold = weightThreshold; } + /** + * Instantiate an enabled control with the specified preset and weight + * threshold. + * + * @param preset (not null) + * @param weightThreshold (>0, <1) + */ public KinematicRagdollControl(RagdollPreset preset, float weightThreshold) { this(); this.preset = preset; this.weightThreshold = weightThreshold; } + /** + * Instantiate an enabled control with the specified preset. + * + * @param preset (not null) + */ public KinematicRagdollControl(RagdollPreset preset) { this(); this.preset = preset; } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is added to a scene. Do not invoke directly + * from user code. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ public void update(float tpf) { if (!enabled) { return; @@ -196,6 +288,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } } + /** + * Update this control in Ragdoll mode, based on Bullet physics. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ protected void ragDollUpdate(float tpf) { TempVars vars = TempVars.get(); Quaternion tmpRot1 = vars.quat1; @@ -213,7 +310,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P //retrieving bone rotation in physics world space Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat(); - //multiplying this rotation by the initialWorld rotation of the bone, + //multiplying this rotation by the initialWorld rotation of the bone, //then transforming it with the inverse world rotation of the model tmpRot1.set(q).multLocal(link.initalWorldRotation); tmpRot2.set(targetModel.getWorldRotation()).inverseLocal().mult(tmpRot1, tmpRot1); @@ -237,13 +334,13 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P link.bone.setUserTransformsInModelSpace(position, tmpRot1); } else { - //if boneList is empty, this means that every bone in the ragdoll has a collision shape, - //so we just update the bone position + //If boneList is empty, every bone has a collision shape, + //so we simply update the bone position. if (boneList.isEmpty()) { link.bone.setUserTransformsInModelSpace(position, tmpRot1); } else { //boneList is not empty, this means some bones of the skeleton might not be associated with a collision shape. - //So we update them recursively + //So we update them recusively RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList); } } @@ -251,6 +348,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P vars.release(); } + /** + * Update this control in Kinematic mode, based on bone animation tracks. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ protected void kinematicUpdate(float tpf) { //the ragdoll does not have control, so the keyframed animation updates the physics position of the physics bonces TempVars vars = TempVars.get(); @@ -261,7 +363,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P // if(link.usedbyIK){ // continue; // } - //if blended control this means, keyframed animation is updating the skeleton, + //if blended control this means, keyframed animation is updating the skeleton, //but to allow smooth transition, we blend this transformation with the saved position of the ragdoll if (blendedControl) { Vector3f position2 = vars.vect2; @@ -301,6 +403,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } vars.release(); } + /** + * Update this control in IK mode, based on IK targets. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ private void ikUpdate(float tpf){ TempVars vars = TempVars.get(); @@ -337,6 +444,19 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P vars.release(); } + /** + * Update a bone and its ancestors in IK mode. Note: recursive! + * + * @param link the bone link for the affected bone (may be null) + * @param tpf the time interval between frames (in seconds, ≥0) + * @param vars unused + * @param tmpRot1 temporary storage used in calculations (not null) + * @param tmpRot2 temporary storage used in calculations (not null) + * @param tipBone (not null) + * @param target the location target in model space (not null, unaffected) + * @param depth depth of the recursion (≥0) + * @param maxDepth recursion limit (≥0) + */ public void updateBone(PhysicsBoneLink link, float tpf, TempVars vars, Quaternion tmpRot1, Quaternion[] tmpRot2, Bone tipBone, Vector3f target, int depth, int maxDepth) { if (link == null || link.bone.getParent() == null) { return; @@ -391,13 +511,12 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * Set the transforms of a rigidBody to match the transforms of a bone. this - * is used to make the ragdoll follow the skeleton motion while in Kinematic - * mode + * Alter the transforms of a rigidBody to match the transforms of a bone. + * This is used to make the ragdoll follow animated motion in Kinematic mode * - * @param link the link containing the bone and the rigidBody - * @param position just a temp vector for position - * @param tmpRot1 just a temp quaternion for rotation + * @param link the bone link connecting the bone and the rigidBody + * @param position temporary storage used in calculations (not null) + * @param tmpRot1 temporary storage used in calculations (not null) */ protected void matchPhysicObjectToBone(PhysicsBoneLink link, Vector3f position, Quaternion tmpRot1) { //computing position from rotation and scale @@ -415,8 +534,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * rebuild the ragdoll this is useful if you applied scale on the ragdoll - * after it's been initialized, same as reattaching. + * Rebuild the ragdoll. This is useful if you applied scale on the ragdoll + * after it was initialized. Same as re-attaching. */ public void reBuild() { if (spatial == null) { @@ -426,6 +545,12 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P createSpatialData(spatial); } + /** + * Create spatial-dependent data. Invoked when this control is added to a + * scene. + * + * @param model the controlled spatial (not null) + */ @Override protected void createSpatialData(Spatial model) { targetModel = model; @@ -451,7 +576,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P model.addControl(sc); // put into bind pose and compute bone transforms in model space - // maybe dont reset to ragdoll out of animations? + // maybe don't reset to ragdoll out of animations? scanSpatial(model); @@ -469,6 +594,12 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P logger.log(Level.FINE, "Created physics ragdoll for skeleton {0}", skeleton); } + /** + * Destroy spatial-dependent data. Invoked when this control is removed from + * a spatial. + * + * @param spat the previously controlled spatial (not null) + */ @Override protected void removeSpatialData(Spatial spat) { if (added) { @@ -478,15 +609,22 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * Add a bone name to this control Using this method you can specify which - * bones of the skeleton will be used to build the collision shapes. + * Add a bone name to this control. Repeated invocations of this method can + * be used to specify which bones to use when generating collision shapes. + *

+ * Not allowed after attaching the control. * - * @param name + * @param name the name of the bone to add */ public void addBoneName(String name) { boneList.add(name); } + /** + * Generate physics shapes and bone links for the skeleton. + * + * @param model the spatial with the model's SkeletonControl (not null) + */ protected void scanSpatial(Spatial model) { AnimControl animControl = model.getControl(AnimControl.class); Map> pointsMap = null; @@ -505,6 +643,16 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } } + /** + * Generate a physics shape and bone links for the specified bone. Note: + * recursive! + * + * @param model the spatial with the model's SkeletonControl (not null) + * @param bone the bone to be linked (not null) + * @param parent the body linked to the parent bone (not null) + * @param reccount depth of the recursion (≥1) + * @param pointsMap (not null) + */ protected void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount, Map> pointsMap) { PhysicsRigidBody parentShape = parent; if (boneList.isEmpty() || boneList.contains(bone.getName())) { @@ -512,7 +660,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P PhysicsBoneLink link = new PhysicsBoneLink(); link.bone = bone; - //creating the collision shape + //create the collision shape HullCollisionShape shape = null; if (pointsMap != null) { //build a shape for the bone, using the vertices that are most influenced by this bone @@ -555,16 +703,16 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * Set the joint limits for the joint between the given bone and its parent. - * This method can't work before attaching the control to a spatial + * Alter the limits of the joint connecting the specified bone to its + * parent. Can only be invoked after adding the control to a spatial. * * @param boneName the name of the bone - * @param maxX the maximum rotation on the x axis (in radians) - * @param minX the minimum rotation on the x axis (in radians) - * @param maxY the maximum rotation on the y axis (in radians) - * @param minY the minimum rotation on the z axis (in radians) - * @param maxZ the maximum rotation on the z axis (in radians) - * @param minZ the minimum rotation on the z axis (in radians) + * @param maxX the maximum rotation on the X axis (in radians) + * @param minX the minimum rotation on the X axis (in radians) + * @param maxY the maximum rotation on the Y axis (in radians) + * @param minY the minimum rotation on the Y axis (in radians) + * @param maxZ the maximum rotation on the Z axis (in radians) + * @param minZ the minimum rotation on the Z axis (in radians) */ public void setJointLimit(String boneName, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) { PhysicsBoneLink link = boneLinks.get(boneName); @@ -576,8 +724,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * Return the joint between the given bone and its parent. This return null - * if it's called before attaching the control to a spatial + * Return the joint between the specified bone and its parent. This return + * null if it's invoked before adding the control to a spatial * * @param boneName the name of the bone * @return the joint between the given bone and its parent @@ -642,9 +790,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * For internal use only callback for collisionevent + * For internal use only: callback for collision event * - * @param event + * @param event (not null) */ public void collision(PhysicsCollisionEvent event) { PhysicsCollisionObject objA = event.getObjectA(); @@ -699,7 +847,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P * be animated by the keyframe animation, but will be able to physically * interact with its physics environment * - * @param ragdollEnabled + * @param mode an enum value (not null) */ protected void setMode(Mode mode) { this.mode = mode; @@ -777,9 +925,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * Set the control into Kinematic mode In this mode, the collision shapes - * follow the movements of the skeleton, and can interact with physical - * environment + * Put the control into Kinematic mode. In this mode, the collision shapes + * follow the movements of the skeleton while interacting with the physics + * environment. */ public void setKinematicMode() { if (mode != Mode.Kinematic) { @@ -798,8 +946,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * Sets the control into Inverse Kinematics mode. The affected bones are affected by IK. - * physics. + * Sets the control into Inverse Kinematics mode. The affected bones are + * affected by IK. physics. */ public void setIKMode() { if (mode != Mode.IK) { @@ -810,16 +958,16 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P /** * returns the mode of this control * - * @return + * @return an enum value */ public Mode getMode() { return mode; } /** - * add a + * Add a collision listener to this control. * - * @param listener + * @param listener (not null, alias created) */ public void addCollisionListener(RagdollCollisionListener listener) { if (listeners == null) { @@ -828,35 +976,66 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P listeners.add(listener); } + /** + * Alter the ragdoll's root mass. + * + * @param rootMass the desired mass (≥0) + */ public void setRootMass(float rootMass) { this.rootMass = rootMass; } + /** + * Read the ragdoll's total mass. + * + * @return mass (≥0) + */ public float getTotalMass() { return totalMass; } + /** + * Read the ragdoll's weight threshold. + * + * @return threshold + */ public float getWeightThreshold() { return weightThreshold; } + /** + * Alter the ragdoll's weight threshold. + * + * @param weightThreshold the desired threshold + */ public void setWeightThreshold(float weightThreshold) { this.weightThreshold = weightThreshold; } + /** + * Read the ragdoll's event-dispatch impulse threshold. + * + * @return threshold + */ public float getEventDispatchImpulseThreshold() { return eventDispatchImpulseThreshold; } + /** + * Alter the ragdoll's event-dispatch impulse threshold. + * + * @param eventDispatchImpulseThreshold desired threshold + */ public void setEventDispatchImpulseThreshold(float eventDispatchImpulseThreshold) { this.eventDispatchImpulseThreshold = eventDispatchImpulseThreshold; } /** - * Set the CcdMotionThreshold of all the bone's rigidBodies of the ragdoll + * Alter the CcdMotionThreshold of all rigid bodies in the ragdoll. * * @see PhysicsRigidBody#setCcdMotionThreshold(float) - * @param value + * @param value the desired threshold value (velocity, >0) or zero to + * disable CCD (default=0) */ public void setCcdMotionThreshold(float value) { for (PhysicsBoneLink link : boneLinks.values()) { @@ -865,10 +1044,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * Set the CcdSweptSphereRadius of all the bone's rigidBodies of the ragdoll + * Alter the CcdSweptSphereRadius of all rigid bodies in the ragdoll. * * @see PhysicsRigidBody#setCcdSweptSphereRadius(float) - * @param value + * @param value the desired radius of the sphere used for continuous + * collision detection (≥0) */ public void setCcdSweptSphereRadius(float value) { for (PhysicsBoneLink link : boneLinks.values()) { @@ -877,7 +1057,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * return the rigidBody associated to the given bone + * Access the rigidBody associated with the named bone. * * @param boneName the name of the bone * @return the associated rigidBody. @@ -891,15 +1071,22 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * For internal use only specific render for the ragdoll (if debugging) + * Render this control. Invoked once per view port per frame, provided the + * control is added to a scene. Should be invoked only by a subclass or by + * the RenderManager. * - * @param rm - * @param vp + * @param rm the render manager (not null) + * @param vp the view port to render (not null) */ @Override public void render(RenderManager rm, ViewPort vp) { } + /** + * Create a shallow clone for the JME cloner. + * + * @return a new control (not null) + */ @Override public Object jmeClone() { KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold); @@ -911,6 +1098,14 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P return control; } + /** + * Add a target for inverse kinematics. + * + * @param bone which bone the IK applies to (not null) + * @param worldPos the world coordinates of the goal (not null) + * @param chainLength number of bones in the chain + * @return a new instance (not null, already added to ikTargets) + */ public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) { Vector3f target = worldPos.subtract(targetModel.getWorldTranslation()); ikTargets.put(bone.getName(), target); @@ -929,6 +1124,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P return target; } + /** + * Remove the inverse-kinematics target for the specified bone. + * + * @param bone which bone has the target (not null, modified) + */ public void removeIKTarget(Bone bone) { int depth = ikChainDepth.remove(bone.getName()); int i = 0; @@ -942,11 +1142,19 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } } + /** + * Remove all inverse-kinematics targets. + */ public void removeAllIKTargets(){ ikTargets.clear(); ikChainDepth.clear(); applyUserControl(); } + + /** + * Ensure that user control is enabled for any bones used by inverse + * kinematics and disabled for any other bones. + */ public void applyUserControl() { for (Bone bone : skeleton.getRoots()) { RagdollUtils.setUserControl(bone, false); @@ -973,39 +1181,77 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P vars.release(); } } + + /** + * Read the rotation speed for inverse kinematics. + * + * @return speed (≥0) + */ public float getIkRotSpeed() { return ikRotSpeed; } + /** + * Alter the rotation speed for inverse kinematics. + * + * @param ikRotSpeed the desired speed (≥0, default=7) + */ public void setIkRotSpeed(float ikRotSpeed) { this.ikRotSpeed = ikRotSpeed; } + /** + * Read the distance threshold for inverse kinematics. + * + * @return distance threshold + */ public float getIKThreshold() { return IKThreshold; } + /** + * Alter the distance threshold for inverse kinematics. + * + * @param IKThreshold the desired distance threshold (default=0.1) + */ public void setIKThreshold(float IKThreshold) { this.IKThreshold = IKThreshold; } - + /** + * Read the limb damping. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getLimbDampening() { return limbDampening; } + /** + * Alter the limb damping. + * + * @param limbDampening the desired viscous damping ratio (0→no + * damping, 1→critically damped, default=0.6) + */ public void setLimbDampening(float limbDampening) { this.limbDampening = limbDampening; } + /** + * Access the named bone. + * + * @param name which bone to access + * @return the pre-existing instance, or null if not found + */ public Bone getBone(String name){ return skeleton.getBone(name); } /** - * serialize this control + * Serialize this control, for example when saving to a J3O file. * - * @param ex - * @throws IOException + * @param ex exporter (not null) + * @throws IOException from exporter */ @Override public void write(JmeExporter ex) throws IOException { @@ -1032,10 +1278,10 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P } /** - * de-serialize this control + * De-serialize this control, for example when loading from a J3O file. * - * @param im - * @throws IOException + * @param im importer (not null) + * @throws IOException from importer */ @Override public void read(JmeImporter im) throws IOException { diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/PhysicsControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/PhysicsControl.java index 693c15590..1c81a7ff7 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/PhysicsControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/PhysicsControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,31 +35,47 @@ import com.jme3.bullet.PhysicsSpace; import com.jme3.scene.control.Control; /** + * An interface for a scene-graph control that links a physics object to a + * Spatial. + *

+ * This interface is shared between JBullet and Native Bullet. * * @author normenhansen */ public interface PhysicsControl extends Control { /** - * Only used internally, do not call. - * @param space + * If enabled, add this control's physics object to the specified physics + * space. In not enabled, alter where the object would be added. The object + * is removed from any other space it's currently in. + * + * @param space where to add, or null to simply remove */ public void setPhysicsSpace(PhysicsSpace space); + /** + * Access the physics space to which the object is (or would be) added. + * + * @return the pre-existing space, or null for none + */ public PhysicsSpace getPhysicsSpace(); /** - * The physics object is removed from the physics space when the control - * is disabled. When the control is enabled again the physics object is - * moved to the current location of the spatial and then added to the physics - * space. This allows disabling/enabling physics to move the spatial freely. - * @param state + * Enable or disable this control. + *

+ * The physics object is removed from its physics space when the control is + * disabled. When the control is enabled again, the physics object is moved + * to the current location of the spatial and then added to the physics + * space. + * + * @param state true→enable the control, false→disable it */ public void setEnabled(boolean state); /** - * Returns the current enabled state of the physics control - * @return current enabled state + * Test whether this control is enabled. + * + * @return true if enabled, otherwise false */ public boolean isEnabled(); } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java index 4ae2d480f..1eb172e13 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/RigidBodyControl.java @@ -56,47 +56,91 @@ import com.jme3.util.clone.JmeCloneable; import java.io.IOException; /** + * A physics control to link a PhysicsRigidBody to a spatial. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl, JmeCloneable { + /** + * spatial to which this control is added, or null if none + */ protected Spatial spatial; + /** + * true→control is enabled, false→control is disabled + */ protected boolean enabled = true; + /** + * true→body is added to the physics space, false→not added + */ protected boolean added = false; + /** + * space to which the body is (or would be) added + */ protected PhysicsSpace space = null; + /** + * true→body is kinematic, false→body is static or dynamic + */ protected boolean kinematicSpatial = true; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public RigidBodyControl() { } /** - * When using this constructor, the CollisionShape for the RigidBody is generated - * automatically when the Control is added to a Spatial. - * @param mass When not 0, a HullCollisionShape is generated, otherwise a MeshCollisionShape is used. For geometries with box or sphere meshes the proper box or sphere collision shape is used. + * When using this constructor, the CollisionShape for the RigidBody is + * generated automatically when the control is added to a spatial. + * + * @param mass When not 0, a HullCollisionShape is generated, otherwise a + * MeshCollisionShape is used. For geometries with box or sphere meshes the + * proper box or sphere collision shape is used. */ public RigidBodyControl(float mass) { this.mass = mass; } /** - * Creates a new PhysicsNode with the supplied collision shape and mass 1 - * @param shape + * Instantiate an enabled control with mass=1 and the specified collision + * shape. + * + * @param shape the desired shape (not null, alias created) */ public RigidBodyControl(CollisionShape shape) { super(shape); } + /** + * Instantiate an enabled control with the specified collision shape and + * mass. + * + * @param shape the desired shape (not null, alias created) + * @param mass the desired mass (≥0) + */ public RigidBodyControl(CollisionShape shape, float mass) { super(shape, mass); } - @Deprecated + /** + * Clone this control for a different spatial. No longer used as of JME 3.1. + * + * @param spatial the spatial for the clone to control (or null) + * @return a new control (not null) + */ @Override public Control cloneForSpatial(Spatial spatial) { throw new UnsupportedOperationException(); } + /** + * Create a shallow clone for the JME cloner. + * + * @return a new control (not null) + */ @Override public Object jmeClone() { RigidBodyControl control = new RigidBodyControl(collisionShape, mass); @@ -127,11 +171,27 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl return control; } + /** + * Callback from {@link com.jme3.util.clone.Cloner} to convert this + * shallow-cloned control into a deep-cloned one, using the specified cloner + * and original to resolve copied fields. + * + * @param cloner the cloner currently cloning this control (not null) + * @param original the control from which this control was shallow-cloned + * (unused) + */ @Override public void cloneFields( Cloner cloner, Object original ) { this.spatial = cloner.clone(spatial); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ public void setSpatial(Spatial spatial) { this.spatial = spatial; setUserObject(spatial); @@ -146,6 +206,10 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl setPhysicsRotation(getSpatialRotation()); } + /** + * Set the collision shape based on the controlled spatial and its + * descendents. + */ protected void createCollisionShape() { if (spatial == null) { return; @@ -168,6 +232,15 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl } } + /** + * Enable or disable this control. + *

+ * When the control is disabled, the body is removed from physics space. + * When the control is enabled again, the body is moved to the current + * location of the spatial and then added to the physics space. + * + * @param enabled true→enable the control, false→disable it + */ public void setEnabled(boolean enabled) { this.enabled = enabled; if (space != null) { @@ -185,40 +258,62 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl } } + /** + * Test whether this control is enabled. + * + * @return true if enabled, otherwise false + */ public boolean isEnabled() { return enabled; } /** - * Checks if this control is in kinematic spatial mode. - * @return true if the spatial location is applied to this kinematic rigidbody + * Test whether this control is in kinematic mode. + * + * @return true if the spatial location and rotation are applied to the + * rigid body, otherwise false */ public boolean isKinematicSpatial() { return kinematicSpatial; } /** - * Sets this control to kinematic spatial mode so that the spatials transform will - * be applied to the rigidbody in kinematic mode, defaults to true. - * @param kinematicSpatial + * Enable or disable kinematic mode. In kinematic mode, the spatial's + * location and rotation will be applied to the rigid body. + * + * @param kinematicSpatial true→kinematic, false→dynamic or static */ public void setKinematicSpatial(boolean kinematicSpatial) { this.kinematicSpatial = kinematicSpatial; } + /** + * Test whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @return true if matching local coordinates, false if matching world + * coordinates + */ public boolean isApplyPhysicsLocal() { return motionState.isApplyPhysicsLocal(); } /** - * When set to true, the physics coordinates will be applied to the local - * translation of the Spatial instead of the world translation. - * @param applyPhysicsLocal + * Alter whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @param applyPhysicsLocal true→match local coordinates, + * false→match world coordinates (default=false) */ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) { motionState.setApplyPhysicsLocal(applyPhysicsLocal); } + /** + * Access whichever spatial translation corresponds to the physics location. + * + * @return the pre-existing vector (not null) + */ private Vector3f getSpatialTranslation(){ if(motionState.isApplyPhysicsLocal()){ return spatial.getLocalTranslation(); @@ -226,6 +321,11 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl return spatial.getWorldTranslation(); } + /** + * Access whichever spatial rotation corresponds to the physics rotation. + * + * @return the pre-existing quaternion (not null) + */ private Quaternion getSpatialRotation(){ if(motionState.isApplyPhysicsLocal()){ return spatial.getLocalRotation(); @@ -233,6 +333,12 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl return spatial.getWorldRotation(); } + /** + * Update this control. Invoked once per frame, during the logical-state + * update, provided the control is added to a scene. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ public void update(float tpf) { if (enabled && spatial != null) { if (isKinematic() && kinematicSpatial) { @@ -244,6 +350,14 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl } } + /** + * Render this control. Invoked once per view port per frame, provided the + * control is added to a scene. Should be invoked only by a subclass or by + * the RenderManager. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ public void render(RenderManager rm, ViewPort vp) { } @@ -274,10 +388,21 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl space = newSpace; } + /** + * Access the physics space to which the body is (or would be) added. + * + * @return the pre-existing space, or null for none + */ public PhysicsSpace getPhysicsSpace() { return space; } + /** + * Serialize this control, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -288,6 +413,12 @@ public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl oc.write(spatial, "spatial", null); } + /** + * De-serialize this control, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java index cebb14a4a..3bf7f5785 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/VehicleControl.java @@ -51,39 +51,75 @@ import java.io.IOException; import java.util.Iterator; /** + * A physics control to link a PhysicsVehicle to a spatial. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, JmeCloneable { + /** + * spatial to which this control is added, or null if none + */ protected Spatial spatial; + /** + * true→control is enabled, false→control is disabled + */ protected boolean enabled = true; + /** + * space to which the vehicle is (or would be) added + */ protected PhysicsSpace space = null; + /** + * true→vehicle is added to the physics space, false→not added + */ protected boolean added = false; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public VehicleControl() { } /** - * Creates a new PhysicsNode with the supplied collision shape - * @param shape + * Instantiate an enabled control with mass=1 and the specified collision + * shape. + * + * @param shape the desired shape (not null, alias created) */ public VehicleControl(CollisionShape shape) { super(shape); } + /** + * Instantiate an enabled with the specified collision shape and mass. + * + * @param shape the desired shape (not null, alias created) + * @param mass (>0) + */ public VehicleControl(CollisionShape shape, float mass) { super(shape, mass); } + /** + * Test whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @return true if matching local coordinates, false if matching world + * coordinates + */ public boolean isApplyPhysicsLocal() { return motionState.isApplyPhysicsLocal(); } /** - * When set to true, the physics coordinates will be applied to the local - * translation of the Spatial - * @param applyPhysicsLocal + * Alter whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @param applyPhysicsLocal true→match local coordinates, + * false→match world coordinates (default=false) */ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) { motionState.setApplyPhysicsLocal(applyPhysicsLocal); @@ -107,13 +143,22 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm return spatial.getWorldRotation(); } - @Deprecated + /** + * Clone this control for a different spatial. No longer used as of JME 3.1. + * + * @param spatial the spatial for the clone to control (or null) + * @return a new control (not null) + */ @Override public Control cloneForSpatial(Spatial spatial) { throw new UnsupportedOperationException(); } - @Override + /** + * Create a shallow clone for the JME cloner. + * + * @return a new control (not null) + */ public Object jmeClone() { VehicleControl control = new VehicleControl(collisionShape, mass); control.setAngularFactor(getAngularFactor()); @@ -161,6 +206,15 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm return control; } + /** + * Callback from {@link com.jme3.util.clone.Cloner} to convert this + * shallow-cloned control into a deep-cloned one, using the specified cloner + * and original to resolve copied fields. + * + * @param cloner the cloner currently cloning this control (not null) + * @param original the control from which this control was shallow-cloned + * (unused) + */ @Override public void cloneFields( Cloner cloner, Object original ) { this.spatial = cloner.clone(spatial); @@ -171,6 +225,11 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm } } + /** + * Alter which spatial is controlled. + * + * @param spatial spatial to control (or null) + */ public void setSpatial(Spatial spatial) { this.spatial = spatial; setUserObject(spatial); @@ -181,6 +240,15 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm setPhysicsRotation(getSpatialRotation()); } + /** + * Enable or disable this control. + *

+ * When the control is disabled, the vehicle is removed from physics space. + * When the control is enabled again, the physics object is moved to the + * spatial's location and then added to the physics space. + * + * @param enabled true→enable the control, false→disable it + */ public void setEnabled(boolean enabled) { this.enabled = enabled; if (space != null) { @@ -198,10 +266,21 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm } } + /** + * Test whether this control is enabled. + * + * @return true if enabled, otherwise false + */ public boolean isEnabled() { return enabled; } + /** + * Update this control. Invoked once per frame, during the logical-state + * update, provided the control is added to a scene. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ public void update(float tpf) { if (enabled && spatial != null) { if (getMotionState().applyTransform(spatial)) { @@ -213,6 +292,14 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm } } + /** + * Render this control. Invoked once per view port per frame, provided the + * control is added to a scene. Should be invoked only by a subclass or by + * the RenderManager. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ public void render(RenderManager rm, ViewPort vp) { } @@ -243,10 +330,21 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm space = newSpace; } + /** + * Access the physics space to which the vehicle is (or would be) added. + * + * @return the pre-existing space, or null for none + */ public PhysicsSpace getPhysicsSpace() { return space; } + /** + * Serialize this control, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -256,6 +354,12 @@ public class VehicleControl extends PhysicsVehicle implements PhysicsControl, Jm oc.write(spatial, "spatial", null); } + /** + * De-serialize this control, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java index cd9829e61..68696090e 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/HumanoidRagdollPreset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,11 +34,17 @@ package com.jme3.bullet.control.ragdoll; import com.jme3.math.FastMath; /** + * Example ragdoll presets for a typical humanoid skeleton. + *

+ * This class is shared between JBullet and Native Bullet. * * @author Nehon */ public class HumanoidRagdollPreset extends RagdollPreset { + /** + * Initialize the map from bone names to joint presets. + */ @Override protected void initBoneMap() { boneMap.put("head", new JointPreset(FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI)); @@ -59,6 +65,9 @@ public class HumanoidRagdollPreset extends RagdollPreset { } + /** + * Initialize the lexicon. + */ @Override protected void initLexicon() { LexiconEntry entry = new LexiconEntry(); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollPreset.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollPreset.java index ab0b3ef1a..637011c95 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollPreset.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollPreset.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,14 +43,35 @@ import java.util.logging.Logger; */ public abstract class RagdollPreset { + /** + * message logger for this class + */ protected static final Logger logger = Logger.getLogger(RagdollPreset.class.getName()); + /** + * map bone names to joint presets + */ protected Map boneMap = new HashMap(); + /** + * lexicon to map bone names to entries + */ protected Map lexicon = new HashMap(); + /** + * Initialize the map from bone names to joint presets. + */ protected abstract void initBoneMap(); + /** + * Initialize the lexicon. + */ protected abstract void initLexicon(); + /** + * Apply the preset for the named bone to the specified joint. + * + * @param boneName name + * @param joint where to apply the preset (not null, modified) + */ public void setupJointForBone(String boneName, SixDofJoint joint) { if (boneMap.isEmpty()) { @@ -87,14 +108,30 @@ public abstract class RagdollPreset { } + /** + * Range of motion for a joint. + */ protected class JointPreset { private float maxX, minX, maxY, minY, maxZ, minZ; + /** + * Instantiate a preset with no motion allowed. + */ public JointPreset() { } public JointPreset(float maxX, float minX, float maxY, float minY, float maxZ, float minZ) { + /** + * Instantiate a preset with the specified range of motion. + * + * @param maxX the maximum rotation on the X axis (in radians) + * @param minX the minimum rotation on the X axis (in radians) + * @param maxY the maximum rotation on the Y axis (in radians) + * @param minY the minimum rotation on the Y axis (in radians) + * @param maxZ the maximum rotation on the Z axis (in radians) + * @param minZ the minimum rotation on the Z axis (in radians) + */ this.maxX = maxX; this.minX = minX; this.maxY = maxY; @@ -103,6 +140,11 @@ public abstract class RagdollPreset { this.minZ = minZ; } + /** + * Apply this preset to the specified joint. + * + * @param joint where to apply (not null, modified) + */ public void setupJoint(SixDofJoint joint) { joint.getRotationalLimitMotor(0).setHiLimit(maxX); joint.getRotationalLimitMotor(0).setLoLimit(minX); @@ -113,13 +155,28 @@ public abstract class RagdollPreset { } } + /** + * One entry in a bone lexicon. + */ protected class LexiconEntry extends HashMap { + /** + * Add a synonym with the specified score. + * + * @param word a substring that might occur in a bone name (not null) + * @param score larger value means more likely to correspond + */ public void addSynonym(String word, int score) { put(word.toLowerCase(), score); } public int getScore(String word) { + /** + * Calculate a total score for the specified bone name. + * + * @param name the name of a bone (not null) + * @return total score: larger value means more likely to correspond + */ int score = 0; String searchWord = word.toLowerCase(); for (String key : this.keySet()) { diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java index 72921f0c7..455a62f7f 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java @@ -48,11 +48,25 @@ import java.nio.FloatBuffer; import java.util.*; /** + * Utility methods used by KinematicRagdollControl. + *

+ * This class is shared between JBullet and Native Bullet. * * @author Nehon */ public class RagdollUtils { + /** + * Alter the limits of the specified 6-DOF joint. + * + * @param joint which joint to alter (not null) + * @param maxX the maximum rotation on the X axis (in radians) + * @param minX the minimum rotation on the X axis (in radians) + * @param maxY the maximum rotation on the Y axis (in radians) + * @param minY the minimum rotation on the Y axis (in radians) + * @param maxZ the maximum rotation on the Z axis (in radians) + * @param minZ the minimum rotation on the Z axis (in radians) + */ public static void setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) { joint.getRotationalLimitMotor(0).setHiLimit(maxX); @@ -63,6 +77,12 @@ public class RagdollUtils { joint.getRotationalLimitMotor(2).setLoLimit(minZ); } + /** + * Build a map of mesh vertices in a subtree of the scene graph. + * + * @param model the root of the subtree (may be null) + * @return a new map (not null) + */ public static Map> buildPointMap(Spatial model) { @@ -122,14 +142,15 @@ public class RagdollUtils { } /** - * Create a hull collision shape from linked vertices to this bone. - * Vertices have to be previously gathered in a map using buildPointMap method - * - * @param pointsMap - * @param boneIndices - * @param initialScale - * @param initialPosition - * @return + * Create a hull collision shape from linked vertices to this bone. Vertices + * must have previously been gathered using buildPointMap(). + * + * @param pointsMap map from bone indices to coordinates (not null, + * unaffected) + * @param boneIndices (not null, unaffected) + * @param initialScale scale factors (not null, unaffected) + * @param initialPosition location (not null, unaffected) + * @return a new shape (not null) */ public static HullCollisionShape makeShapeFromPointMap(Map> pointsMap, List boneIndices, Vector3f initialScale, Vector3f initialPosition) { @@ -160,7 +181,15 @@ public class RagdollUtils { return new HullCollisionShape(p); } - //returns the list of bone indices of the given bone and its child (if they are not in the boneList) + /** + * Enumerate the bone indices of the specified bone and all its descendents. + * + * @param bone the input bone (not null) + * @param skeleton the skeleton containing the bone (not null) + * @param boneList a set of bone names (not null, unaffected) + * + * @return a new list (not null) + */ public static List getBoneIndices(Bone bone, Skeleton skeleton, Set boneList) { List list = new LinkedList(); if (boneList.isEmpty()) { @@ -178,13 +207,13 @@ public class RagdollUtils { /** * Create a hull collision shape from linked vertices to this bone. - * - * @param model - * @param boneIndices - * @param initialScale - * @param initialPosition - * @param weightThreshold - * @return + * + * @param model the model on which to base the shape + * @param boneIndices indices of relevant bones (not null, unaffected) + * @param initialScale scale factors + * @param initialPosition location + * @param weightThreshold minimum weight for inclusion + * @return a new shape */ public static HullCollisionShape makeShapeFromVerticeWeights(Spatial model, List boneIndices, Vector3f initialScale, Vector3f initialPosition, float weightThreshold) { @@ -216,12 +245,18 @@ public class RagdollUtils { } /** - * returns a list of points for the given bone - * @param mesh - * @param boneIndex - * @param offset - * @param link - * @return + * Enumerate vertices that meet the weight threshold for the indexed bone. + * + * @param mesh the mesh to analyze (not null) + * @param boneIndex the index of the bone (≥0) + * @param initialScale a scale applied to vertex positions (not null, + * unaffected) + * @param offset an offset subtracted from vertex positions (not null, + * unaffected) + * @param weightThreshold the minimum bone weight for inclusion in the + * result (≥0, ≤1) + * @return a new list of vertex coordinates (not null, length a multiple of + * 3) */ private static List getPoints(Mesh mesh, int boneIndex, Vector3f initialScale, Vector3f offset, float weightThreshold) { @@ -265,12 +300,16 @@ public class RagdollUtils { } /** - * Updates a bone position and rotation. - * if the child bones are not in the bone list this means, they are not associated with a physics shape. - * So they have to be updated + * Updates a bone position and rotation. if the child bones are not in the + * bone list this means, they are not associated with a physics shape. So + * they have to be updated + * * @param bone the bone * @param pos the position * @param rot the rotation + * @param restoreBoneControl true → user-control flag should be set + * @param boneList the names of all bones without collision shapes (not + * null, unaffected) */ public static void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl, Set boneList) { //we ensure that we have the control @@ -292,6 +331,12 @@ public class RagdollUtils { } } + /** + * Alter the user-control flags of a bone and all its descendents. + * + * @param bone the ancestor bone (not null, modified) + * @param bool true to enable user control, false to disable + */ public static void setUserControl(Bone bone, boolean bool) { bone.setUserControl(bool); for (Bone child : bone.getChildren()) { diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/AbstractPhysicsDebugControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/AbstractPhysicsDebugControl.java index 75e7bbe45..44293d3b0 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/AbstractPhysicsDebugControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/AbstractPhysicsDebugControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,14 +37,27 @@ import com.jme3.scene.Spatial; import com.jme3.scene.control.AbstractControl; /** + * The abstract base class for physics-debug controls (such as + * BulletRigidBodyDebugControl) used to visualize individual collision objects + * and joints. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public abstract class AbstractPhysicsDebugControl extends AbstractControl { private final Quaternion tmp_inverseWorldRotation = new Quaternion(); + /** + * the app state that this control serves + */ protected final BulletDebugAppState debugAppState; + /** + * Instantiate an enabled control to serve the specified debug app state. + * + * @param debugAppState which app state (not null, alias created) + */ public AbstractPhysicsDebugControl(BulletDebugAppState debugAppState) { this.debugAppState = debugAppState; } @@ -55,10 +68,27 @@ public abstract class AbstractPhysicsDebugControl extends AbstractControl { @Override protected abstract void controlUpdate(float tpf); + /** + * Apply the specified location and orientation to the controlled spatial. + * + * @param worldLocation location vector (in physics-space coordinates, not + * null, unaffected) + * @param worldRotation orientation (in physics-space coordinates, not null, + * unaffected) + */ protected void applyPhysicsTransform(Vector3f worldLocation, Quaternion worldRotation) { applyPhysicsTransform(worldLocation, worldRotation, this.spatial); } + /** + * Apply the specified location and orientation to the specified spatial. + * + * @param worldLocation location vector (in physics-space coordinates, not + * null, unaffected) + * @param worldRotation orientation (in physics-space coordinates, not null, + * unaffected) + * @param spatial where to apply (may be null) + */ protected void applyPhysicsTransform(Vector3f worldLocation, Quaternion worldRotation, Spatial spatial) { if (spatial != null) { Vector3f localLocation = spatial.getLocalTranslation(); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletCharacterDebugControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletCharacterDebugControl.java index 43c1cfd36..b66620ad3 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletCharacterDebugControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletCharacterDebugControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,16 +42,37 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; /** + * A physics-debug control used to visualize a PhysicsCharacter. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class BulletCharacterDebugControl extends AbstractPhysicsDebugControl { + /** + * character to visualize (not null) + */ protected final PhysicsCharacter body; + /** + * temporary storage for physics location + */ protected final Vector3f location = new Vector3f(); protected final Quaternion rotation = new Quaternion(); + /** + * shape for which geom was generated + */ protected CollisionShape myShape; + /** + * geometry to visualize myShape (not null) + */ protected Spatial geom; + /** + * Instantiate an enabled control to visualize the specified character. + * + * @param debugAppState which app state (not null, alias created) + * @param body the character to visualize (not null, alias created) + */ public BulletCharacterDebugControl(BulletDebugAppState debugAppState, PhysicsCharacter body) { super(debugAppState); @@ -61,6 +82,13 @@ public class BulletCharacterDebugControl extends AbstractPhysicsDebugControl { geom.setMaterial(debugAppState.DEBUG_PINK); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ @Override public void setSpatial(Spatial spatial) { if (spatial != null && spatial instanceof Node) { @@ -73,6 +101,13 @@ public class BulletCharacterDebugControl extends AbstractPhysicsDebugControl { super.setSpatial(spatial); } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is enabled and added to a scene. Should be + * invoked only by a subclass or by AbstractControl. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ @Override protected void controlUpdate(float tpf) { if(myShape != body.getCollisionShape()){ @@ -86,6 +121,14 @@ public class BulletCharacterDebugControl extends AbstractPhysicsDebugControl { geom.setLocalScale(body.getCollisionShape().getScale()); } + /** + * Render this control. Invoked once port per frame, provided the + * control is enabled and added to a scene. Should be invoked only by a + * subclass or by AbstractControl. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ @Override protected void controlRender(RenderManager rm, ViewPort vp) { } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletDebugAppState.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletDebugAppState.java index 4019d2c2b..3e2f6aef1 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletDebugAppState.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletDebugAppState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,31 +55,84 @@ import java.util.logging.Level; import java.util.logging.Logger; /** + * An app state to manage a debug visualization of a physics space. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class BulletDebugAppState extends AbstractAppState { + /** + * message logger for this class + */ protected static final Logger logger = Logger.getLogger(BulletDebugAppState.class.getName()); + /** + * limit which objects are visualized, or null to visualize all objects + */ protected DebugAppStateFilter filter; protected Application app; protected AssetManager assetManager; + /** + * physics space to visualize (not null) + */ protected final PhysicsSpace space; + /** + * scene-graph node to parent the geometries + */ protected final Node physicsDebugRootNode = new Node("Physics Debug Root Node"); + /** + * view port in which to render (not null) + */ protected ViewPort viewPort; protected RenderManager rm; + /** + * material for inactive rigid bodies + */ public Material DEBUG_BLUE; public Material DEBUG_RED; + /** + * material for joints + */ public Material DEBUG_GREEN; + /** + * material for ghosts + */ public Material DEBUG_YELLOW; + /** + * material for vehicles and active rigid bodies + */ public Material DEBUG_MAGENTA; + /** + * material for physics characters + */ public Material DEBUG_PINK; + /** + * map rigid bodies to visualizations + */ protected HashMap bodies = new HashMap(); + /** + * map joints to visualizations + */ protected HashMap joints = new HashMap(); + /** + * map ghosts to visualizations + */ protected HashMap ghosts = new HashMap(); + /** + * map physics characters to visualizations + */ protected HashMap characters = new HashMap(); + /** + * map vehicles to visualizations + */ protected HashMap vehicles = new HashMap(); - + /** + * Instantiate an app state to visualize the specified space. This constructor should be invoked only by + * BulletAppState. + * + * @param space physics space to visualize (not null, alias created) + */ public BulletDebugAppState(PhysicsSpace space) { this.space = space; } @@ -88,10 +141,22 @@ public class BulletDebugAppState extends AbstractAppState { return new DebugTools(assetManager); } + /** + * Alter which objects are visualized. + * + * @param filter the desired filter, or or null to visualize all objects + */ public void setFilter(DebugAppStateFilter filter) { this.filter = filter; } + /** + * Initialize this state prior to its 1st update. Should be invoked only by + * a subclass or by the AppStateManager. + * + * @param stateManager the manager for this state (not null) + * @param app the application which owns this state (not null) + */ @Override public void initialize(AppStateManager stateManager, Application app) { super.initialize(stateManager, app); @@ -105,12 +170,25 @@ public class BulletDebugAppState extends AbstractAppState { viewPort.attachScene(physicsDebugRootNode); } + /** + * Transition this state from terminating to detached. Should be invoked + * only by a subclass or by the AppStateManager. Invoked once for each time + * {@link #initialize(com.jme3.app.state.AppStateManager, com.jme3.app.Application)} + * is invoked. + */ @Override public void cleanup() { rm.removeMainView(viewPort); super.cleanup(); } + /** + * Update this state prior to rendering. Should be invoked only by a + * subclass or by the AppStateManager. Invoked once per frame, provided the + * state is attached and enabled. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ @Override public void update(float tpf) { super.update(tpf); @@ -125,6 +203,13 @@ public class BulletDebugAppState extends AbstractAppState { physicsDebugRootNode.updateGeometricState(); } + /** + * Render this state. Should be invoked only by a subclass or by the + * AppStateManager. Invoked once per frame, provided the state is attached + * and enabled. + * + * @param rm the render manager (not null) + */ @Override public void render(RenderManager rm) { super.render(rm); @@ -133,6 +218,11 @@ public class BulletDebugAppState extends AbstractAppState { } } + /** + * Initialize the materials. + * + * @param app the application which owns this state (not null) + */ private void setupMaterials(Application app) { AssetManager manager = app.getAssetManager(); DEBUG_BLUE = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); @@ -311,14 +401,14 @@ public class BulletDebugAppState extends AbstractAppState { } /** - * Interface that allows filtering out objects from the debug display + * Interface to restrict which physics objects are visualized. */ public static interface DebugAppStateFilter { /** - * Queries an object to be displayed + * Test whether the specified physics object should be displayed. * - * @param obj The object to be displayed + * @param obj the joint or collision object to test (unaffected) * @return return true if the object should be displayed, false if not */ public boolean displayObject(Object obj); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletGhostObjectDebugControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletGhostObjectDebugControl.java index 62ab7a214..09f24c7fc 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletGhostObjectDebugControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletGhostObjectDebugControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,17 +42,41 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; /** + * A physics-debug control used to visualize a PhysicsGhostObject. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class BulletGhostObjectDebugControl extends AbstractPhysicsDebugControl { + /** + * ghost object to visualize (not null) + */ protected final PhysicsGhostObject body; + /** + * temporary storage for physics location + */ protected final Vector3f location = new Vector3f(); + /** + * temporary storage for physics rotation + */ protected final Quaternion rotation = new Quaternion(); + /** + * shape for which geom was generated (not null) + */ protected CollisionShape myShape; + /** + * geometry to visualize myShape (not null) + */ protected Spatial geom; + /** + * Instantiate an enabled control to visualize the specified ghost object. + * + * @param debugAppState which app state (not null, alias created) + * @param body which object to visualize (not null, alias created) + */ public BulletGhostObjectDebugControl(BulletDebugAppState debugAppState, PhysicsGhostObject body) { super(debugAppState); this.body = body; @@ -63,6 +87,13 @@ public class BulletGhostObjectDebugControl extends AbstractPhysicsDebugControl { geom.setMaterial(debugAppState.DEBUG_YELLOW); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ @Override public void setSpatial(Spatial spatial) { if (spatial != null && spatial instanceof Node) { @@ -75,6 +106,13 @@ public class BulletGhostObjectDebugControl extends AbstractPhysicsDebugControl { super.setSpatial(spatial); } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is enabled and added to a scene. Should be + * invoked only by a subclass or by AbstractControl. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ @Override protected void controlUpdate(float tpf) { if (myShape != body.getCollisionShape()) { @@ -88,6 +126,14 @@ public class BulletGhostObjectDebugControl extends AbstractPhysicsDebugControl { geom.setLocalScale(body.getCollisionShape().getScale()); } + /** + * Render this control. Invoked once per frame, provided the + * control is enabled and added to a scene. Should be invoked only by a + * subclass or by AbstractControl. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ @Override protected void controlRender(RenderManager rm, ViewPort vp) { } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletJointDebugControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletJointDebugControl.java index 571452811..7b11a41d6 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletJointDebugControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletJointDebugControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,9 @@ import com.jme3.scene.Spatial; import com.jme3.scene.debug.Arrow; /** + * A physics-debug control used to visualize a PhysicsJoint. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ @@ -58,6 +61,12 @@ public class BulletJointDebugControl extends AbstractPhysicsDebugControl { protected final Vector3f offA = new Vector3f(); protected final Vector3f offB = new Vector3f(); + /** + * Instantiate an enabled control to visualize the specified joint. + * + * @param debugAppState which app state (not null, alias created) + * @param body the joint to visualize (not null, alias created) + */ public BulletJointDebugControl(BulletDebugAppState debugAppState, PhysicsJoint body) { super(debugAppState); this.body = body; @@ -71,6 +80,13 @@ public class BulletJointDebugControl extends AbstractPhysicsDebugControl { geomB.setMaterial(debugAppState.DEBUG_GREEN); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ @Override public void setSpatial(Spatial spatial) { if (spatial != null && spatial instanceof Node) { @@ -85,6 +101,13 @@ public class BulletJointDebugControl extends AbstractPhysicsDebugControl { super.setSpatial(spatial); } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is enabled and added to a scene. Should be + * invoked only by a subclass or by AbstractControl. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ @Override protected void controlUpdate(float tpf) { body.getBodyA().getPhysicsLocation(a.getTranslation()); @@ -100,6 +123,14 @@ public class BulletJointDebugControl extends AbstractPhysicsDebugControl { arrowB.setArrowExtent(body.getPivotB()); } + /** + * Render this control. Invoked once per frame, provided the + * control is enabled and added to a scene. Should be invoked only by a + * subclass or by AbstractControl. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ @Override protected void controlRender(RenderManager rm, ViewPort vp) { } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletRigidBodyDebugControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletRigidBodyDebugControl.java index 3bc10f3e6..83e5b786f 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletRigidBodyDebugControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletRigidBodyDebugControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,17 +42,41 @@ import com.jme3.scene.Node; import com.jme3.scene.Spatial; /** + * A physics-debug control used to visualize a PhysicsRigidBody. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class BulletRigidBodyDebugControl extends AbstractPhysicsDebugControl { + /** + * rigid body to visualize (not null) + */ protected final PhysicsRigidBody body; + /** + * temporary storage for physics location + */ protected final Vector3f location = new Vector3f(); + /** + * temporary storage for physics rotation + */ protected final Quaternion rotation = new Quaternion(); + /** + * shape for which geom was generated (not null) + */ protected CollisionShape myShape; + /** + * geometry to visualize myShape (not null) + */ protected Spatial geom; + /** + * Instantiate an enabled control to visualize the specified body. + * + * @param debugAppState which app state (not null, alias created) + * @param body which body to visualize (not null, alias created) + */ public BulletRigidBodyDebugControl(BulletDebugAppState debugAppState, PhysicsRigidBody body) { super(debugAppState); this.body = body; @@ -62,6 +86,13 @@ public class BulletRigidBodyDebugControl extends AbstractPhysicsDebugControl { geom.setMaterial(debugAppState.DEBUG_BLUE); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ @Override public void setSpatial(Spatial spatial) { if (spatial != null && spatial instanceof Node) { @@ -74,6 +105,13 @@ public class BulletRigidBodyDebugControl extends AbstractPhysicsDebugControl { super.setSpatial(spatial); } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is enabled and added to a scene. Should be + * invoked only by a subclass or by AbstractControl. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ @Override protected void controlUpdate(float tpf) { if(myShape != body.getCollisionShape()){ @@ -91,6 +129,14 @@ public class BulletRigidBodyDebugControl extends AbstractPhysicsDebugControl { geom.setLocalScale(body.getCollisionShape().getScale()); } + /** + * Render this control. Invoked once per frame, provided the + * control is enabled and added to a scene. Should be invoked only by a + * subclass or by AbstractControl. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ @Override protected void controlRender(RenderManager rm, ViewPort vp) { } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletVehicleDebugControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletVehicleDebugControl.java index f7adf1208..c964c420b 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletVehicleDebugControl.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/BulletVehicleDebugControl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,6 +43,9 @@ import com.jme3.scene.Spatial; import com.jme3.scene.debug.Arrow; /** + * A physics-debug control used to visualize a PhysicsVehicle. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ @@ -53,6 +56,12 @@ public class BulletVehicleDebugControl extends AbstractPhysicsDebugControl { protected final Vector3f location = new Vector3f(); protected final Quaternion rotation = new Quaternion(); + /** + * Instantiate an enabled control to visualize the specified vehicle. + * + * @param debugAppState which app state (not null, alias created) + * @param body which vehicle to visualize (not null, alias created) + */ public BulletVehicleDebugControl(BulletDebugAppState debugAppState, PhysicsVehicle body) { super(debugAppState); this.body = body; @@ -60,6 +69,13 @@ public class BulletVehicleDebugControl extends AbstractPhysicsDebugControl { createVehicle(); } + /** + * Alter which spatial is controlled. Invoked when the control is added to + * or removed from a spatial. Should be invoked only by a subclass or from + * Spatial. Do not invoke directly from user code. + * + * @param spatial the spatial to control (or null) + */ @Override public void setSpatial(Spatial spatial) { if (spatial != null && spatial instanceof Node) { @@ -104,6 +120,13 @@ public class BulletVehicleDebugControl extends AbstractPhysicsDebugControl { } } + /** + * Update this control. Invoked once per frame during the logical-state + * update, provided the control is enabled and added to a scene. Should be + * invoked only by a subclass or by AbstractControl. + * + * @param tpf the time interval between frames (in seconds, ≥0) + */ @Override protected void controlUpdate(float tpf) { for (int i = 0; i < body.getNumWheels(); i++) { @@ -136,6 +159,14 @@ public class BulletVehicleDebugControl extends AbstractPhysicsDebugControl { applyPhysicsTransform(body.getPhysicsLocation(location), body.getPhysicsRotation(rotation)); } + /** + * Render this control. Invoked once per frame, provided the + * control is enabled and added to a scene. Should be invoked only by a + * subclass or by AbstractControl. + * + * @param rm the render manager (not null) + * @param vp the view port to render (not null) + */ @Override protected void controlRender(RenderManager rm, ViewPort vp) { } diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/debug/DebugTools.java b/jme3-bullet/src/common/java/com/jme3/bullet/debug/DebugTools.java index 2e7246b7a..16bc38ac8 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/debug/DebugTools.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/debug/DebugTools.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,43 +42,129 @@ import com.jme3.scene.Node; import com.jme3.scene.debug.Arrow; /** + * Debugging aids. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen */ public class DebugTools { protected final AssetManager manager; + /** + * unshaded blue material + */ public Material DEBUG_BLUE; + /** + * unshaded red material + */ public Material DEBUG_RED; + /** + * unshaded green material + */ public Material DEBUG_GREEN; + /** + * unshaded yellow material + */ public Material DEBUG_YELLOW; + /** + * unshaded magenta material + */ public Material DEBUG_MAGENTA; + /** + * unshaded pink material + */ public Material DEBUG_PINK; + /** + * node for attaching debug geometries + */ public Node debugNode = new Node("Debug Node"); + /** + * mesh for the blue arrow + */ public Arrow arrowBlue = new Arrow(Vector3f.ZERO); + /** + * geometry for the blue arrow + */ public Geometry arrowBlueGeom = new Geometry("Blue Arrow", arrowBlue); + /** + * mesh for the green arrow + */ public Arrow arrowGreen = new Arrow(Vector3f.ZERO); + /** + * geometry for the green arrow + */ public Geometry arrowGreenGeom = new Geometry("Green Arrow", arrowGreen); + /** + * mesh for the red arrow + */ public Arrow arrowRed = new Arrow(Vector3f.ZERO); + /** + * geometry for the red arrow + */ public Geometry arrowRedGeom = new Geometry("Red Arrow", arrowRed); + /** + * mesh for the magenta arrow + */ public Arrow arrowMagenta = new Arrow(Vector3f.ZERO); + /** + * geometry for the magenta arrow + */ public Geometry arrowMagentaGeom = new Geometry("Magenta Arrow", arrowMagenta); + /** + * mesh for the yellow arrow + */ public Arrow arrowYellow = new Arrow(Vector3f.ZERO); + /** + * geometry for the yellow arrow + */ public Geometry arrowYellowGeom = new Geometry("Yellow Arrow", arrowYellow); + /** + * mesh for the pink arrow + */ public Arrow arrowPink = new Arrow(Vector3f.ZERO); + /** + * geometry for the pink arrow + */ public Geometry arrowPinkGeom = new Geometry("Pink Arrow", arrowPink); + /** + * local copy of {@link com.jme3.math.Vector3f#UNIT_X} + */ protected static final Vector3f UNIT_X_CHECK = new Vector3f(1, 0, 0); + /** + * local copy of {@link com.jme3.math.Vector3f#UNIT_Y} + */ protected static final Vector3f UNIT_Y_CHECK = new Vector3f(0, 1, 0); + /** + * local copy of {@link com.jme3.math.Vector3f#UNIT_Z} + */ protected static final Vector3f UNIT_Z_CHECK = new Vector3f(0, 0, 1); + /** + * local copy of {@link com.jme3.math.Vector3f#UNIT_XYZ} + */ protected static final Vector3f UNIT_XYZ_CHECK = new Vector3f(1, 1, 1); + /** + * local copy of {@link com.jme3.math.Vector3f#ZERO} + */ protected static final Vector3f ZERO_CHECK = new Vector3f(0, 0, 0); + /** + * Instantiate a set of debug tools. + * + * @param manager for loading assets (not null, alias created) + */ public DebugTools(AssetManager manager) { this.manager = manager; setupMaterials(); setupDebugNode(); } + /** + * Render all the debug geometries to the specified view port. + * + * @param rm the render manager (not null) + * @param vp the view port (not null) + */ public void show(RenderManager rm, ViewPort vp) { if (!Vector3f.UNIT_X.equals(UNIT_X_CHECK) || !Vector3f.UNIT_Y.equals(UNIT_Y_CHECK) || !Vector3f.UNIT_Z.equals(UNIT_Z_CHECK) || !Vector3f.UNIT_XYZ.equals(UNIT_XYZ_CHECK) || !Vector3f.ZERO.equals(ZERO_CHECK)) { @@ -94,36 +180,75 @@ public class DebugTools { rm.renderScene(debugNode, vp); } + /** + * Alter the location and extent of the blue arrow. + * + * @param location the coordinates of the tail (not null, unaffected) + * @param extent the offset of the tip from the tail (not null, unaffected) + */ public void setBlueArrow(Vector3f location, Vector3f extent) { arrowBlueGeom.setLocalTranslation(location); arrowBlue.setArrowExtent(extent); } + /** + * Alter the location and extent of the green arrow. + * + * @param location the coordinates of the tail (not null, unaffected) + * @param extent the offset of the tip from the tail (not null, unaffected) + */ public void setGreenArrow(Vector3f location, Vector3f extent) { arrowGreenGeom.setLocalTranslation(location); arrowGreen.setArrowExtent(extent); } + /** + * Alter the location and extent of the red arrow. + * + * @param location the coordinates of the tail (not null, unaffected) + * @param extent the offset of the tip from the tail (not null, unaffected) + */ public void setRedArrow(Vector3f location, Vector3f extent) { arrowRedGeom.setLocalTranslation(location); arrowRed.setArrowExtent(extent); } + /** + * Alter the location and extent of the magenta arrow. + * + * @param location the coordinates of the tail (not null, unaffected) + * @param extent the offset of the tip from the tail (not null, unaffected) + */ public void setMagentaArrow(Vector3f location, Vector3f extent) { arrowMagentaGeom.setLocalTranslation(location); arrowMagenta.setArrowExtent(extent); } + /** + * Alter the location and extent of the yellow arrow. + * + * @param location the coordinates of the tail (not null, unaffected) + * @param extent the offset of the tip from the tail (not null, unaffected) + */ public void setYellowArrow(Vector3f location, Vector3f extent) { arrowYellowGeom.setLocalTranslation(location); arrowYellow.setArrowExtent(extent); } + /** + * Alter the location and extent of the pink arrow. + * + * @param location the coordinates of the tail (not null, unaffected) + * @param extent the offset of the tip from the tail (not null, unaffected) + */ public void setPinkArrow(Vector3f location, Vector3f extent) { arrowPinkGeom.setLocalTranslation(location); arrowPink.setArrowExtent(extent); } + /** + * Attach all the debug geometries to the debug node. + */ protected void setupDebugNode() { arrowBlueGeom.setMaterial(DEBUG_BLUE); arrowGreenGeom.setMaterial(DEBUG_GREEN); @@ -139,6 +264,9 @@ public class DebugTools { debugNode.attachChild(arrowPinkGeom); } + /** + * Initialize all the DebugTools materials. + */ protected void setupMaterials() { DEBUG_BLUE = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); DEBUG_BLUE.getAdditionalRenderState().setWireframe(true); diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/util/CollisionShapeFactory.java b/jme3-bullet/src/common/java/com/jme3/bullet/util/CollisionShapeFactory.java index de28aba02..19e14d49b 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/util/CollisionShapeFactory.java +++ b/jme3-bullet/src/common/java/com/jme3/bullet/util/CollisionShapeFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,17 +44,21 @@ import java.util.Iterator; import java.util.LinkedList; /** + * A utility class for generating collision shapes from Spatials. + *

+ * This class is shared between JBullet and Native Bullet. * * @author normenhansen, tim8dev */ public class CollisionShapeFactory { /** - * returns the correct transform for a collisionshape in relation - * to the ancestor for which the collisionshape is generated + * Calculate the correct transform for a collision shape relative to the + * ancestor for which the shape was generated. + * * @param spat * @param parent - * @return + * @return a new instance (not null) */ private static Transform getTransform(Spatial spat, Spatial parent) { Transform shapeTransform = new Transform(); @@ -135,30 +139,48 @@ public class CollisionShapeFactory { } /** - * This type of collision shape is mesh-accurate and meant for immovable "world objects". - * Examples include terrain, houses or whole shooter levels.
- * Objects with "mesh" type collision shape will not collide with each other. + * This type of collision shape is mesh-accurate and meant for immovable + * "world objects". Examples include terrain, houses or whole shooter + * levels. + *

+ * Objects with "mesh" type collision shape will not collide with each + * other. + * + * @param rootNode the node on which to base the shape (not null) + * @return a new shape (not null) */ private static CompoundCollisionShape createMeshCompoundShape(Node rootNode) { return createCompoundShape(rootNode, new CompoundCollisionShape(), true); } /** - * This type of collision shape creates a CompoundShape made out of boxes that - * are based on the bounds of the Geometries in the tree. - * @param rootNode - * @return + * This type of collision shape creates a CompoundShape made out of boxes + * that are based on the bounds of the Geometries in the tree. + * + * @param rootNode the node on which to base the shape (not null) + * @return a new shape (not null) */ private static CompoundCollisionShape createBoxCompoundShape(Node rootNode) { return createCompoundShape(rootNode, new CompoundCollisionShape(), false); } /** - * This type of collision shape is mesh-accurate and meant for immovable "world objects". - * Examples include terrain, houses or whole shooter levels.
- * Objects with "mesh" type collision shape will not collide with each other.
- * Creates a HeightfieldCollisionShape if the supplied spatial is a TerrainQuad. - * @return A MeshCollisionShape or a CompoundCollisionShape with MeshCollisionShapes as children if the supplied spatial is a Node. A HeightieldCollisionShape if a TerrainQuad was supplied. + * Create a mesh shape for the given Spatial. + *

+ * This type of collision shape is mesh-accurate and meant for immovable + * "world objects". Examples include terrain, houses or whole shooter + * levels. + *

+ * Objects with "mesh" type collision shape will not collide with each + * other. + *

+ * Creates a HeightfieldCollisionShape if the supplied spatial is a + * TerrainQuad. + * + * @param spatial the spatial on which to base the shape (not null) + * @return A MeshCollisionShape or a CompoundCollisionShape with + * MeshCollisionShapes as children if the supplied spatial is a Node. A + * HeightieldCollisionShape if a TerrainQuad was supplied. */ public static CollisionShape createMeshShape(Spatial spatial) { if (spatial instanceof TerrainQuad) { @@ -177,9 +199,14 @@ public class CollisionShapeFactory { } /** - * This method creates a hull shape for the given Spatial.
- * If you want to have mesh-accurate dynamic shapes (CPU intense!!!) use GImpact shapes, its probably best to do so with a low-poly version of your model. - * @return A HullCollisionShape or a CompoundCollisionShape with HullCollisionShapes as children if the supplied spatial is a Node. + * Create a hull shape for the given Spatial. + *

+ * For mesh-accurate animated meshes (CPU intense!) use GImpact shapes. + * + * @param spatial the spatial on which to base the shape (not null) + * @return a HullCollisionShape (if spatial is a Geometry) or a + * CompoundCollisionShape with HullCollisionShapes as children (if spatial + * is a Node) */ public static CollisionShape createDynamicMeshShape(Spatial spatial) { if (spatial instanceof Geometry) { @@ -192,6 +219,14 @@ public class CollisionShapeFactory { } + /** + * Create a box shape for the given Spatial. + * + * @param spatial the spatial on which to base the shape (not null) + * @return a BoxCollisionShape (if spatial is a Geometry) or a + * CompoundCollisionShape with BoxCollisionShapes as children (if spatial is + * a Node) + */ public static CollisionShape createBoxShape(Spatial spatial) { if (spatial instanceof Geometry) { return createSingleBoxShape((Geometry) spatial, spatial); @@ -203,9 +238,12 @@ public class CollisionShapeFactory { } /** - * This type of collision shape is mesh-accurate and meant for immovable "world objects". - * Examples include terrain, houses or whole shooter levels.
- * Objects with "mesh" type collision shape will not collide with each other. + * This type of collision shape is mesh-accurate and meant for immovable + * "world objects". Examples include terrain, houses or whole shooter + * levels. + *

+ * Objects with "mesh" type collision shape will not collide with each + * other. */ private static MeshCollisionShape createSingleMeshShape(Geometry geom, Spatial parent) { Mesh mesh = geom.getMesh(); @@ -220,9 +258,13 @@ public class CollisionShapeFactory { } /** - * Uses the bounding box of the supplied spatial to create a BoxCollisionShape - * @param spatial - * @return BoxCollisionShape with the size of the spatials BoundingBox + * Use the bounding box of the supplied spatial to create a + * BoxCollisionShape. + * + * @param spatial the spatial on which to base the shape (not null) + * @param parent unused + * @return a new shape with the dimensions of the spatial's bounding box + * (not null) */ private static BoxCollisionShape createSingleBoxShape(Spatial spatial, Spatial parent) { //TODO: using world bound here instead of "local world" bound... @@ -232,7 +274,10 @@ public class CollisionShapeFactory { } /** - * This method creates a hull collision shape for the given mesh.
+ * Create a hull collision shape for the specified geometry. + * + * @param geom the geometry on which to base the shape (not null) + * @param parent */ private static HullCollisionShape createSingleDynamicMeshShape(Geometry geom, Spatial parent) { Mesh mesh = geom.getMesh(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java index 83d26e3aa..c2a2bc7d8 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/PhysicsSpace.java @@ -62,17 +62,36 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - *

PhysicsSpace - The central jbullet-jme physics space

+ * A jbullet-jme physics space with its own btDynamicsWorld. * * @author normenhansen */ public class PhysicsSpace { + /** + * message logger for this class + */ private static final Logger logger = Logger.getLogger(PhysicsSpace.class.getName()); + /** + * index of the X axis + */ public static final int AXIS_X = 0; + /** + * index of the Y axis + */ public static final int AXIS_Y = 1; + /** + * index of the Z axis + */ public static final int AXIS_Z = 2; + /** + * Bullet identifier of the physics space. The constructor sets this to a + * non-zero value. + */ private long physicsSpaceId = 0; + /** + * first-in/first-out (FIFO) queue of physics tasks for each thread + */ private static ThreadLocal>> pQueueTL = new ThreadLocal>>() { @Override @@ -80,8 +99,17 @@ public class PhysicsSpace { return new ConcurrentLinkedQueue>(); } }; + /** + * first-in/first-out (FIFO) queue of physics tasks + */ private ConcurrentLinkedQueue> pQueue = new ConcurrentLinkedQueue>(); + /** + * physics space for each thread + */ private static ThreadLocal physicsSpaceTL = new ThreadLocal(); + /** + * copy of type of acceleration structure used + */ private BroadphaseType broadphaseType = BroadphaseType.DBVT; // private DiscreteDynamicsWorld dynamicsWorld = null; // private BroadphaseInterface broadphase; @@ -99,10 +127,32 @@ public class PhysicsSpace { private Map collisionGroupListeners = new ConcurrentHashMap(); private ConcurrentLinkedQueue tickListeners = new ConcurrentLinkedQueue(); private PhysicsCollisionEventFactory eventFactory = new PhysicsCollisionEventFactory(); + /** + * copy of minimum coordinate values when using AXIS_SWEEP broadphase + * algorithms + */ private Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f); + /** + * copy of maximum coordinate values when using AXIS_SWEEP broadphase + * algorithms + */ private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f); + /** + * physics time step (in seconds, >0) + */ private float accuracy = 1f / 60f; - private int maxSubSteps = 4, rayTestFlags = 1 << 2; + /** + * maximum number of physics steps per frame (≥0, default=4) + */ + private int maxSubSteps = 4; + /** + * flags used in ray tests + */ + private int rayTestFlags = 1 << 2; + /** + * copy of number of iterations used by the contact-and-constraint solver + * (default=10) + */ private int solverNumIterations = 10; static { @@ -111,9 +161,8 @@ public class PhysicsSpace { } /** - * Get the current PhysicsSpace running on this thread
For - * parallel physics, this can also be called from the OpenGL thread to - * receive the PhysicsSpace + * Access the PhysicsSpace running on this thread. For parallel + * physics, this can be invoked from the OpenGL thread. * * @return the PhysicsSpace running on this thread */ @@ -124,24 +173,47 @@ public class PhysicsSpace { /** * Used internally * - * @param space + * @param space which physics space to simulate on this thread */ public static void setLocalThreadPhysicsSpace(PhysicsSpace space) { physicsSpaceTL.set(space); } + /** + * Instantiate a PhysicsSpace. Must be invoked on the designated physics + * thread. + */ public PhysicsSpace() { this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT); } + /** + * Instantiate a PhysicsSpace. Must be invoked on the designated physics + * thread. + */ public PhysicsSpace(BroadphaseType broadphaseType) { this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType); } + /** + * Instantiate a PhysicsSpace. Must be invoked on the designated physics + * thread. + */ public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) { this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3); } + /** + * Instantiate a PhysicsSpace. Must be invoked on the designated physics + * thread. + * + * @param worldMin the desired minimum coordinates values (not null, + * unaffected, default=-10k,-10k,-10k) + * @param worldMax the desired minimum coordinates values (not null, + * unaffected, default=10k,10k,10k) + * @param broadphaseType which broadphase collision-detection algorithm to + * use (not null) + */ public PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) { this.worldMin.set(worldMin); this.worldMax.set(worldMax); @@ -150,7 +222,7 @@ public class PhysicsSpace { } /** - * Has to be called from the (designated) physics thread + * Must be invoked on the designated physics thread. */ public void create() { physicsSpaceId = createPhysicsSpace(worldMin.x, worldMin.y, worldMin.z, worldMax.x, worldMax.y, worldMax.z, broadphaseType.ordinal(), false); @@ -191,6 +263,13 @@ public class PhysicsSpace { private native long createPhysicsSpace(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int broadphaseType, boolean threading); + /** + * Callback invoked just before the physics is stepped. + *

+ * This method is invoked from native code. + * + * @param timeStep the time per physics step (in seconds, ≥0) + */ private void preTick_native(float f) { AppTask task; while((task=pQueue.poll())!=null){ @@ -208,6 +287,13 @@ public class PhysicsSpace { } } + /** + * Callback invoked just after the physics is stepped. + *

+ * This method is invoked from native code. + * + * @param timeStep the time per physics step (in seconds, ≥0) + */ private void postTick_native(float f) { for (Iterator it = tickListeners.iterator(); it.hasNext();) { PhysicsTickListener physicsTickCallback = it.next(); @@ -215,6 +301,9 @@ public class PhysicsSpace { } } + /** + * This method is invoked from native code. + */ private void addCollision_native() { } @@ -334,6 +423,9 @@ public class PhysicsSpace { collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, manifoldPointObjectId)); } + /** + * This method is invoked from native code. + */ private boolean notifyCollisionGroupListeners_native(PhysicsCollisionObject node, PhysicsCollisionObject node1){ PhysicsCollisionGroupListener listener = collisionGroupListeners.get(node.getCollisionGroup()); PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(node1.getCollisionGroup()); @@ -350,19 +442,21 @@ public class PhysicsSpace { } /** - * updates the physics space + * Update this space. Invoked (by the Bullet app state) once per frame while + * the app state is attached and enabled. * - * @param time the current time value + * @param time time-per-frame multiplied by speed (in seconds, ≥0) */ public void update(float time) { update(time, maxSubSteps); } /** - * updates the physics space, uses maxSteps
+ * Simulate for the specified time interval, using no more than the + * specified number of steps. * - * @param time the current time value - * @param maxSteps + * @param time the time interval (in seconds, ≥0) + * @param maxSteps the maximum number of steps (≥1) */ public void update(float time, int maxSteps) { // if (getDynamicsWorld() == null) { @@ -374,6 +468,9 @@ public class PhysicsSpace { private native void stepSimulation(long space, float time, int maxSteps, float accuracy); + /** + * Distribute each collision event to all listeners. + */ public void distributeEvents() { //add collision callbacks int clistsize = collisionListeners.size(); @@ -387,6 +484,13 @@ public class PhysicsSpace { } } + /** + * Enqueue a callable on the currently executing thread. + * + * @param the task's result type + * @param callable the task to be executed + * @return a new task (not null) + */ public static Future enqueueOnThisThread(Callable callable) { AppTask task = new AppTask(callable); System.out.println("created apptask"); @@ -395,11 +499,11 @@ public class PhysicsSpace { } /** - * calls the callable on the next physics tick (ensuring e.g. force - * applying) + * Invoke the specified callable during the next physics tick. This is + * useful for applying forces. * - * @param - * @param callable + * @param the return type of the callable + * @param callable which callable to invoke * @return Future object */ public Future enqueue(Callable callable) { @@ -409,9 +513,10 @@ public class PhysicsSpace { } /** - * adds an object to the physics space + * Add the specified object to this space. * - * @param obj the PhysicsControl or Spatial with PhysicsControl to add + * @param obj the PhysicsControl, Spatial-with-PhysicsControl, + * PhysicsCollisionObject, or PhysicsJoint to add (not null, modified) */ public void add(Object obj) { if (obj instanceof PhysicsControl) { @@ -432,6 +537,11 @@ public class PhysicsSpace { } } + /** + * Add the specified collision object to this space. + * + * @param obj the PhysicsCollisionObject to add (not null, modified) + */ public void addCollisionObject(PhysicsCollisionObject obj) { if (obj instanceof PhysicsGhostObject) { addGhostObject((PhysicsGhostObject) obj); @@ -445,9 +555,9 @@ public class PhysicsSpace { } /** - * removes an object from the physics space + * Remove the specified object from this space. * - * @param obj the PhysicsControl or Spatial with PhysicsControl to remove + * @param obj the PhysicsCollisionObject to add, or null (modified) */ public void remove(Object obj) { if (obj == null) return; @@ -469,6 +579,11 @@ public class PhysicsSpace { } } + /** + * Remove the specified collision object from this space. + * + * @param obj the PhysicsControl or Spatial with PhysicsControl to remove + */ public void removeCollisionObject(PhysicsCollisionObject obj) { if (obj instanceof PhysicsGhostObject) { removeGhostObject((PhysicsGhostObject) obj); @@ -480,9 +595,10 @@ public class PhysicsSpace { } /** - * adds all physics controls and joints in the given spatial node to the physics space - * (e.g. after loading from disk) - recursive if node - * @param spatial the rootnode containing the physics objects + * Add all physics controls and joints in the specified subtree of the scene + * graph to this space (e.g. after loading from disk). Note: recursive! + * + * @param spatial the root of the subtree (not null) */ public void addAll(Spatial spatial) { add(spatial); @@ -510,9 +626,11 @@ public class PhysicsSpace { } /** - * Removes all physics controls and joints in the given spatial from the physics space - * (e.g. before saving to disk) - recursive if node - * @param spatial the rootnode containing the physics objects + * Remove all physics controls and joints in the specified subtree of the + * scene graph from the physics space (e.g. before saving to disk) Note: + * recursive! + * + * @param spatial the root of the subtree (not null) */ public void removeAll(Spatial spatial) { if (spatial.getControl(RigidBodyControl.class) != null) { @@ -611,6 +729,12 @@ public class PhysicsSpace { // dynamicsWorld.removeCollisionObject(node.getObjectId()); } + /** + * NOTE: When a rigid body is added, its gravity gets set to that of the + * physics space. + * + * @param node the body to add (not null, not already in the space) + */ private void addRigidBody(PhysicsRigidBody node) { if (physicsBodies.containsKey(node.getObjectId())) { logger.log(Level.WARNING, "RigidBody {0} already exists in PhysicsSpace, cannot add.", node); @@ -619,7 +743,7 @@ public class PhysicsSpace { physicsBodies.put(node.getObjectId(), node); //Workaround - //It seems that adding a Kinematic RigidBody to the dynamicWorld prevent it from being non kinematic again afterward. + //It seems that adding a Kinematic RigidBody to the dynamicWorld prevents it from being non-kinematic again afterward. //so we add it non kinematic, then set it kinematic again. boolean kinematic = false; if (node.isKinematic()) { @@ -682,30 +806,64 @@ public class PhysicsSpace { // dynamicsWorld.removeConstraint(joint.getObjectId()); } + /** + * Copy the list of rigid bodies that have been added to this space and not + * yet removed. + * + * @return a new list (not null) + */ public Collection getRigidBodyList() { return new LinkedList(physicsBodies.values()); } + /** + * Copy the list of ghost objects that have been added to this space and not + * yet removed. + * + * @return a new list (not null) + */ public Collection getGhostObjectList() { return new LinkedList(physicsGhostObjects.values()); } + /** + * Copy the list of physics characters that have been added to this space + * and not yet removed. + * + * @return a new list (not null) + */ public Collection getCharacterList() { return new LinkedList(physicsCharacters.values()); } + /** + * Copy the list of physics joints that have been added to this space and + * not yet removed. + * + * @return a new list (not null) + */ public Collection getJointList() { return new LinkedList(physicsJoints.values()); } + /** + * Copy the list of physics vehicles that have been added to this space and + * not yet removed. + * + * @return a new list (not null) + */ public Collection getVehicleList() { return new LinkedList(physicsVehicles.values()); } /** - * Sets the gravity of the PhysicsSpace, set before adding physics objects! + * Alter the gravitational acceleration acting on newly-added bodies. + *

+ * Whenever a rigid body is added to a space, the body's gravity gets set to + * that of the space. Thus it makes sense to set the space's vector before + * adding any bodies to the space. * - * @param gravity + * @param gravity the desired acceleration vector (not null, unaffected) */ public void setGravity(Vector3f gravity) { this.gravity.set(gravity); @@ -714,8 +872,17 @@ public class PhysicsSpace { private native void setGravity(long spaceId, Vector3f gravity); - //TODO: getGravity + /** + * copy of gravity-acceleration vector (default is 9.81 in the -Y direction, + * corresponding to Earth-normal in MKS units) + */ private final Vector3f gravity = new Vector3f(0,-9.81f,0); + /** + * Copy the gravitational acceleration acting on newly-added bodies. + * + * @param gravity storage for the result (not null, modified) + * @return acceleration (in the vector provided) + */ public Vector3f getGravity(Vector3f gravity) { return gravity.set(this.gravity); } @@ -735,57 +902,89 @@ public class PhysicsSpace { // } // /** - * Adds the specified listener to the physics tick listeners. The listeners - * are called on each physics step, which is not necessarily each frame but - * is determined by the accuracy of the physics space. + * Register the specified tick listener with this space. + *

+ * Tick listeners are notified before and after each physics step. A physics + * step is not necessarily the same as a frame; it is more influenced by the + * accuracy of the physics space. * - * @param listener + * @see #setAccuracy(float) + * + * @param listener the listener to register (not null) */ public void addTickListener(PhysicsTickListener listener) { tickListeners.add(listener); } + /** + * De-register the specified tick listener. + * + * @see #addTickListener(com.jme3.bullet.PhysicsTickListener) + * @param listener the listener to de-register (not null) + */ public void removeTickListener(PhysicsTickListener listener) { tickListeners.remove(listener); } /** - * Adds a CollisionListener that will be informed about collision events + * Register the specified collision listener with this space. + *

+ * Collision listeners are notified when collisions occur in the space. * - * @param listener the CollisionListener to add + * @param listener the listener to register (not null, alias created) */ public void addCollisionListener(PhysicsCollisionListener listener) { collisionListeners.add(listener); } /** - * Removes a CollisionListener from the list + * De-register the specified collision listener. * - * @param listener the CollisionListener to remove + * @see + * #addCollisionListener(com.jme3.bullet.collision.PhysicsCollisionListener) + * @param listener the listener to de-register (not null) */ public void removeCollisionListener(PhysicsCollisionListener listener) { collisionListeners.remove(listener); } /** - * Adds a listener for a specific collision group, such a listener can - * disable collisions when they happen.
There can be only one listener - * per collision group. + * Register the specified collision-group listener with the specified + * collision group of this space. + *

+ * Such a listener can disable collisions when they occur. There can be only + * one listener per collision group per space. * - * @param listener - * @param collisionGroup + * @param listener the listener to register (not null) + * @param collisionGroup which group it should listen for (bit mask with + * exactly one bit set) */ public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) { collisionGroupListeners.put(collisionGroup, listener); } + /** + * De-register the specified collision-group listener. + * + * @see + * #addCollisionGroupListener(com.jme3.bullet.collision.PhysicsCollisionGroupListener, + * int) + * @param collisionGroup the group of the listener to de-register (bit mask + * with exactly one bit set) + */ public void removeCollisionGroupListener(int collisionGroup) { collisionGroupListeners.remove(collisionGroup); } /** - * Performs a ray collision test and returns the results as a list of - * PhysicsRayTestResults ordered by it hitFraction (lower to higher) + * Perform a ray-collision test and return the results as a list of + * PhysicsRayTestResults sorted by ascending hitFraction. + * + * @param from the starting location (physics-space coordinates, not null, + * unaffected) + * @param to the ending location (in physics-space coordinates, not null, + * unaffected) + * @return a new list of results (not null) */ public List rayTest(Vector3f from, Vector3f to) { List results = new ArrayList(); @@ -795,8 +994,14 @@ public class PhysicsSpace { } /** - * Performs a ray collision test and returns the results as a list of - * PhysicsRayTestResults without performing any sort operation + * Perform a ray-collision test and return the results as a list of + * PhysicsRayTestResults in arbitrary order. + * + * @param from the starting location (in physics-space coordinates, not + * null, unaffected) + * @param to the ending location (in physics-space coordinates, not null, + * unaffected) + * @return a new list of results (not null) */ public List rayTestRaw(Vector3f from, Vector3f to) { List results = new ArrayList(); @@ -806,17 +1011,22 @@ public class PhysicsSpace { } /** - * Sets m_flags for raytest, see https://code.google.com/p/bullet/source/browse/trunk/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h + * Alters the m_flags used in ray tests. see + * https://code.google.com/p/bullet/source/browse/trunk/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h * for possible options. Defaults to using the faster, approximate raytest. + * + * @param flags the desired flags, ORed together (default=0x4) */ public void SetRayTestFlags(int flags) { rayTestFlags = flags; } /** - * Gets m_flags for raytest, see https://code.google.com/p/bullet/source/browse/trunk/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h + * Reads m_flags used in ray tests. see + * https://code.google.com/p/bullet/source/browse/trunk/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h * for possible options. - * @return rayTestFlags + * + * @return which flags are used */ public int GetRayTestFlags() { return rayTestFlags; @@ -831,8 +1041,15 @@ public class PhysicsSpace { }; /** - * Performs a ray collision test and returns the results as a list of - * PhysicsRayTestResults ordered by it hitFraction (lower to higher) + * Perform a ray-collision test and return the results as a list of + * PhysicsRayTestResults sorted by ascending hitFraction. + * + * @param from coordinates of the starting location (in physics space, not + * null, unaffected) + * @param to coordinates of the ending location (in physics space, not null, + * unaffected) + * @param results the list to hold results (not null, modified) + * @return results */ public List rayTest(Vector3f from, Vector3f to, List results) { results.clear(); @@ -843,8 +1060,15 @@ public class PhysicsSpace { } /** - * Performs a ray collision test and returns the results as a list of - * PhysicsRayTestResults without performing any sort operation + * Perform a ray-collision test and return the results as a list of + * PhysicsRayTestResults in arbitrary order. + * + * @param from coordinates of the starting location (in physics space, not + * null, unaffected) + * @param to coordinates of the ending location (in physics space, not null, + * unaffected) + * @param results the list to hold results (not null, modified) + * @return results */ public List rayTestRaw(Vector3f from, Vector3f to, List results) { results.clear(); @@ -874,11 +1098,18 @@ public class PhysicsSpace { /** - * Performs a sweep collision test and returns the results as a list of - * PhysicsSweepTestResults
You have to use different Transforms for - * start and end (at least distance > 0.4f). SweepTest will not see a - * collision if it starts INSIDE an object and is moving AWAY from its - * center. + * Perform a sweep-collision test and return the results as a new list. + *

+ * The starting and ending locations must be at least 0.4f physics-space + * units apart. + *

+ * A sweep test will miss a collision if it starts inside an object and + * sweeps away from the object's center. + * + * @param shape the shape to sweep (not null) + * @param start the starting physics-space transform (not null) + * @param end the ending physics-space transform (not null) + * @return a new list of results */ public List sweepTest(CollisionShape shape, Transform start, Transform end) { List results = new LinkedList(); @@ -886,17 +1117,41 @@ public class PhysicsSpace { return (List) results; } + /** + * Perform a sweep-collision test and store the results in an existing list. + *

+ * The starting and ending locations must be at least 0.4f physics-space + * units apart. + *

+ * A sweep test will miss a collision if it starts inside an object and + * sweeps away from the object's center. + * + * @param shape the shape to sweep (not null) + * @param start the starting physics-space transform (not null) + * @param end the ending physics-space transform (not null) + * @param results the list to hold results (not null, modified) + * @return results + */ public List sweepTest(CollisionShape shape, Transform start, Transform end, List results) { return sweepTest(shape, start, end, results, 0.0f); } public native void sweepTest_native(long shape, Transform from, Transform to, long physicsSpaceId, List results, float allowedCcdPenetration); /** - * Performs a sweep collision test and returns the results as a list of - * PhysicsSweepTestResults
You have to use different Transforms for - * start and end (at least distance > allowedCcdPenetration). SweepTest will not see a - * collision if it starts INSIDE an object and is moving AWAY from its - * center. + * Perform a sweep-collision test and store the results in an existing list. + *

+ * The starting and ending locations must be at least 0.4f physics-space + * units apart. + *

+ * A sweep test will miss a collision if it starts inside an object and + * sweeps away from the object's center. + * + * @param shape the shape to sweep (not null) + * @param start the starting physics-space transform (not null) + * @param end the ending physics-space transform (not null) + * @param results the list to hold results (not null, modified) + * @param allowedCcdPenetration true→allow, false→disallow + * @return results */ public List sweepTest(CollisionShape shape, Transform start, Transform end, List results, float allowedCcdPenetration ) { results.clear(); @@ -923,7 +1178,7 @@ public class PhysicsSpace { */ /** - * destroys the current PhysicsSpace so that a new one can be created + * Destroy this space so that a new one can be instantiated. */ public void destroy() { physicsBodies.clear(); @@ -942,59 +1197,87 @@ public class PhysicsSpace { return physicsSpaceId; } + /** + * Read the type of acceleration structure used. + * + * @return an enum value (not null) + */ public BroadphaseType getBroadphaseType() { return broadphaseType; } + /** + * Alter the type of acceleration structure used. + * + * @param broadphaseType the desired algorithm (not null) + */ public void setBroadphaseType(BroadphaseType broadphaseType) { this.broadphaseType = broadphaseType; } /** - * Sets the maximum amount of extra steps that will be used to step the - * physics when the fps is below the physics fps. Doing this maintains - * determinism in physics. For example a maximum number of 2 can compensate - * for framerates as low as 30fps when the physics has the default accuracy - * of 60 fps. Note that setting this value too high can make the physics - * drive down its own fps in case it's overloaded. + * Alter the maximum number of physics steps per frame. + *

+ * Extra physics steps help maintain determinism when the render fps drops + * below 1/accuracy. For example a value of 2 can compensate for frame rates + * as low as 30fps, assuming the physics has an accuracy of 1/60 sec. + *

+ * Setting this value too high can depress the frame rate. * - * @param steps The maximum number of extra steps, default is 4. + * @param steps the desired maximum number of steps per frame (≥1, + * default=4) */ public void setMaxSubSteps(int steps) { maxSubSteps = steps; } /** - * get the current accuracy of the physics computation + * Read the accuracy (time step) of the physics simulation. * - * @return the current accuracy + * @return the timestep (in seconds, >0) */ public float getAccuracy() { return accuracy; } /** - * sets the accuracy of the physics computation, default=1/60s
+ * Alter the accuracy (time step) of the physics simulation. + *

+ * In general, the smaller the time step, the more accurate (and + * compute-intensive) the simulation will be. Bullet works best with a + * timestep of no more than 1/60 second. * - * @param accuracy + * @param accuracy the desired time step (in seconds, >0, default=1/60) */ public void setAccuracy(float accuracy) { this.accuracy = accuracy; } + /** + * Access the minimum coordinate values for this space. + * + * @return the pre-existing vector + */ public Vector3f getWorldMin() { return worldMin; } /** - * only applies for AXIS_SWEEP broadphase + * Alter the minimum coordinate values for this space. (only affects + * AXIS_SWEEP broadphase algorithms) * - * @param worldMin + * @param worldMin the desired minimum coordinate values (not null, + * unaffected) */ public void setWorldMin(Vector3f worldMin) { this.worldMin.set(worldMin); } + /** + * Access the maximum coordinate values for this space. + * + * @return the pre-existing vector (not null) + */ public Vector3f getWorldMax() { return worldMax; } @@ -1009,11 +1292,11 @@ public class PhysicsSpace { } /** - * Set the number of iterations used by the contact solver. - * - * The default is 10. Use 4 for low quality, 20 for high quality. - * - * @param numIterations The number of iterations used by the contact & constraint solver. + * Alter the number of iterations used by the contact-and-constraint solver. + *

+ * Use 4 for low quality, 20 for high quality. + * + * @param numIterations the desired number of iterations (≥1, default=10) */ public void setSolverNumIterations(int numIterations) { this.solverNumIterations = numIterations; @@ -1021,9 +1304,9 @@ public class PhysicsSpace { } /** - * Get the number of iterations used by the contact solver. - * - * @return The number of iterations used by the contact & constraint solver. + * Read the number of iterations used by the contact-and-constraint solver. + * + * @return the number of iterations used */ public int getSolverNumIterations() { return solverNumIterations; @@ -1034,28 +1317,40 @@ public class PhysicsSpace { public static native void initNativePhysics(); /** - * interface with Broadphase types + * Enumerate the available acceleration structures for broadphase collision + * detection. */ public enum BroadphaseType { /** - * basic Broadphase + * btSimpleBroadphase: a brute-force reference implementation for + * debugging purposes */ SIMPLE, /** - * better Broadphase, needs worldBounds , max Object number = 16384 + * btAxisSweep3: uses incremental 3-D sweep and prune, requires world + * bounds, limited to 16_384 objects */ AXIS_SWEEP_3, /** - * better Broadphase, needs worldBounds , max Object number = 65536 + * bt32BitAxisSweep3: uses incremental 3-D sweep and prune, requires + * world bounds, limited to 65_536 objects */ AXIS_SWEEP_3_32, /** - * Broadphase allowing quicker adding/removing of physics objects + * btDbvtBroadphase: uses a fast, dynamic bounding-volume hierarchy + * based on AABB tree to allow quicker addition/removal of physics + * objects */ DBVT; } + /** + * Finalize this physics space just before it is destroyed. Should be + * invoked only by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEvent.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEvent.java index 0022ebd1c..2c81bee35 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEvent.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,21 +36,53 @@ import com.jme3.scene.Spatial; import java.util.EventObject; /** - * A CollisionEvent stores all information about a collision in the PhysicsWorld. - * Do not store this Object, as it will be reused after the collision() method has been called. - * Get/reference all data you need in the collide method. + * An event that describes a collision in the physics world. + *

+ * Do not retain this object, as it will be reused after the collision() method + * returns. Copy any data you need during the collide() method. + * * @author normenhansen */ public class PhysicsCollisionEvent extends EventObject { + /** + * type value to indicate a new event + */ public static final int TYPE_ADDED = 0; + /** + * type value to indicate an event that has been added to a PhysicsSpace + * queue + */ public static final int TYPE_PROCESSED = 1; + /** + * type value to indicate a cleaned/destroyed event + */ public static final int TYPE_DESTROYED = 2; + /** + * type value that indicates the event's status + */ private int type; + /** + * 1st involved object + */ private PhysicsCollisionObject nodeA; + /** + * 2nd involved object + */ private PhysicsCollisionObject nodeB; + /** + * Bullet identifier of the btManifoldPoint + */ private long manifoldPointObjectId = 0; + /** + * Instantiate a collision event. + * + * @param type event type (0=added/1=processed/2=destroyed) + * @param nodeA 1st involved object (alias created) + * @param nodeB 2nd involved object (alias created) + * @param manifoldPointObjectId Bullet identifier of the btManifoldPoint + */ public PhysicsCollisionEvent(int type, PhysicsCollisionObject nodeA, PhysicsCollisionObject nodeB, long manifoldPointObjectId) { super(nodeA); this.type = type; @@ -58,9 +90,9 @@ public class PhysicsCollisionEvent extends EventObject { this.nodeB = nodeB; this.manifoldPointObjectId = manifoldPointObjectId; } - + /** - * used by event factory, called when event is destroyed + * Destroy this event. */ public void clean() { source = null; @@ -71,7 +103,12 @@ public class PhysicsCollisionEvent extends EventObject { } /** - * used by event factory, called when event reused + * Reuse this event. + * + * @param type event type (added/processed/destroyed) + * @param source 1st involved object (alias created) + * @param nodeB 2nd involved object (alias created) + * @param manifoldPointObjectId Bullet identifier */ public void refactor(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, long manifoldPointObjectId) { this.source = source; @@ -81,12 +118,19 @@ public class PhysicsCollisionEvent extends EventObject { this.manifoldPointObjectId = manifoldPointObjectId; } + /** + * Read the type of event. + * + * @return added/processed/destroyed + */ public int getType() { return type; } /** - * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial + * Access the user object of collision object A, provided it's a Spatial. + * + * @return the pre-existing Spatial, or null if none */ public Spatial getNodeA() { if (nodeA.getUserObject() instanceof Spatial) { @@ -96,7 +140,9 @@ public class PhysicsCollisionEvent extends EventObject { } /** - * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial + * Access the user object of collision object B, provided it's a Spatial. + * + * @return the pre-existing Spatial, or null if none */ public Spatial getNodeB() { if (nodeB.getUserObject() instanceof Spatial) { @@ -105,139 +151,286 @@ public class PhysicsCollisionEvent extends EventObject { return null; } + /** + * Access collision object A. + * + * @return the pre-existing object (not null) + */ public PhysicsCollisionObject getObjectA() { return nodeA; } + /** + * Access collision object B. + * + * @return the pre-existing object (not null) + */ public PhysicsCollisionObject getObjectB() { return nodeB; } + /** + * Read the collision's applied impulse. + * + * @return impulse + */ public float getAppliedImpulse() { return getAppliedImpulse(manifoldPointObjectId); } private native float getAppliedImpulse(long manifoldPointObjectId); + /** + * Read the collision's applied lateral impulse #1. + * + * @return impulse + */ public float getAppliedImpulseLateral1() { return getAppliedImpulseLateral1(manifoldPointObjectId); } private native float getAppliedImpulseLateral1(long manifoldPointObjectId); + /** + * Read the collision's applied lateral impulse #2. + * + * @return impulse + */ public float getAppliedImpulseLateral2() { return getAppliedImpulseLateral2(manifoldPointObjectId); } private native float getAppliedImpulseLateral2(long manifoldPointObjectId); + /** + * Read the collision's combined friction. + * + * @return friction + */ public float getCombinedFriction() { return getCombinedFriction(manifoldPointObjectId); } private native float getCombinedFriction(long manifoldPointObjectId); + /** + * Read the collision's combined restitution. + * + * @return restitution + */ public float getCombinedRestitution() { return getCombinedRestitution(manifoldPointObjectId); } private native float getCombinedRestitution(long manifoldPointObjectId); + /** + * Read the collision's distance #1. + * + * @return distance + */ public float getDistance1() { return getDistance1(manifoldPointObjectId); } private native float getDistance1(long manifoldPointObjectId); + /** + * Read the collision's index 0. + * + * @return index + */ public int getIndex0() { return getIndex0(manifoldPointObjectId); } private native int getIndex0(long manifoldPointObjectId); + /** + * Read the collision's index 1. + * + * @return index + */ public int getIndex1() { return getIndex1(manifoldPointObjectId); } private native int getIndex1(long manifoldPointObjectId); + /** + * Copy the collision's lateral friction direction #1. + * + * @return a new vector (not null) + */ public Vector3f getLateralFrictionDir1() { return getLateralFrictionDir1(new Vector3f()); } + /** + * Copy the collision's lateral friction direction #1. + * + * @param lateralFrictionDir1 storage for the result (not null, modified) + * @return direction vector (not null) + */ public Vector3f getLateralFrictionDir1(Vector3f lateralFrictionDir1) { getLateralFrictionDir1(manifoldPointObjectId, lateralFrictionDir1); return lateralFrictionDir1; } private native void getLateralFrictionDir1(long manifoldPointObjectId, Vector3f lateralFrictionDir1); + /** + * Copy the collision's lateral friction direction #2. + * + * @return a new vector + */ public Vector3f getLateralFrictionDir2() { return getLateralFrictionDir2(new Vector3f()); } + /** + * Copy the collision's lateral friction direction #2. + * + * @param lateralFrictionDir2 storage for the result (not null, modified) + * @return direction vector (not null) + */ public Vector3f getLateralFrictionDir2(Vector3f lateralFrictionDir2) { getLateralFrictionDir2(manifoldPointObjectId, lateralFrictionDir2); return lateralFrictionDir2; } private native void getLateralFrictionDir2(long manifoldPointObjectId, Vector3f lateralFrictionDir2); + /** + * Test whether the collision's lateral friction is initialized. + * + * @return true if initialized, otherwise false + */ public boolean isLateralFrictionInitialized() { return isLateralFrictionInitialized(manifoldPointObjectId); } private native boolean isLateralFrictionInitialized(long manifoldPointObjectId); + /** + * Read the collision's lifetime. + * + * @return lifetime + */ public int getLifeTime() { return getLifeTime(manifoldPointObjectId); } private native int getLifeTime(long manifoldPointObjectId); + /** + * Copy the collision's location in the local coordinates of object A. + * + * @return a new location vector (in local coordinates, not null) + */ public Vector3f getLocalPointA() { return getLocalPointA(new Vector3f()); } + /** + * Copy the collision's location in the local coordinates of object A. + * + * @param localPointA storage for the result (not null, modified) + * @return a location vector (in local coordinates, not null) + */ public Vector3f getLocalPointA(Vector3f localPointA) { getLocalPointA(manifoldPointObjectId, localPointA); return localPointA; } private native void getLocalPointA(long manifoldPointObjectId, Vector3f localPointA); + /** + * Copy the collision's location in the local coordinates of object B. + * + * @return a new location vector (in local coordinates, not null) + */ public Vector3f getLocalPointB() { return getLocalPointB(new Vector3f()); } + /** + * Copy the collision's location in the local coordinates of object B. + * + * @param localPointB storage for the result (not null, modified) + * @return a location vector (in local coordinates, not null) + */ public Vector3f getLocalPointB(Vector3f localPointB) { getLocalPointB(manifoldPointObjectId, localPointB); return localPointB; } private native void getLocalPointB(long manifoldPointObjectId, Vector3f localPointB); + /** + * Copy the collision's normal on object B. + * + * @return a new normal vector (in physics-space coordinates, not null) + */ public Vector3f getNormalWorldOnB() { return getNormalWorldOnB(new Vector3f()); } + /** + * Copy the collision's normal on object B. + * + * @param normalWorldOnB storage for the result (not null, modified) + * @return a normal vector (in physics-space coordinates, not null) + */ public Vector3f getNormalWorldOnB(Vector3f normalWorldOnB) { getNormalWorldOnB(manifoldPointObjectId, normalWorldOnB); return normalWorldOnB; } private native void getNormalWorldOnB(long manifoldPointObjectId, Vector3f normalWorldOnB); + /** + * Read part identifier 0. + * + * @return identifier + */ public int getPartId0() { return getPartId0(manifoldPointObjectId); } private native int getPartId0(long manifoldPointObjectId); + /** + * Read part identifier 1. + * + * @return identifier + */ public int getPartId1() { return getPartId1(manifoldPointObjectId); } private native int getPartId1(long manifoldPointObjectId); + /** + * Copy the collision's location. + * + * @return a new vector (in physics-space coordinates, not null) + */ public Vector3f getPositionWorldOnA() { return getPositionWorldOnA(new Vector3f()); } + /** + * Copy the collision's location. + * + * @param positionWorldOnA storage for the result (not null, modified) + * @return a location vector (in physics-space coordinates, not null) + */ public Vector3f getPositionWorldOnA(Vector3f positionWorldOnA) { getPositionWorldOnA(manifoldPointObjectId, positionWorldOnA); return positionWorldOnA; } private native void getPositionWorldOnA(long manifoldPointObjectId, Vector3f positionWorldOnA); + /** + * Copy the collision's location. + * + * @return a new location vector (in physics-space coordinates, not null) + */ public Vector3f getPositionWorldOnB() { return getPositionWorldOnB(new Vector3f()); } + /** + * Copy the collision's location. + * + * @param positionWorldOnB storage for the result (not null, modified) + * @return a location vector (in physics-space coordinates, not null) + */ public Vector3f getPositionWorldOnB(Vector3f positionWorldOnB) { getPositionWorldOnB(manifoldPointObjectId, positionWorldOnB); return positionWorldOnB; diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java index 8c328a15f..d8eb11879 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java @@ -41,6 +41,11 @@ public class PhysicsCollisionEventFactory { private ConcurrentLinkedQueue eventBuffer = new ConcurrentLinkedQueue(); + /** + * Obtain an unused event. + * + * @return an event (not null) + */ public PhysicsCollisionEvent getEvent(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, long manifoldPointObjectId) { PhysicsCollisionEvent event = eventBuffer.poll(); if (event == null) { @@ -51,6 +56,11 @@ public class PhysicsCollisionEventFactory { return event; } + /** + * Recycle the specified event. + * + * @param event + */ public void recycle(PhysicsCollisionEvent event) { event.clean(); eventBuffer.add(event); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java index 7e268e56b..9c9a1338f 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsCollisionObject.java @@ -38,67 +38,147 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Base class for collision objects (PhysicsRigidBody, PhysicsGhostObject) + * The abstract base class for collision objects based on Bullet's + * btCollisionObject. + *

+ * Collision objects include PhysicsCharacter, PhysicsRigidBody, and + * PhysicsGhostObject. + * * @author normenhansen */ public abstract class PhysicsCollisionObject implements Savable { + /** + * Unique identifier of the btCollisionObject. Constructors are responsible + * for setting this to a non-zero value. The id might change if the object + * gets rebuilt. + */ protected long objectId = 0; + /** + * shape associated with this object (not null) + */ protected CollisionShape collisionShape; + /** + * collideWithGroups bitmask that represents "no groups" + */ public static final int COLLISION_GROUP_NONE = 0x00000000; + /** + * collisionGroup/collideWithGroups bitmask that represents group #1 + */ public static final int COLLISION_GROUP_01 = 0x00000001; + /** + * collisionGroup/collideWithGroups bitmask that represents group #2 + */ public static final int COLLISION_GROUP_02 = 0x00000002; + /** + * collisionGroup/collideWithGroups bitmask that represents group #3 + */ public static final int COLLISION_GROUP_03 = 0x00000004; + /** + * collisionGroup/collideWithGroups bitmask that represents group #4 + */ public static final int COLLISION_GROUP_04 = 0x00000008; + /** + * collisionGroup/collideWithGroups bitmask that represents group #5 + */ public static final int COLLISION_GROUP_05 = 0x00000010; + /** + * collisionGroup/collideWithGroups bitmask that represents group #6 + */ public static final int COLLISION_GROUP_06 = 0x00000020; + /** + * collisionGroup/collideWithGroups bitmask that represents group #7 + */ public static final int COLLISION_GROUP_07 = 0x00000040; + /** + * collisionGroup/collideWithGroups bitmask that represents group #8 + */ public static final int COLLISION_GROUP_08 = 0x00000080; + /** + * collisionGroup/collideWithGroups bitmask that represents group #9 + */ public static final int COLLISION_GROUP_09 = 0x00000100; + /** + * collisionGroup/collideWithGroups bitmask that represents group #10 + */ public static final int COLLISION_GROUP_10 = 0x00000200; + /** + * collisionGroup/collideWithGroups bitmask that represents group #11 + */ public static final int COLLISION_GROUP_11 = 0x00000400; + /** + * collisionGroup/collideWithGroups bitmask that represents group #12 + */ public static final int COLLISION_GROUP_12 = 0x00000800; + /** + * collisionGroup/collideWithGroups bitmask that represents group #13 + */ public static final int COLLISION_GROUP_13 = 0x00001000; + /** + * collisionGroup/collideWithGroups bitmask that represents group #14 + */ public static final int COLLISION_GROUP_14 = 0x00002000; + /** + * collisionGroup/collideWithGroups bitmask that represents group #15 + */ public static final int COLLISION_GROUP_15 = 0x00004000; + /** + * collisionGroup/collideWithGroups bitmask that represents group #16 + */ public static final int COLLISION_GROUP_16 = 0x00008000; + /** + * collision group to which this physics object belongs (default=group #1) + */ protected int collisionGroup = 0x00000001; + /** + * collision groups with which this object can collide (default=only group + * #1) + */ protected int collisionGroupsMask = 0x00000001; private Object userObject; /** - * Sets a CollisionShape to this physics object, note that the object should - * not be in the physics space when adding a new collision shape as it is rebuilt - * on the physics side. - * @param collisionShape the CollisionShape to set + * Apply the specified CollisionShape to this object. Note that the object + * should not be in any physics space while changing shape; the object gets + * rebuilt on the physics side. + * + * @param collisionShape the shape to apply (not null, alias created) */ public void setCollisionShape(CollisionShape collisionShape) { this.collisionShape = collisionShape; } /** - * @return the CollisionShape of this PhysicsNode, to be able to reuse it with - * other physics nodes (increases performance) + * Access the shape of this physics object. + * + * @return the pre-existing instance, which can then be applied to other + * physics objects (increases performance) */ public CollisionShape getCollisionShape() { return collisionShape; } /** - * Returns the collision group for this collision shape - * @return The collision group + * Read the collision group for this physics object. + * + * @return the collision group (bit mask with exactly one bit set) */ public int getCollisionGroup() { return collisionGroup; } /** - * Sets the collision group number for this physics object.
- * The groups are integer bit masks and some pre-made variables are available in CollisionObject. - * All physics objects are by default in COLLISION_GROUP_01.
- * Two object will collide when one of the parties has the - * collisionGroup of the other in its collideWithGroups set. - * @param collisionGroup the collisionGroup to set + * Alter the collision group for this physics object. + *

+ * Groups are represented by integer bit masks with exactly 1 bit set. + * Pre-made variables are available in PhysicsCollisionObject. By default, + * physics objects are in COLLISION_GROUP_01. + *

+ * Two objects can collide only if one of them has the collisionGroup of the + * other in its collideWithGroups set. + * + * @param collisionGroup the collisionGroup to apply (bit mask with exactly + * 1 bit set) */ public void setCollisionGroup(int collisionGroup) { this.collisionGroup = collisionGroup; @@ -108,10 +188,12 @@ public abstract class PhysicsCollisionObject implements Savable { } /** - * Add a group that this object will collide with.
- * Two object will collide when one of the parties has the - * collisionGroup of the other in its collideWithGroups set.
- * @param collisionGroup + * Add collision groups to the set with which this object can collide. + * + * Two objects can collide only if one of them has the collisionGroup of the + * other in its collideWithGroups set. + * + * @param collisionGroup groups to add (bit mask) */ public void addCollideWithGroup(int collisionGroup) { this.collisionGroupsMask = this.collisionGroupsMask | collisionGroup; @@ -121,8 +203,9 @@ public abstract class PhysicsCollisionObject implements Savable { } /** - * Remove a group from the list this object collides with. - * @param collisionGroup + * Remove collision groups from the set with which this object can collide. + * + * @param collisionGroup groups to remove, ORed together (bit mask) */ public void removeCollideWithGroup(int collisionGroup) { this.collisionGroupsMask = this.collisionGroupsMask & ~collisionGroup; @@ -132,8 +215,9 @@ public abstract class PhysicsCollisionObject implements Savable { } /** - * Directly set the bitmask for collision groups that this object collides with. - * @param collisionGroups + * Directly alter the collision groups with which this object can collide. + * + * @param collisionGroups desired groups, ORed together (bit mask) */ public void setCollideWithGroups(int collisionGroups) { this.collisionGroupsMask = collisionGroups; @@ -143,13 +227,17 @@ public abstract class PhysicsCollisionObject implements Savable { } /** - * Gets the bitmask of collision groups that this object collides with. - * @return Collision groups mask + * Read the set of collision groups with which this object can collide. + * + * @return bit mask */ public int getCollideWithGroups() { return collisionGroupsMask; } + /** + * Initialize the user pointer and collision-group information of this object. + */ protected void initUserPointer() { Logger.getLogger(this.getClass().getName()).log(Level.FINE, "initUserPointer() objectId = {0}", Long.toHexString(objectId)); initUserPointer(objectId, collisionGroup, collisionGroupsMask); @@ -157,27 +245,51 @@ public abstract class PhysicsCollisionObject implements Savable { native void initUserPointer(long objectId, int group, int groups); /** - * @return the userObject + * Access the user object associated with this collision object. + * + * @return the pre-existing instance, or null if none */ public Object getUserObject() { return userObject; } /** - * @param userObject the userObject to set + * Associate a user object (such as a Spatial) with this collision object. + * + * @param userObject the object to associate with this collision object + * (alias created, may be null) */ public void setUserObject(Object userObject) { this.userObject = userObject; } - public long getObjectId(){ + /** + * Read the id of the btCollisionObject. + * + * @return the unique identifier (not zero) + */ + public long getObjectId(){ return objectId; } + /** + * Attach the identified btCollisionShape to the identified + * btCollisionObject. Native method. + * + * @param objectId the unique identifier of the btCollisionObject (not zero) + * @param collisionShapeId the unique identifier of the btCollisionShape + * (not zero) + */ protected native void attachCollisionShape(long objectId, long collisionShapeId); native void setCollisionGroup(long objectId, int collisionGroup); native void setCollideWithGroups(long objectId, int collisionGroups); + /** + * Serialize this object, for example when saving to a J3O file. + * + * @param e exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter e) throws IOException { OutputCapsule capsule = e.getCapsule(this); @@ -186,6 +298,12 @@ public abstract class PhysicsCollisionObject implements Savable { capsule.write(collisionShape, "collisionShape", null); } + /** + * De-serialize this object, for example when loading from a J3O file. + * + * @param e importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter e) throws IOException { InputCapsule capsule = e.getCapsule(this); @@ -195,6 +313,12 @@ public abstract class PhysicsCollisionObject implements Savable { collisionShape = shape; } + /** + * Finalize this collision object just before it is destroyed. Should be + * invoked only by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); @@ -202,5 +326,10 @@ public abstract class PhysicsCollisionObject implements Savable { finalizeNative(objectId); } + /** + * Finalize the identified btCollisionObject. Native method. + * + * @param objectId the unique identifier of the btCollisionObject (not zero) + */ protected native void finalizeNative(long objectId); } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsRayTestResult.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsRayTestResult.java index bbc231fa7..d00c58df5 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsRayTestResult.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsRayTestResult.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,47 +34,67 @@ package com.jme3.bullet.collision; import com.jme3.math.Vector3f; /** - * Contains the results of a PhysicsSpace rayTest - * bulletAppState.getPhysicsSpace().rayTest(new Vector3f(0,1000,0),new Vector3f(0,-1000,0)); - javap -s java.util.List + * Represent the results of a Bullet ray test. + * * @author Empire-Phoenix,normenhansen */ public class PhysicsRayTestResult { + /** + * collision object that was hit + */ private PhysicsCollisionObject collisionObject; + /** + * normal vector at the point of contact + */ private Vector3f hitNormalLocal; + /** + * fraction of the ray's total length (from=0, to=1, ≥0, ≤1) + */ private float hitFraction; + /** + * true→need to transform normal into world space + */ private boolean normalInWorldSpace = true; /** - * allocated by native code only + * A private constructor to inhibit instantiation of this class by Java. + * These results are instantiated exclusively by native code. */ private PhysicsRayTestResult() { } /** - * @return the collisionObject + * Access the collision object that was hit. + * + * @return the pre-existing instance */ public PhysicsCollisionObject getCollisionObject() { return collisionObject; } /** - * @return the hitNormalLocal + * Access the normal vector at the point of contact. + * + * @return the pre-existing vector (not null) */ public Vector3f getHitNormalLocal() { return hitNormalLocal; } /** - * @return the hitFraction + * Read the fraction of the ray's total length. + * + * @return fraction (from=0, to=1, ≥0, ≤1) */ public float getHitFraction() { return hitFraction; } /** - * @return the normalInWorldSpace + * Test whether the normal is in world space. + * + * @return true if in world space, otherwise false */ public boolean isNormalInWorldSpace() { return normalInWorldSpace; diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsSweepTestResult.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsSweepTestResult.java index 8e61b66a2..7a00f2f49 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsSweepTestResult.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/PhysicsSweepTestResult.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,13 +35,26 @@ import com.jme3.math.Vector3f; /** * Contains the results of a PhysicsSpace rayTest + * * @author normenhansen */ public class PhysicsSweepTestResult { + /** + * collision object that was hit + */ private PhysicsCollisionObject collisionObject; + /** + * normal vector at the point of contact + */ private Vector3f hitNormalLocal; + /** + * fraction of the way between the transforms (from=0, to=1, ≥0, ≤1) + */ private float hitFraction; + /** + * true→need to transform normal into world space + */ private boolean normalInWorldSpace; public PhysicsSweepTestResult() { @@ -55,33 +68,49 @@ public class PhysicsSweepTestResult { } /** - * @return the collisionObject + * Access the collision object that was hit. + * + * @return the pre-existing instance */ public PhysicsCollisionObject getCollisionObject() { return collisionObject; } /** - * @return the hitNormalLocal + * Access the normal vector at the point of contact. + * + * @return the pre-existing vector (not null) */ public Vector3f getHitNormalLocal() { return hitNormalLocal; } /** - * @return the hitFraction + * Read the hit fraction. + * + * @return fraction (from=0, to=1, ≥0, ≤1) */ public float getHitFraction() { return hitFraction; } /** - * @return the normalInWorldSpace + * Test whether the normal is in world space. + * + * @return true if in world space, otherwise false */ public boolean isNormalInWorldSpace() { return normalInWorldSpace; } + /** + * Fill in the fields of this result. + * + * @param collisionObject + * @param hitNormalLocal + * @param hitFraction + * @param normalInWorldSpace + */ public void fill(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) { this.collisionObject = collisionObject; this.hitNormalLocal = hitNormalLocal; diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/BoxCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/BoxCollisionShape.java index 000bb2dc7..a967143c3 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/BoxCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/BoxCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,35 +41,63 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Basic box collision shape + * A rectangular-solid collision shape based on Bullet's btBoxShape. + * * @author normenhansen */ public class BoxCollisionShape extends CollisionShape { + /** + * copy of half-extents of the box on each local axis (not null, no negative + * component) + */ private Vector3f halfExtents; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public BoxCollisionShape() { } /** - * creates a collision box from the given halfExtents - * @param halfExtents the halfExtents of the CollisionBox + * Instantiate a box shape with the specified half extents. + * + * @param halfExtents the desired half extents (not null, no negative + * component, alias created) */ public BoxCollisionShape(Vector3f halfExtents) { this.halfExtents = halfExtents; createShape(); } + /** + * Access the half extents. + * + * @return the pre-existing instance (not null, no negative component) + */ public final Vector3f getHalfExtents() { return halfExtents; } - + + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); capsule.write(halfExtents, "halfExtents", new Vector3f(1, 1, 1)); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -78,6 +106,9 @@ public class BoxCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { objectId = createShape(halfExtents); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java index 00ccdf74e..2aa81f954 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,20 +41,37 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Basic capsule collision shape + * A capsule collision shape based on Bullet's btCapsuleShapeX, btCapsuleShape, + * or btCapsuleShapeZ. + * * @author normenhansen */ public class CapsuleCollisionShape extends CollisionShape{ - protected float radius,height; - protected int axis; + /** + * copy of height of the cylindrical portion (≥0) + */ + private float height; + /** + * copy of radius (≥0) + */ + private float radius; + /** + * copy of main (height) axis (0→X, 1→Y, 2→Z) + */ + private int axis; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public CapsuleCollisionShape() { } /** - * creates a new CapsuleCollisionShape with the given radius and height - * @param radius the radius of the capsule - * @param height the height of the capsule + * Instantiate a Y-axis capsule shape with the specified radius and height. + * + * @param radius the desired radius (≥0) + * @param height the desired height (of the cylindrical portion) (≥0) */ public CapsuleCollisionShape(float radius, float height) { this.radius=radius; @@ -64,10 +81,11 @@ public class CapsuleCollisionShape extends CollisionShape{ } /** - * creates a capsule shape around the given axis (0=X,1=Y,2=Z) - * @param radius - * @param height - * @param axis + * Instantiate a capsule shape around the specified main (height) axis. + * + * @param radius the desired radius (≥0) + * @param height the desired height (of the cylindrical portion) (≥0) + * @param axis which local axis: 0→X, 1→Y, 2→Z */ public CapsuleCollisionShape(float radius, float height, int axis) { this.radius=radius; @@ -76,20 +94,39 @@ public class CapsuleCollisionShape extends CollisionShape{ createShape(); } + /** + * Read the radius of the capsule. + * + * @return radius (≥0) + */ public float getRadius() { return radius; } + /** + * Read the height (of the cylindrical portion) of the capsule. + * + * @return height (≥0) + */ public float getHeight() { return height; } + /** + * Determine the main (height) axis of the capsule. + * + * @return 0 for local X, 1 for local Y, or 2 for local Z + */ public int getAxis() { return axis; } /** - * WARNING - CompoundCollisionShape scaling has no effect. + * Alter the scaling factors of this shape. Scaling is disabled + * for capsule shapes. + * + * @param scale the desired scaling factor for each local axis (not null, no + * negative component, unaffected, default=1,1,1) */ @Override public void setScale(Vector3f scale) { @@ -98,6 +135,12 @@ public class CapsuleCollisionShape extends CollisionShape{ } } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); @@ -106,6 +149,12 @@ public class CapsuleCollisionShape extends CollisionShape{ capsule.write(axis, "axis", 1); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -115,6 +164,9 @@ public class CapsuleCollisionShape extends CollisionShape{ createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape(){ objectId = createShape(axis, radius, height); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java index c5b390e0e..abd46e607 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,15 +38,32 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * This Object holds information about a jbullet CollisionShape to be able to reuse - * CollisionShapes (as suggested in bullet manuals) - * TODO: add static methods to create shapes from nodes (like jbullet-jme constructor) + * The abstract base class for collision shapes based on Bullet's + * btCollisionShape. + *

+ * Collision shapes include BoxCollisionShape and CapsuleCollisionShape. As + * suggested in the Bullet manual, a single collision shape can be shared among + * multiple collision objects. + * * @author normenhansen */ public abstract class CollisionShape implements Savable { + /** + * unique identifier of the Bullet shape + *

+ * Constructors are responsible for setting this to a non-zero value. After + * that, the id never changes. + */ protected long objectId = 0; + /** + * copy of scaling factors: one for each local axis (default=1,1,1) + */ protected Vector3f scale = new Vector3f(1, 1, 1); + /** + * copy of collision margin (in physics-space units, >0, + * default=0) + */ protected float margin = 0.0f; public CollisionShape() { @@ -70,7 +87,9 @@ public abstract class CollisionShape implements Savable { // private native void calculateLocalInertia(long objectId, long shapeId, float mass); /** - * used internally + * Read the id of the Bullet shape. + * + * @return the unique identifier (not zero) */ public long getObjectId() { return objectId; @@ -83,21 +102,52 @@ public abstract class CollisionShape implements Savable { this.objectId = id; } + /** + * Alter the scaling factors of this shape. CAUTION: Not all shapes can be + * scaled. + *

+ * Note that if the shape is shared (between collision objects and/or + * compound shapes) changes can have unintended consequences. + * + * @param scale the desired scaling factor for each local axis (not null, no + * negative component, unaffected, default=1,1,1) + */ public void setScale(Vector3f scale) { this.scale.set(scale); setLocalScaling(objectId, scale); } - + /** + * Access the scaling factors. + * + * @return the pre-existing vector (not null) + */ public Vector3f getScale() { return scale; } + /** + * Read the collision margin for this shape. + * + * @return the margin distance (in physics-space units, >0) + */ public float getMargin() { return getMargin(objectId); } private native float getMargin(long objectId); + /** + * Alter the collision margin for this shape. CAUTION: Margin is applied + * differently, depending on the type of shape. Generally the collision + * margin expands the object, creating a gap. Don't set the collision margin + * to zero. + *

+ * Note that if the shape is shared (between collision objects and/or + * compound shapes) changes can have unintended consequences. + * + * @param margin the desired margin distance (in physics-space units, >0, + * default=0) + */ public void setMargin(float margin) { setMargin(objectId, margin); this.margin = margin; @@ -107,18 +157,37 @@ public abstract class CollisionShape implements Savable { private native void setMargin(long objectId, float margin); + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { OutputCapsule capsule = ex.getCapsule(this); capsule.write(scale, "scale", new Vector3f(1, 1, 1)); capsule.write(getMargin(), "margin", 0.0f); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ + @Override public void read(JmeImporter im) throws IOException { InputCapsule capsule = im.getCapsule(this); this.scale = (Vector3f) capsule.readSavable("scale", new Vector3f(1, 1, 1)); this.margin = capsule.readFloat("margin", 0.0f); } + /** + * Finalize this shape just before it is destroyed. Should be invoked only + * by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java index b2321afdc..254006676 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,23 +46,33 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * A CompoundCollisionShape allows combining multiple base shapes - * to generate a more sophisticated shape. + * A collision shape formed by combining convex child shapes, based on Bullet's + * btCompoundShape. + * * @author normenhansen */ public class CompoundCollisionShape extends CollisionShape { + /** + * children of this shape + */ protected ArrayList children = new ArrayList(); + /** + * Instantiate an empty compound shape (with no children). + */ public CompoundCollisionShape() { objectId = createShape();//new CompoundShape(); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); } /** - * adds a child shape at the given local translation - * @param shape the child shape to add - * @param location the local location of the child shape + * Add a child shape with the specified local translation. + * + * @param shape the child shape to add (not null, not a compound shape, + * alias created) + * @param location the local coordinates of the child shape's center (not + * null, unaffected) */ public void addChildShape(CollisionShape shape, Vector3f location) { // Transform transA = new Transform(Converter.convert(new Matrix3f())); @@ -73,9 +83,14 @@ public class CompoundCollisionShape extends CollisionShape { } /** - * adds a child shape at the given local translation - * @param shape the child shape to add - * @param location the local location of the child shape + * Add a child shape with the specified local translation and orientation. + * + * @param shape the child shape to add (not null, not a compound shape, + * alias created) + * @param location the local coordinates of the child shape's center (not + * null, unaffected) + * @param rotation the local orientation of the child shape (not null, + * unaffected) */ public void addChildShape(CollisionShape shape, Vector3f location, Matrix3f rotation) { if(shape instanceof CompoundCollisionShape){ @@ -101,8 +116,9 @@ public class CompoundCollisionShape extends CollisionShape { } /** - * removes a child shape - * @param shape the child shape to remove + * Remove a child from this shape. + * + * @param shape the child shape to remove (not null) */ public void removeChildShape(CollisionShape shape) { removeChildShape(objectId, shape.getObjectId()); @@ -115,6 +131,11 @@ public class CompoundCollisionShape extends CollisionShape { } } + /** + * Access the list of children. + * + * @return the pre-existing list (not null) + */ public List getChildren() { return children; } @@ -125,12 +146,24 @@ public class CompoundCollisionShape extends CollisionShape { private native long removeChildShape(long objectId, long childId); + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); capsule.writeSavableArrayList(children, "children", new ArrayList()); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/ConeCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/ConeCollisionShape.java index 3dd317624..c111fbd9e 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/ConeCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/ConeCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,18 +41,40 @@ import java.util.logging.Level; import java.util.logging.Logger; /** + * A conical collision shape based on Bullet's btConeShapeX, btConeShape, or + * btConeShapeZ. * * @author normenhansen */ public class ConeCollisionShape extends CollisionShape { + /** + * copy of radius (≥0) + */ protected float radius; + /** + * copy of height (≥0) + */ protected float height; + /** + * copy of main (height) axis (0→X, 1→Y, 2→Z) + */ protected int axis; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public ConeCollisionShape() { } + /** + * Instantiate a cone shape around the specified main (height) axis. + * + * @param radius the desired radius (≥0) + * @param height the desired height (≥0) + * @param axis which local axis: 0→X, 1→Y, 2→Z + */ public ConeCollisionShape(float radius, float height, int axis) { this.radius = radius; this.height = height; @@ -60,6 +82,12 @@ public class ConeCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate a cone shape oriented along the Y axis. + * + * @param radius the desired radius (≥0) + * @param height the desired height (≥0) + */ public ConeCollisionShape(float radius, float height) { this.radius = radius; this.height = height; @@ -67,14 +95,30 @@ public class ConeCollisionShape extends CollisionShape { createShape(); } + /** + * Read the radius of the cone. + * + * @return radius (≥0) + */ public float getRadius() { return radius; } - + + /** + * Read the height of the cone. + * + * @return height (≥0) + */ public float getHeight() { return height; } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); @@ -83,6 +127,12 @@ public class ConeCollisionShape extends CollisionShape { capsule.write(axis, "axis", PhysicsSpace.AXIS_Y); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -92,6 +142,9 @@ public class ConeCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { objectId = createShape(axis, radius, height); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java index 4fe48f9b7..807516fff 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,20 +41,35 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Basic cylinder collision shape + * A cylindrical collision shape based on Bullet's btCylinderShapeX, new + * btCylinderShape, or btCylinderShapeZ. + * * @author normenhansen */ public class CylinderCollisionShape extends CollisionShape { + /** + * copy of half-extents of the cylinder on each local axis (not null, no + * negative component) + */ protected Vector3f halfExtents; + /** + * main (height) axis (0→X, 1→Y, 2→Z) + */ protected int axis; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public CylinderCollisionShape() { } /** - * creates a cylinder shape from the given halfextents - * @param halfExtents the halfextents to use + * Instantiate a Z-axis cylinder shape with the specified half extents. + * + * @param halfExtents the desired half extents (not null, no negative + * component, alias created) */ public CylinderCollisionShape(Vector3f halfExtents) { this.halfExtents = halfExtents; @@ -63,9 +78,11 @@ public class CylinderCollisionShape extends CollisionShape { } /** - * Creates a cylinder shape around the given axis from the given halfextents - * @param halfExtents the halfextents to use - * @param axis (0=X,1=Y,2=Z) + * Instantiate a cylinder shape around the specified axis. + * + * @param halfExtents the desired half extents (not null, no negative + * component, alias created) + * @param axis which local axis: 0→X, 1→Y, 2→Z */ public CylinderCollisionShape(Vector3f halfExtents, int axis) { this.halfExtents = halfExtents; @@ -73,16 +90,30 @@ public class CylinderCollisionShape extends CollisionShape { createShape(); } + /** + * Access the half extents of the cylinder. + * + * @return the pre-existing vector (not null, no negative component) + */ public final Vector3f getHalfExtents() { return halfExtents; } + /** + * Determine the main axis of the cylinder. + * + * @return 0→X, 1→Y, 2→Z + */ public int getAxis() { return axis; } /** - * WARNING - CompoundCollisionShape scaling has no effect. + * Alter the scaling factors of this shape. Scaling is disabled + * for cylinder shapes. + * + * @param scale the desired scaling factor for each local axis (not null, no + * negative component, unaffected, default=1,1,1) */ @Override public void setScale(Vector3f scale) { @@ -91,6 +122,12 @@ public class CylinderCollisionShape extends CollisionShape { } } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); @@ -98,6 +135,12 @@ public class CylinderCollisionShape extends CollisionShape { capsule.write(axis, "axis", 1); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -106,6 +149,9 @@ public class CylinderCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { objectId = createShape(axis, halfExtents); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java index 200e6dc3b..0f9cfc425 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,8 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Basic mesh collision shape + * A mesh collision shape based on Bullet's btGImpactMeshShape. + * * @author normenhansen */ public class GImpactCollisionShape extends CollisionShape { @@ -55,14 +56,23 @@ public class GImpactCollisionShape extends CollisionShape { // protected Vector3f worldScale; protected int numVertices, numTriangles, vertexStride, triangleIndexStride; protected ByteBuffer triangleIndexBase, vertexBase; + /** + * Unique identifier of the Bullet mesh. The constructor sets this to a + * non-zero value. + */ protected long meshId = 0; // protected IndexedMesh bulletMesh; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public GImpactCollisionShape() { } /** - * creates a collision shape from the given Mesh + * Instantiate a shape based on the specified JME mesh. + * * @param mesh the Mesh to use */ public GImpactCollisionShape(Mesh mesh) { @@ -105,6 +115,12 @@ public class GImpactCollisionShape extends CollisionShape { // public Mesh createJmeMesh() { // return Converter.convert(bulletMesh); // } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); @@ -118,6 +134,12 @@ public class GImpactCollisionShape extends CollisionShape { capsule.write(vertexBase.array(), "vertexBase", new byte[0]); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -132,6 +154,9 @@ public class GImpactCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { // bulletMesh = new IndexedMesh(); // bulletMesh.numVertices = numVertices; @@ -157,6 +182,12 @@ public class GImpactCollisionShape extends CollisionShape { private native long createShape(long meshId); + /** + * Finalize this shape just before it is destroyed. Should be invoked only + * by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java index e525d3f1c..43f764fb5 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -45,36 +45,72 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Uses Bullet Physics Heightfield terrain collision system. This is MUCH faster - * than using a regular mesh. - * There are a couple tricks though: - * -No rotation or translation is supported. - * -The collision bbox must be centered around 0,0,0 with the height above and below the y-axis being - * equal on either side. If not, the whole collision box is shifted vertically and things don't collide - * as they should. - * + * A terrain collision shape based on Bullet's btHeightfieldTerrainShape. + *

+ * This is much more efficient than a regular mesh, but it has a couple + * limitations: + *

    + *
  • No rotation or translation.
  • + *
  • The collision bounding box must be centered on (0,0,0) with the height + * above and below the X-Z plane being equal on either side. If not, the whole + * collision box is shifted vertically and objects won't collide properly.
  • + *
+ * * @author Brent Owens */ public class HeightfieldCollisionShape extends CollisionShape { + /** + * number of rows in the heightfield (>1) + */ protected int heightStickWidth; + /** + * number of columns in the heightfield (>1) + */ protected int heightStickLength; + /** + * array of heightfield samples + */ protected float[] heightfieldData; protected float heightScale; protected float minHeight; protected float maxHeight; + /** + * index of the height axis (0→X, 1→Y, 2→Z) + */ protected int upAxis; protected boolean flipQuadEdges; + /** + * buffer for passing height data to Bullet + *

+ * A Java reference must persist after createShape() completes, or else the + * buffer might get garbaged collected. + */ protected ByteBuffer bbuf; // protected FloatBuffer fbuf; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public HeightfieldCollisionShape() { } + /** + * Instantiate a new shape for the specified height map. + * + * @param heightmap (not null, length≥4, length a perfect square) + */ public HeightfieldCollisionShape(float[] heightmap) { createCollisionHeightfield(heightmap, Vector3f.UNIT_XYZ); } + /** + * Instantiate a new shape for the specified height map and scale vector. + * + * @param heightmap (not null, length≥4, length a perfect square) + * @param scale (not null, no negative component, unaffected, default=1,1,1) + */ public HeightfieldCollisionShape(float[] heightmap, Vector3f scale) { createCollisionHeightfield(heightmap, scale); } @@ -120,6 +156,9 @@ public class HeightfieldCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { bbuf = BufferUtils.createByteBuffer(heightfieldData.length * 4); // fbuf = bbuf.asFloatBuffer();//FloatBuffer.wrap(heightfieldData); @@ -138,11 +177,22 @@ public class HeightfieldCollisionShape extends CollisionShape { private native long createShape(int heightStickWidth, int heightStickLength, ByteBuffer heightfieldData, float heightScale, float minHeight, float maxHeight, int upAxis, boolean flipQuadEdges); + /** + * Does nothing. + * + * @return null + */ public Mesh createJmeMesh() { //TODO return Converter.convert(bulletMesh); return null; } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); @@ -156,6 +206,12 @@ public class HeightfieldCollisionShape extends CollisionShape { capsule.write(flipQuadEdges, "flipQuadEdges", false); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java index db40eedac..527024fa9 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/HullCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,24 +44,50 @@ import java.nio.FloatBuffer; import java.util.logging.Level; import java.util.logging.Logger; +/** + * A convex hull collision shape based on Bullet's btConvexHullShape. + */ public class HullCollisionShape extends CollisionShape { private float[] points; // protected FloatBuffer fbuf; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public HullCollisionShape() { } + /** + * Instantiate a collision shape based on the specified JME mesh. For best + * performance and stability, use the mesh should have no more than 100 + * vertices. + * + * @param mesh a mesh on which to base the shape (not null) + */ public HullCollisionShape(Mesh mesh) { this.points = getPoints(mesh); createShape(); } + /** + * Instantiate a collision shape based on the specified JME mesh. + * + * @param points an array of coordinates on which to base the shape (not + * null, length a multiple of 3) + */ public HullCollisionShape(float[] points) { this.points = points; createShape(); } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -70,6 +96,12 @@ public class HullCollisionShape extends CollisionShape { capsule.write(points, "points", null); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); @@ -89,6 +121,9 @@ public class HullCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { // ObjectArrayList pointList = new ObjectArrayList(); // for (int i = 0; i < points.length; i += 3) { @@ -114,6 +149,12 @@ public class HullCollisionShape extends CollisionShape { private native long createShape(ByteBuffer points); + /** + * Copy the vertex positions from a JME mesh. + * + * @param mesh the mesh to read (not null) + * @return a new array (not null, length a multiple of 3) + */ protected float[] getPoints(Mesh mesh) { FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); vertices.rewind(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java index 3c5dbbb49..dead3294c 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/MeshCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -49,7 +49,7 @@ import com.jme3.scene.mesh.IndexBuffer; import com.jme3.util.BufferUtils; /** - * Basic mesh collision shape + * A mesh collision shape based on Bullet's btBvhTriangleMeshShape. * * @author normenhansen */ @@ -64,38 +64,45 @@ public class MeshCollisionShape extends CollisionShape { private static final String NATIVE_BVH = "nativeBvh"; protected int numVertices, numTriangles, vertexStride, triangleIndexStride; protected ByteBuffer triangleIndexBase, vertexBase; + /** + * Unique identifier of the Bullet mesh. The constructor sets this to a + * non-zero value. + */ protected long meshId = 0; protected long nativeBVHBuffer = 0; private boolean memoryOptimized; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public MeshCollisionShape() { } /** - * Creates a collision shape from the given Mesh. - * Default behavior, more optimized for memory usage. + * Instantiate a collision shape based on the specified JME mesh, optimized + * for memory usage. * - * @param mesh + * @param mesh the mesh on which to base the shape (not null) */ public MeshCollisionShape(Mesh mesh) { this(mesh, true); } /** - * Creates a collision shape from the given Mesh. - * memoryOptimized determines if optimized instead of - * quantized BVH will be used. - * Internally, memoryOptimized BVH is slower to calculate (~4x) - * but also smaller (~0.5x). - * It is preferable to use the memory optimized version and then serialize - * the resulting MeshCollisionshape as this will also save the - * generated BVH. - * An exception can be procedurally / generated collision shapes, where - * the generation time is more of a concern + * Instantiate a collision shape based on the specified JME mesh. + *

+ * memoryOptimized determines if optimized instead of quantized + * BVH will be used. Internally, memoryOptimized BVH is slower + * to calculate (~4x) but also smaller (~0.5x). It is preferable to use the + * memory optimized version and then serialize the resulting + * MeshCollisionshape as this will also save the generated BVH. An exception + * can be procedurally / generated collision shapes, where the generation + * time is more of a concern * - * @param mesh the Mesh to use - * @param memoryOptimized True to generate a memory optimized BVH, - * false to generate quantized BVH. + * @param mesh the mesh on which to base the shape (not null) + * @param memoryOptimized true to generate a memory-optimized BVH, false to + * generate quantized BVH */ public MeshCollisionShape(final Mesh mesh, final boolean memoryOptimized) { this.memoryOptimized = memoryOptimized; @@ -103,16 +110,15 @@ public class MeshCollisionShape extends CollisionShape { } /** - * Advanced constructor, usually you don’t want to use this, but the Mesh - * based one. Passing false values can lead to a crash, use at own risk - * + * An advanced constructor. Passing false values can lead to a crash. + * Usually you don’t want to use this. Use at own risk. + *

* This constructor bypasses all copy logic normally used, this allows for - * faster bullet shape generation when using procedurally generated Meshes. - * + * faster Bullet shape generation when using procedurally generated Meshes. * * @param indices the raw index buffer * @param vertices the raw vertex buffer - * @param memoryOptimized use quantisize BVH, uses less memory, but slower + * @param memoryOptimized use quantized BVH, uses less memory, but slower */ public MeshCollisionShape(ByteBuffer indices, ByteBuffer vertices, boolean memoryOptimized) { this.triangleIndexBase = indices; @@ -153,6 +159,12 @@ public class MeshCollisionShape extends CollisionShape { this.createShape(null); } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(final JmeExporter ex) throws IOException { super.write(ex); @@ -178,6 +190,12 @@ public class MeshCollisionShape extends CollisionShape { } } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(final JmeImporter im) throws IOException { super.read(im); @@ -195,6 +213,9 @@ public class MeshCollisionShape extends CollisionShape { createShape(nativeBvh); } + /** + * Instantiate the configured shape in Bullet. + */ private void createShape(byte bvh[]) { boolean buildBvh=bvh==null||bvh.length==0; this.meshId = NativeMeshUtil.createTriangleIndexVertexArray(this.triangleIndexBase, this.vertexBase, this.numTriangles, this.numVertices, this.vertexStride, this.triangleIndexStride); @@ -216,6 +237,12 @@ public class MeshCollisionShape extends CollisionShape { private native long createShape(boolean memoryOptimized, boolean buildBvt, long meshId); + /** + * Finalize this shape just before it is destroyed. Should be invoked only + * by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override public void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java index caf654899..6b8bb88bb 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,34 +42,60 @@ import java.util.logging.Level; import java.util.logging.Logger; /** + * A planar collision shape based on Bullet's btStaticPlaneShape. * * @author normenhansen */ public class PlaneCollisionShape extends CollisionShape{ + /** + * description of the plane + */ private Plane plane; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public PlaneCollisionShape() { } /** - * Creates a plane Collision shape - * @param plane the plane that defines the shape + * Instantiate a plane shape defined by the specified plane. + * + * @param plane the desired plane (not null, alias created) */ public PlaneCollisionShape(Plane plane) { this.plane = plane; createShape(); } + /** + * Access the defining plane. + * + * @return the pre-existing instance (not null) + */ public final Plane getPlane() { return plane; } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); capsule.write(plane, "collisionPlane", new Plane()); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -77,6 +103,9 @@ public class PlaneCollisionShape extends CollisionShape{ createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { objectId = createShape(plane.getNormal(), plane.getConstant()); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java index fff1c7058..6a538a1c0 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,16 +41,33 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * A simple point, line, triangle or quad collisionShape based on one to four points- + * A simple point, line-segment, triangle, or tetrahedron collision shape based + * on Bullet's btBU_Simplex1to4. + * * @author normenhansen */ public class SimplexCollisionShape extends CollisionShape { + /** + * vertex positions + */ private Vector3f vector1, vector2, vector3, vector4; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public SimplexCollisionShape() { } + /** + * Instantiate a tetrahedral collision shape based on the specified points. + * + * @param point1 the coordinates of 1st point (not null, alias created) + * @param point2 the coordinates of 2nd point (not null, alias created) + * @param point3 the coordinates of 3rd point (not null, alias created) + * @param point4 the coordinates of 4th point (not null, alias created) + */ public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3, Vector3f point4) { vector1 = point1; vector2 = point2; @@ -59,6 +76,13 @@ public class SimplexCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate a triangular collision shape based on the specified points. + * + * @param point1 the coordinates of 1st point (not null, alias created) + * @param point2 the coordinates of 2nd point (not null, alias created) + * @param point3 the coordinates of 3rd point (not null, alias created) + */ public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3) { vector1 = point1; vector2 = point2; @@ -66,17 +90,34 @@ public class SimplexCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate a line-segment collision shape based on the specified points. + * + * @param point1 the coordinates of 1st point (not null, alias created) + * @param point2 the coordinates of 2nd point (not null, alias created) + */ public SimplexCollisionShape(Vector3f point1, Vector3f point2) { vector1 = point1; vector2 = point2; createShape(); } + /** + * Instantiate a point collision shape based on the specified points. + * + * @param point1 the coordinates of point (not null, alias created) + */ public SimplexCollisionShape(Vector3f point1) { vector1 = point1; createShape(); } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); @@ -86,6 +127,12 @@ public class SimplexCollisionShape extends CollisionShape { capsule.write(vector4, "simplexPoint4", null); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -96,6 +143,9 @@ public class SimplexCollisionShape extends CollisionShape { createShape(); } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { if (vector4 != null) { objectId = createShape(vector1, vector2, vector3, vector4); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SphereCollisionShape.java b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SphereCollisionShape.java index 4ba6152c9..1f0ab7e29 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SphereCollisionShape.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/collision/shapes/SphereCollisionShape.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,35 +41,61 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Basic sphere collision shape + * A spherical collision shape based on Bullet's btSphereShape. + * * @author normenhansen */ public class SphereCollisionShape extends CollisionShape { + /** + * copy of radius (≥0) + */ protected float radius; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public SphereCollisionShape() { } /** - * creates a SphereCollisionShape with the given radius - * @param radius + * Instantiate a sphere shape with the specified radius. + * + * @param radius the desired radius (≥0) */ public SphereCollisionShape(float radius) { this.radius = radius; createShape(); } + /** + * Read the radius of this shape. + * + * @return the radius (≥0) + */ public float getRadius() { return radius; } + /** + * Serialize this shape, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); capsule.write(radius, "radius", 0.5f); } + /** + * De-serialize this shape, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -78,7 +104,11 @@ public class SphereCollisionShape extends CollisionShape { } /** - * WARNING - CompoundCollisionShape scaling has no effect. + * Alter the scaling factors of this shape. Scaling is disabled + * for sphere shapes. + * + * @param scale the desired scaling factor for each local axis (not null, no + * negative component, unaffected, default=1,1,1) */ @Override public void setScale(Vector3f scale) { @@ -87,6 +117,9 @@ public class SphereCollisionShape extends CollisionShape { } } + /** + * Instantiate the configured shape in Bullet. + */ protected void createShape() { objectId = createShape(radius); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Shape {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/ConeJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/ConeJoint.java index faf739632..d3ec3534a 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/ConeJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/ConeJoint.java @@ -43,10 +43,13 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * From bullet manual:
- * To create ragdolls, the cone twist constraint is very useful for limbs like the upper arm. - * It is a special point to point constraint that adds cone and twist axis limits. - * The x-axis serves as twist axis. + * A joint based on Bullet's btConeTwistConstraint. + *

+ * From the Bullet manual:
+ * To create ragdolls, the cone twist constraint is very useful for limbs like + * the upper arm. It is a special point to point constraint that adds cone and + * twist axis limits. The x-axis serves as twist axis. + * * @author normenhansen */ public class ConeJoint extends PhysicsJoint { @@ -57,12 +60,25 @@ public class ConeJoint extends PhysicsJoint { protected float twistSpan = 1e30f; protected boolean angularOnly = false; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public ConeJoint() { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a ConeJoint. To be effective, the joint must be added to a + * physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) */ public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) { super(nodeA, nodeB, pivotA, pivotB); @@ -72,8 +88,21 @@ public class ConeJoint extends PhysicsJoint { } /** + * Instantiate a ConeJoint. To be effective, the joint must be added to a + * physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) * @param pivotA local translation of the joint connection point in node A + * (not null, alias created) * @param pivotB local translation of the joint connection point in node B + * (not null, alias created) + * @param rotA the local orientation of the connection to node A (not null, + * alias created) + * @param rotB the local orientation of the connection to node B (not null, + * alias created) */ public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB) { super(nodeA, nodeB, pivotA, pivotB); @@ -82,6 +111,13 @@ public class ConeJoint extends PhysicsJoint { createJoint(); } + /** + * Alter the angular limits for this joint. + * + * @param swingSpan1 angle (in radians) + * @param swingSpan2 angle (in radians) + * @param twistSpan angle (in radians) + */ public void setLimit(float swingSpan1, float swingSpan2, float twistSpan) { this.swingSpan1 = swingSpan1; this.swingSpan2 = swingSpan2; @@ -91,6 +127,11 @@ public class ConeJoint extends PhysicsJoint { private native void setLimit(long objectId, float swingSpan1, float swingSpan2, float twistSpan); + /** + * Alter whether this joint is angular only. + * + * @param value the desired setting (default=false) + */ public void setAngularOnly(boolean value) { angularOnly = value; setAngularOnly(objectId, value); @@ -98,6 +139,12 @@ public class ConeJoint extends PhysicsJoint { private native void setAngularOnly(long objectId, boolean value); + /** + * Serialize this joint, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -111,6 +158,12 @@ public class ConeJoint extends PhysicsJoint { capsule.write(twistSpan, "twistSpan", 1e30f); } + /** + * De-serialize this joint, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); @@ -125,6 +178,9 @@ public class ConeJoint extends PhysicsJoint { createJoint(); } + /** + * Create the configured joint in Bullet. + */ protected void createJoint() { objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, rotA, pivotB, rotB); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Joint {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/HingeJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/HingeJoint.java index 352517df7..b0b5c1944 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/HingeJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/HingeJoint.java @@ -42,29 +42,63 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * From 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. + * A 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 PhysicsJoint { protected Vector3f axisA; protected Vector3f axisB; + /** + * copy of the angular-only flag (default=false) + */ protected boolean angularOnly = false; + /** + * copy of the limit's bias factor, how strictly position errors (drift) is + * corrected (default=0.3) + */ protected float biasFactor = 0.3f; + /** + * copy of the limit's relaxation factor, the rate at which velocity errors + * are corrected (default=1) + */ protected float relaxationFactor = 1.0f; + /** + * copy of the limit's softness, the range fraction at which velocity-error + * correction starts operating (default=0.9) + */ protected float limitSoftness = 0.9f; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public HingeJoint() { } /** - * Creates a new HingeJoint - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a HingeJoint. To be effective, the joint must be added to a + * physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) + * @param axisA the local axis of the connection to node A (not null, alias + * created) + * @param axisB the local axis of the connection to node B (not null, alias + * created) */ public HingeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Vector3f axisA, Vector3f axisB) { super(nodeA, nodeB, pivotA, pivotB); @@ -74,10 +108,11 @@ public class HingeJoint extends PhysicsJoint { } /** - * Enables the motor. - * @param enable if true, motor is enabled. - * @param targetVelocity the target velocity of the rotation. - * @param maxMotorImpulse the max force applied to the hinge to rotate it. + * Enable or disable this joint's motor. + * + * @param enable true to enable, false to disable + * @param targetVelocity the desired target velocity + * @param maxMotorImpulse the desired maximum rotational force */ public void enableMotor(boolean enable, float targetVelocity, float maxMotorImpulse) { enableMotor(objectId, enable, targetVelocity, maxMotorImpulse); @@ -85,18 +120,33 @@ public class HingeJoint extends PhysicsJoint { private native void enableMotor(long objectId, boolean enable, float targetVelocity, float maxMotorImpulse); + /** + * Test whether this joint's motor is enabled. + * + * @return true if enabled, otherwise false + */ public boolean getEnableMotor() { return getEnableAngularMotor(objectId); } private native boolean getEnableAngularMotor(long objectId); + /** + * Read the motor's target velocity. + * + * @return velocity + */ public float getMotorTargetVelocity() { return getMotorTargetVelocity(objectId); } private native float getMotorTargetVelocity(long objectId); + /** + * Read the motor's maximum impulse. + * + * @return impulse + */ public float getMaxMotorImpulse() { return getMaxMotorImpulse(objectId); } @@ -104,9 +154,10 @@ public class HingeJoint extends PhysicsJoint { private native float getMaxMotorImpulse(long objectId); /** - * Sets the limits of this joint. - * @param low the low limit in radians. - * @param high the high limit in radians. + * Alter this joint's limits. + * + * @param low the desired lower limit of the hinge angle (in radians) + * @param high the desired upper limit of the joint angle (in radians) */ public void setLimit(float low, float high) { setLimit(objectId, low, high); @@ -115,13 +166,20 @@ public class HingeJoint extends PhysicsJoint { private native void setLimit(long objectId, float low, float high); /** - * Sets the limits of this joint. - * If you're above the softness, velocities that would shoot through the actual limit are slowed down. The bias be in the range of 0.2 - 0.5. - * @param low the low limit in radians. - * @param high the high limit in radians. - * @param _softness the factor at which the velocity error correction starts operating,i.e a softness of 0.9 means that the vel. corr starts at 90% of the limit range. - * @param _biasFactor the magnitude of the position correction. It tells you how strictly the position error (drift ) is corrected. - * @param _relaxationFactor the 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. + * Alter this joint's limits. 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) + * @param high the desired upper limit of the joint angle (in radians) + * @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 _biasFactor the desired magnitude of the position correction, how + * strictly position errors (drift) is corrected. (default=0.3) + * @param _relaxationFactor 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 _biasFactor, float _relaxationFactor) { biasFactor = _biasFactor; @@ -132,18 +190,34 @@ public class HingeJoint extends PhysicsJoint { private native void setLimit(long objectId, float low, float high, float _softness, float _biasFactor, float _relaxationFactor); + /** + * Read the upper limit of the hinge angle. + * + * @return angle (in radians) + */ public float getUpperLimit() { return getUpperLimit(objectId); } private native float getUpperLimit(long objectId); + /** + * Read the lower limit of the hinge angle. + * + * @return the angle (in radians) + */ public float getLowerLimit() { return getLowerLimit(objectId); } private native float getLowerLimit(long objectId); + /** + * Alter the hinge translation flag. + * + * @param angularOnly true→rotate only, false→rotate and translate + * (default=false) + */ public void setAngularOnly(boolean angularOnly) { this.angularOnly = angularOnly; setAngularOnly(objectId, angularOnly); @@ -151,12 +225,23 @@ public class HingeJoint extends PhysicsJoint { private native void setAngularOnly(long objectId, boolean angularOnly); + /** + * Read the hinge angle. + * + * @return the angle (in radians) + */ public float getHingeAngle() { return getHingeAngle(objectId); } private native float getHingeAngle(long objectId); + /** + * Serialize this joint, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { super.write(ex); OutputCapsule capsule = ex.getCapsule(this); @@ -177,6 +262,12 @@ public class HingeJoint extends PhysicsJoint { capsule.write(getMaxMotorImpulse(), "maxMotorImpulse", 0.0f); } + /** + * De-serialize this joint, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { super.read(im); InputCapsule capsule = im.getCapsule(this); @@ -200,6 +291,9 @@ public class HingeJoint extends PhysicsJoint { setLimit(lowerLimit, upperLimit, limitSoftness, biasFactor, relaxationFactor); } + /** + * Create the configured joint in Bullet. + */ protected void createJoint() { objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, axisA, pivotB, axisB); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Joint {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/PhysicsJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/PhysicsJoint.java index 256036ff7..b78e9fb3d 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/PhysicsJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/PhysicsJoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,24 +39,59 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - *

PhysicsJoint - Basic Phyiscs Joint

+ * The abstract base class for physics joints based on Bullet's + * btTypedConstraint, used to connect 2 dynamic rigid bodies in the same + * physics space. + *

+ * Joints include ConeJoint, HingeJoint, Point2PointJoint, and SixDofJoint. + * * @author normenhansen */ public abstract class PhysicsJoint implements Savable { + /** + * Unique identifier of the Bullet constraint. Constructors are responsible + * for setting this to a non-zero value. After that, the id never changes. + */ protected long objectId = 0; + /** + * one of the connected rigid bodies + */ protected PhysicsRigidBody nodeA; + /** + * the other connected rigid body + */ protected PhysicsRigidBody nodeB; + /** + * local offset of this joint's connection point in node A + */ protected Vector3f pivotA; + /** + * local offset of this joint's connection point in node B + */ protected Vector3f pivotB; protected boolean collisionBetweenLinkedBodys = true; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public PhysicsJoint() { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a PhysicsJoint. To be effective, the joint must be added to + * the physics space of the two bodies. Also, the bodies must be dynamic and + * distinct. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA local offset of the joint connection point in node A (not + * null, alias created) + * @param pivotB local offset of the joint connection point in node B (not + * null, alias created) */ public PhysicsJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) { this.nodeA = nodeA; @@ -67,6 +102,11 @@ public abstract class PhysicsJoint implements Savable { nodeB.addJoint(this); } + /** + * Read the magnitude of the applied impulse. + * + * @return impulse + */ public float getAppliedImpulse() { return getAppliedImpulse(objectId); } @@ -74,52 +114,84 @@ public abstract class PhysicsJoint implements Savable { private native float getAppliedImpulse(long objectId); /** - * @return the constraint + * Read the id of the Bullet constraint. + * + * @return the unique identifier (not zero) */ public long getObjectId() { return objectId; } /** - * @return the collisionBetweenLinkedBodys + * Test whether collisions are allowed between the linked bodies. + * + * @return true if collision are allowed, otherwise false */ public boolean isCollisionBetweenLinkedBodys() { return collisionBetweenLinkedBodys; } /** - * toggles collisions between linked bodys
- * joint has to be removed from and added to PhyiscsSpace to apply this. - * @param collisionBetweenLinkedBodys set to false to have no collisions between linked bodys + * Enable or disable collisions between the linked bodies. The joint must be + * removed from and added to PhysicsSpace for this change to be effective. + * + * @param collisionBetweenLinkedBodys true → allow collisions, false → prevent them */ public void setCollisionBetweenLinkedBodys(boolean collisionBetweenLinkedBodys) { this.collisionBetweenLinkedBodys = collisionBetweenLinkedBodys; } + /** + * Access the 1st body specified in during construction. + * + * @return the pre-existing body + */ public PhysicsRigidBody getBodyA() { return nodeA; } + /** + * Access the 2nd body specified in during construction. + * + * @return the pre-existing body + */ public PhysicsRigidBody getBodyB() { return nodeB; } + /** + * Access the local offset of the joint connection point in node A. + * + * @return the pre-existing vector + */ public Vector3f getPivotA() { return pivotA; } + /** + * Access the local offset of the joint connection point in node A. + * + * @return the pre-existing vector + */ public Vector3f getPivotB() { return pivotB; } /** - * destroys this joint and removes it from its connected PhysicsRigidBodys joint lists + * Destroy this joint and remove it from the joint lists of its connected + * bodies. */ public void destroy() { getBodyA().removeJoint(this); getBodyB().removeJoint(this); } + /** + * Serialize this joint, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ public void write(JmeExporter ex) throws IOException { OutputCapsule capsule = ex.getCapsule(this); capsule.write(nodeA, "nodeA", null); @@ -128,6 +200,12 @@ public abstract class PhysicsJoint implements Savable { capsule.write(pivotB, "pivotB", null); } + /** + * De-serialize this joint, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ public void read(JmeImporter im) throws IOException { InputCapsule capsule = im.getCapsule(this); this.nodeA = ((PhysicsRigidBody) capsule.readSavable("nodeA", new PhysicsRigidBody())); @@ -136,6 +214,12 @@ public abstract class PhysicsJoint implements Savable { this.pivotB = (Vector3f) capsule.readSavable("pivotB", new Vector3f()); } + /** + * Finalize this physics joint just before it is destroyed. Should be + * invoked only by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/Point2PointJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/Point2PointJoint.java index 312293e99..932f41f7b 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/Point2PointJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/Point2PointJoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,62 +42,116 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * From bullet manual:
- * Point to point constraint, also known as ball socket joint limits the translation - * so that the local pivot points of 2 rigidbodies match in worldspace. - * A chain of rigidbodies can be connected using this constraint. + * A joint based on Bullet's btPoint2PointConstraint. + *

+ * From the Bullet manual:
+ * Point to point constraint limits the translation so that the local pivot + * points of 2 rigidbodies match in worldspace. A chain of rigidbodies can be + * connected using this constraint. + * * @author normenhansen */ public class Point2PointJoint extends PhysicsJoint { + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public Point2PointJoint() { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a Point2PointJoint. To be effective, the joint must be added + * to a physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) */ public Point2PointJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) { super(nodeA, nodeB, pivotA, pivotB); createJoint(); } + /** + * Alter the joint's damping. + * + * @param value the desired viscous damping ratio (0→no damping, + * 1→critically damped, default=1) + */ public void setDamping(float value) { setDamping(objectId, value); } private native void setDamping(long objectId, float value); + /** + * Alter the joint's impulse clamp. + * + * @param value the desired impulse clamp value (default=0) + */ public void setImpulseClamp(float value) { setImpulseClamp(objectId, value); } private native void setImpulseClamp(long objectId, float value); + /** + * Alter the joint's tau value. + * + * @param value the desired tau value (default=0.3) + */ public void setTau(float value) { setTau(objectId, value); } private native void setTau(long objectId, float value); + /** + * Read the joint's damping ratio. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDamping() { return getDamping(objectId); } private native float getDamping(long objectId); + /** + * Read the joint's impulse clamp. + * + * @return the clamp value + */ public float getImpulseClamp() { return getImpulseClamp(objectId); } private native float getImpulseClamp(long objectId); + /** + * Read the joint's tau value. + * + * @return the tau value + */ public float getTau() { return getTau(objectId); } private native float getTau(long objectId); + /** + * Serialize this joint, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -107,6 +161,12 @@ public class Point2PointJoint extends PhysicsJoint { cap.write(getImpulseClamp(), "impulseClamp", 0f); } + /** + * De-serialize this joint, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); @@ -117,6 +177,9 @@ public class Point2PointJoint extends PhysicsJoint { setDamping(cap.readFloat("impulseClamp", 0f)); } + /** + * Create the configured joint in Bullet. + */ protected void createJoint() { objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, pivotB); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Joint {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java index 648398149..660319a18 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofJoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,33 +47,79 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * From bullet manual:
- * This generic constraint can emulate a variety of standard constraints, - * by configuring each of the 6 degrees of freedom (dof). - * The first 3 dof axis are linear axis, which represent translation of rigidbodies, - * and the latter 3 dof axis represent the angular motion. Each axis can be either locked, - * free or limited. On construction of a new btGeneric6DofConstraint, all axis are locked. - * Afterwards the axis can be reconfigured. Note that several combinations that - * include free and/or limited angular degrees of freedom are undefined. + * A joint based on Bullet's btGeneric6DofConstraint. + *

+ * From the Bullet manual:
+ * This generic constraint can emulate a variety of standard constraints, by + * configuring each of the 6 degrees of freedom (dof). The first 3 dof axis are + * linear axis, which represent translation of rigidbodies, and the latter 3 dof + * axis represent the angular motion. Each axis can be either locked, free or + * limited. On construction of a new btGeneric6DofSpring2Constraint, all axis + * are locked. Afterwards the axis can be reconfigured. Note that several + * combinations that include free and/or limited angular degrees of freedom are + * undefined. + *

+ * For each axis:

    + *
  • Lowerlimit = Upperlimit → axis is locked
  • + *
  • Lowerlimit > Upperlimit → axis is free
  • + *
  • Lowerlimit < Upperlimit → axis it limited in that range
  • + *
+ * * @author normenhansen */ public class SixDofJoint extends PhysicsJoint { Matrix3f rotA, rotB; + /** + * true→limits give the allowable range of movement of frameB in frameA + * space, false→limits give the allowable range of movement of frameA + * in frameB space + */ boolean useLinearReferenceFrameA; LinkedList rotationalMotors = new LinkedList(); TranslationalLimitMotor translationalMotor; + /** + * upper limits for rotation of all 3 axes + */ Vector3f angularUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY); + /** + * lower limits for rotation of all 3 axes + */ Vector3f angularLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY); + /** + * upper limit for translation of all 3 axes + */ Vector3f linearUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY); + /** + * lower limits for translation of all 3 axes + */ Vector3f linearLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY); + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public SixDofJoint() { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a SixDofJoint. To be effective, the joint must be added to a + * physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) + * @param rotA the local orientation of the connection to node A (not null, + * alias created) + * @param rotB the local orientation of the connection to node B (not null, + * alias created) + * @param useLinearReferenceFrameA true→use node A, false→use node + * B */ public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) { super(nodeA, nodeB, pivotA, pivotB); @@ -87,8 +133,19 @@ public class SixDofJoint extends PhysicsJoint { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a SixDofJoint. To be effective, the joint must be added to a + * physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) + * @param useLinearReferenceFrameA true→use node A, false→use node + * B */ public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) { super(nodeA, nodeB, pivotA, pivotB); @@ -114,24 +171,32 @@ public class SixDofJoint extends PhysicsJoint { private native long getTranslationalLimitMotor(long objectId); /** - * returns the TranslationalLimitMotor of this 6DofJoint which allows - * manipulating the translational axis - * @return the TranslationalLimitMotor + * Access the TranslationalLimitMotor of this joint, the motor which + * influences translation on all 3 axes. + * + * @return the pre-existing instance */ public TranslationalLimitMotor getTranslationalLimitMotor() { return translationalMotor; } /** - * returns one of the three RotationalLimitMotors of this 6DofJoint which - * allow manipulating the rotational axes - * @param index the index of the RotationalLimitMotor - * @return the RotationalLimitMotor at the given index + * Access the indexed RotationalLimitMotor of this joint, the motor which + * influences rotation around one axis. + * + * @param index the axis index of the desired motor: 0→X, 1→Y, + * 2→Z + * @return the pre-existing instance */ public RotationalLimitMotor getRotationalLimitMotor(int index) { return rotationalMotors.get(index); } + /** + * Alter the joint's upper limits for translation of all 3 axes. + * + * @param vector the desired upper limits (not null, unaffected) + */ public void setLinearUpperLimit(Vector3f vector) { linearUpperLimit.set(vector); setLinearUpperLimit(objectId, vector); @@ -139,6 +204,11 @@ public class SixDofJoint extends PhysicsJoint { private native void setLinearUpperLimit(long objctId, Vector3f vector); + /** + * Alter the joint's lower limits for translation of all 3 axes. + * + * @param vector the desired lower limits (not null, unaffected) + */ public void setLinearLowerLimit(Vector3f vector) { linearLowerLimit.set(vector); setLinearLowerLimit(objectId, vector); @@ -146,6 +216,11 @@ public class SixDofJoint extends PhysicsJoint { private native void setLinearLowerLimit(long objctId, Vector3f vector); + /** + * Alter the joint's upper limits for rotation of all 3 axes. + * + * @param vector the desired upper limits (in radians, not null, unaffected) + */ public void setAngularUpperLimit(Vector3f vector) { angularUpperLimit.set(vector); setAngularUpperLimit(objectId, vector); @@ -153,6 +228,11 @@ public class SixDofJoint extends PhysicsJoint { private native void setAngularUpperLimit(long objctId, Vector3f vector); + /** + * Alter the joint's lower limits for rotation of all 3 axes. + * + * @param vector the desired lower limits (in radians, not null, unaffected) + */ public void setAngularLowerLimit(Vector3f vector) { angularLowerLimit.set(vector); setAngularLowerLimit(objectId, vector); @@ -162,6 +242,12 @@ public class SixDofJoint extends PhysicsJoint { native long createJoint(long objectIdA, long objectIdB, Vector3f pivotA, Matrix3f rotA, Vector3f pivotB, Matrix3f rotB, boolean useLinearReferenceFrameA); + /** + * De-serialize this joint, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); @@ -197,6 +283,12 @@ public class SixDofJoint extends PhysicsJoint { getTranslationalLimitMotor().setUpperLimit((Vector3f) capsule.readSavable("transMotor_UpperLimit", Vector3f.ZERO)); } + /** + * Serialize this joint, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofSpringJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofSpringJoint.java index e52f9836c..a9479d1e7 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofSpringJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SixDofSpringJoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,14 +36,24 @@ import com.jme3.math.Matrix3f; import com.jme3.math.Vector3f; /** - * From bullet manual:
- * This generic constraint can emulate a variety of standard constraints, - * by configuring each of the 6 degrees of freedom (dof). - * The first 3 dof axis are linear axis, which represent translation of rigidbodies, - * and the latter 3 dof axis represent the angular motion. Each axis can be either locked, - * free or limited. On construction of a new btGeneric6DofConstraint, all axis are locked. - * Afterwards the axis can be reconfigured. Note that several combinations that - * include free and/or limited angular degrees of freedom are undefined. + * A 6 degree-of-freedom joint based on Bullet's btGeneric6DofSpringConstraint. + *

+ * From the Bullet manual:
+ * This generic constraint can emulate a variety of standard constraints, by + * configuring each of the 6 degrees of freedom (dof). The first 3 dof axis are + * linear axis, which represent translation of rigidbodies, and the latter 3 dof + * axis represent the angular motion. Each axis can be either locked, free or + * limited. On construction of a new btGeneric6DofSpring2Constraint, all axis + * are locked. Afterwards the axis can be reconfigured. Note that several + * combinations that include free and/or limited angular degrees of freedom are + * undefined. + *

+ * For each axis:

    + *
  • Lowerlimit = Upperlimit → axis is locked
  • + *
  • Lowerlimit > Upperlimit → axis is free
  • + *
  • Lowerlimit < Upperlimit → axis it limited in that range
  • + *
+ * * @author normenhansen */ public class SixDofSpringJoint extends SixDofJoint { @@ -53,35 +63,84 @@ public class SixDofSpringJoint extends SixDofJoint { final float springStiffness[] = new float[6]; final float springDamping[] = new float[6]; // between 0 and 1 (1 == no damping) + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public SixDofSpringJoint() { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a SixDofSpringJoint. To be effective, the joint must be added + * to a physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) + * @param rotA the local orientation of the connection to node A (not + * null, alias created) + * @param rotB the local orientation of the connection to node B (not + * null, alias created) + * @param useLinearReferenceFrameA true→use node A, false→use node + * B */ public SixDofSpringJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) { super(nodeA, nodeB, pivotA, pivotB, rotA, rotB, useLinearReferenceFrameA); } + + /** + * Enable or disable the spring for the indexed degree of freedom. + * + * @param index which degree of freedom (≥0, <6) + * @param onOff true → enable, false → disable + */ public void enableSpring(int index, boolean onOff) { enableSpring(objectId, index, onOff); } native void enableSpring(long objctId, int index, boolean onOff); + /** + * Alter the spring stiffness for the indexed degree of freedom. + * + * @param index which degree of freedom (≥0, <6) + * @param stiffness the desired stiffness + */ public void setStiffness(int index, float stiffness) { setStiffness(objectId, index, stiffness); } native void setStiffness(long objctId, int index, float stiffness); + /** + * Alter the damping for the indexed degree of freedom. + * + * @param index which degree of freedom (≥0, <6) + * @param damping the desired viscous damping ratio (0→no damping, + * 1→critically damped, default=1) + */ public void setDamping(int index, float damping) { setDamping(objectId, index, damping); } native void setDamping(long objctId, int index, float damping); + /** + * Alter the equilibrium points for all degrees of freedom, based on the + * current constraint position/orientation. + */ public void setEquilibriumPoint() { // set the current constraint position/orientation as an equilibrium point for all DOF setEquilibriumPoint(objectId); } native void setEquilibriumPoint(long objctId); + /** + * Alter the equilibrium point of the indexed degree of freedom, based on + * the current constraint position/orientation. + * + * @param index which degree of freedom (≥0, <6) + */ public void setEquilibriumPoint(int index){ // set the current constraint position/orientation as an equilibrium point for given DOF setEquilibriumPoint(objectId, index); } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SliderJoint.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SliderJoint.java index 4412204ba..8299fbd65 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/SliderJoint.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/SliderJoint.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,8 +43,12 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * From bullet manual:
- * The slider constraint allows the body to rotate around one axis and translate along this axis. + * A slider joint based on Bullet's btSliderConstraint. + *

+ * From the Bullet manual:
+ * The slider constraint allows the body to rotate around one axis and translate + * along this axis. + * * @author normenhansen */ public class SliderJoint extends PhysicsJoint { @@ -52,12 +56,29 @@ public class SliderJoint extends PhysicsJoint { protected Matrix3f rotA, rotB; protected boolean useLinearReferenceFrameA; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public SliderJoint() { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a SliderJoint. To be effective, the joint must be added to a + * physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) + * @param rotA the local orientation of the connection to node A (not null, alias created) + * @param rotB the local orientation of the connection to node B (not null, alias created) + * @param useLinearReferenceFrameA true→use node A, false→use node + * B */ public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) { super(nodeA, nodeB, pivotA, pivotB); @@ -68,8 +89,19 @@ public class SliderJoint extends PhysicsJoint { } /** - * @param pivotA local translation of the joint connection point in node A - * @param pivotB local translation of the joint connection point in node B + * Instantiate a SliderJoint. To be effective, the joint must be added to a + * physics space. + * + * @param nodeA the 1st body connected by the joint (not null, alias + * created) + * @param nodeB the 2nd body connected by the joint (not null, alias + * created) + * @param pivotA the local offset of the connection point in node A (not + * null, alias created) + * @param pivotB the local offset of the connection point in node B (not + * null, alias created) + * @param useLinearReferenceFrameA true→use node A, false→use node + * B */ public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) { super(nodeA, nodeB, pivotA, pivotB); @@ -79,210 +111,399 @@ public class SliderJoint extends PhysicsJoint { createJoint(); } + /** + * Read the joint's lower limit for on-axis translation. + * + * @return the lower limit + */ public float getLowerLinLimit() { return getLowerLinLimit(objectId); } private native float getLowerLinLimit(long objectId); + /** + * Alter the joint's lower limit for on-axis translation. + * + * @param lowerLinLimit the desired lower limit (default=-1) + */ public void setLowerLinLimit(float lowerLinLimit) { setLowerLinLimit(objectId, lowerLinLimit); } private native void setLowerLinLimit(long objectId, float value); + /** + * Read the joint's upper limit for on-axis translation. + * + * @return the upper limit + */ public float getUpperLinLimit() { return getUpperLinLimit(objectId); } private native float getUpperLinLimit(long objectId); + /** + * Alter the joint's upper limit for on-axis translation. + * + * @param upperLinLimit the desired upper limit (default=1) + */ public void setUpperLinLimit(float upperLinLimit) { setUpperLinLimit(objectId, upperLinLimit); } private native void setUpperLinLimit(long objectId, float value); + /** + * Read the joint's lower limit for on-axis rotation. + * + * @return the lower limit angle (in radians) + */ public float getLowerAngLimit() { return getLowerAngLimit(objectId); } private native float getLowerAngLimit(long objectId); + /** + * Alter the joint's lower limit for on-axis rotation. + * + * @param lowerAngLimit the desired lower limit angle (in radians, + * default=0) + */ public void setLowerAngLimit(float lowerAngLimit) { setLowerAngLimit(objectId, lowerAngLimit); } private native void setLowerAngLimit(long objectId, float value); + /** + * Read the joint's upper limit for on-axis rotation. + * + * @return the upper limit angle (in radians) + */ public float getUpperAngLimit() { return getUpperAngLimit(objectId); } private native float getUpperAngLimit(long objectId); + /** + * Alter the joint's upper limit for on-axis rotation. + * + * @param upperAngLimit the desired upper limit angle (in radians, + * default=0) + */ public void setUpperAngLimit(float upperAngLimit) { setUpperAngLimit(objectId, upperAngLimit); } private native void setUpperAngLimit(long objectId, float value); + /** + * Read the joint's softness for on-axis translation between the limits. + * + * @return the softness + */ public float getSoftnessDirLin() { return getSoftnessDirLin(objectId); } private native float getSoftnessDirLin(long objectId); + /** + * Alter the joint's softness for on-axis translation between the limits. + * + * @param softnessDirLin the desired softness (default=1) + */ public void setSoftnessDirLin(float softnessDirLin) { setSoftnessDirLin(objectId, softnessDirLin); } private native void setSoftnessDirLin(long objectId, float value); + /** + * Read the joint's restitution for on-axis translation between the limits. + * + * @return the restitution (bounce) factor + */ public float getRestitutionDirLin() { return getRestitutionDirLin(objectId); } private native float getRestitutionDirLin(long objectId); + /** + * Alter the joint's restitution for on-axis translation between the limits. + * + * @param restitutionDirLin the desired restitution (bounce) factor + * (default=0.7) + */ public void setRestitutionDirLin(float restitutionDirLin) { setRestitutionDirLin(objectId, restitutionDirLin); } private native void setRestitutionDirLin(long objectId, float value); + /** + * Read the joint's damping for on-axis translation between the limits. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDampingDirLin() { return getDampingDirLin(objectId); } private native float getDampingDirLin(long objectId); + /** + * Alter the joint's damping for on-axis translation between the limits. + * + * @param dampingDirLin the desired viscous damping ratio (0→no + * damping, 1→critically damped, default=0) + */ public void setDampingDirLin(float dampingDirLin) { setDampingDirLin(objectId, dampingDirLin); } private native void setDampingDirLin(long objectId, float value); + /** + * Read the joint's softness for on-axis rotation between the limits. + * + * @return the softness + */ public float getSoftnessDirAng() { return getSoftnessDirAng(objectId); } private native float getSoftnessDirAng(long objectId); + /** + * Alter the joint's softness for on-axis rotation between the limits. + * + * @param softnessDirAng the desired softness (default=1) + */ public void setSoftnessDirAng(float softnessDirAng) { setSoftnessDirAng(objectId, softnessDirAng); } private native void setSoftnessDirAng(long objectId, float value); + /** + * Read the joint's restitution for on-axis rotation between the limits. + * + * @return the restitution (bounce) factor + */ public float getRestitutionDirAng() { return getRestitutionDirAng(objectId); } private native float getRestitutionDirAng(long objectId); + /** + * Alter the joint's restitution for on-axis rotation between the limits. + * + * @param restitutionDirAng the desired restitution (bounce) factor + * (default=0.7) + */ public void setRestitutionDirAng(float restitutionDirAng) { setRestitutionDirAng(objectId, restitutionDirAng); } private native void setRestitutionDirAng(long objectId, float value); + /** + * Read the joint's damping for on-axis rotation between the limits. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDampingDirAng() { return getDampingDirAng(objectId); } private native float getDampingDirAng(long objectId); + /** + * Alter the joint's damping for on-axis rotation between the limits. + * + * @param dampingDirAng the desired viscous damping ratio (0→no + * damping, 1→critically damped, default=0) + */ public void setDampingDirAng(float dampingDirAng) { setDampingDirAng(objectId, dampingDirAng); } private native void setDampingDirAng(long objectId, float value); + /** + * Read the joint's softness for on-axis translation hitting the limits. + * + * @return the softness + */ public float getSoftnessLimLin() { return getSoftnessLimLin(objectId); } private native float getSoftnessLimLin(long objectId); + /** + * Alter the joint's softness for on-axis translation hitting the limits. + * + * @param softnessLimLin the desired softness (default=1) + */ public void setSoftnessLimLin(float softnessLimLin) { setSoftnessLimLin(objectId, softnessLimLin); } private native void setSoftnessLimLin(long objectId, float value); + /** + * Read the joint's restitution for on-axis translation hitting the limits. + * + * @return the restitution (bounce) factor + */ public float getRestitutionLimLin() { return getRestitutionLimLin(objectId); } private native float getRestitutionLimLin(long objectId); + /** + * Alter the joint's restitution for on-axis translation hitting the limits. + * + * @param restitutionLimLin the desired restitution (bounce) factor + * (default=0.7) + */ public void setRestitutionLimLin(float restitutionLimLin) { setRestitutionLimLin(objectId, restitutionLimLin); } private native void setRestitutionLimLin(long objectId, float value); + /** + * Read the joint's damping for on-axis translation hitting the limits. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDampingLimLin() { return getDampingLimLin(objectId); } private native float getDampingLimLin(long objectId); + /** + * Alter the joint's damping for on-axis translation hitting the limits. + * + * @param dampingLimLin the desired viscous damping ratio (0→no + * damping, 1→critically damped, default=1) + */ public void setDampingLimLin(float dampingLimLin) { setDampingLimLin(objectId, dampingLimLin); } private native void setDampingLimLin(long objectId, float value); + /** + * Read the joint's softness for on-axis rotation hitting the limits. + * + * @return the softness + */ public float getSoftnessLimAng() { return getSoftnessLimAng(objectId); } private native float getSoftnessLimAng(long objectId); + /** + * Alter the joint's softness for on-axis rotation hitting the limits. + * + * @param softnessLimAng the desired softness (default=1) + */ public void setSoftnessLimAng(float softnessLimAng) { setSoftnessLimAng(objectId, softnessLimAng); } private native void setSoftnessLimAng(long objectId, float value); + /** + * Read the joint's restitution for on-axis rotation hitting the limits. + * + * @return the restitution (bounce) factor + */ public float getRestitutionLimAng() { return getRestitutionLimAng(objectId); } private native float getRestitutionLimAng(long objectId); + /** + * Alter the joint's restitution for on-axis rotation hitting the limits. + * + * @param restitutionLimAng the desired restitution (bounce) factor + * (default=0.7) + */ public void setRestitutionLimAng(float restitutionLimAng) { setRestitutionLimAng(objectId, restitutionLimAng); } private native void setRestitutionLimAng(long objectId, float value); + /** + * Read the joint's damping for on-axis rotation hitting the limits. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDampingLimAng() { return getDampingLimAng(objectId); } private native float getDampingLimAng(long objectId); + /** + * Alter the joint's damping for on-axis rotation hitting the limits. + * + * @param dampingLimAng the desired viscous damping ratio (0→no + * damping, 1→critically damped, default=1) + */ public void setDampingLimAng(float dampingLimAng) { setDampingLimAng(objectId, dampingLimAng); } private native void setDampingLimAng(long objectId, float value); + /** + * Read the joint's softness for off-axis translation. + * + * @return the softness + */ public float getSoftnessOrthoLin() { return getSoftnessOrthoLin(objectId); } private native float getSoftnessOrthoLin(long objectId); + /** + * Alter the joint's softness for off-axis translation. + * + * @param softnessOrthoLin the desired softness (default=1) + */ public void setSoftnessOrthoLin(float softnessOrthoLin) { setSoftnessOrthoLin(objectId, softnessOrthoLin); } private native void setSoftnessOrthoLin(long objectId, float value); + /** + * Read the joint's restitution for off-axis translation. + * + * @return the restitution (bounce) factor + */ public float getRestitutionOrthoLin() { return getRestitutionOrthoLin(objectId); } @@ -292,7 +513,8 @@ public class SliderJoint extends PhysicsJoint { /** * Alter the joint's restitution for off-axis translation. * - * @param restitutionOrthoLin the desired restitution (default=0.7) + * @param restitutionOrthoLin the desired restitution (bounce) factor + * (default=0.7) */ public void setRestitutionOrthoLin(float restitutionOrthoLin) { setRestitutionOrthoLin(objectId, restitutionOrthoLin); @@ -300,126 +522,240 @@ public class SliderJoint extends PhysicsJoint { private native void setRestitutionOrthoLin(long objectId, float value); + /** + * Read the joint's damping for off-axis translation. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDampingOrthoLin() { return getDampingOrthoLin(objectId); } private native float getDampingOrthoLin(long objectId); + /** + * Alter the joint's damping for off-axis translation. + * + * @param dampingOrthoLin the desired viscous damping ratio (0→no + * damping, 1→critically damped, default=1) + */ public void setDampingOrthoLin(float dampingOrthoLin) { setDampingOrthoLin(objectId, dampingOrthoLin); } private native void setDampingOrthoLin(long objectId, float value); + /** + * Read the joint's softness for off-axis rotation. + * + * @return the softness + */ public float getSoftnessOrthoAng() { return getSoftnessOrthoAng(objectId); } private native float getSoftnessOrthoAng(long objectId); + /** + * Alter the joint's softness for off-axis rotation. + * + * @param softnessOrthoAng the desired softness (default=1) + */ public void setSoftnessOrthoAng(float softnessOrthoAng) { setSoftnessOrthoAng(objectId, softnessOrthoAng); } private native void setSoftnessOrthoAng(long objectId, float value); + /** + * Read the joint's restitution for off-axis rotation. + * + * @return the restitution (bounce) factor + */ public float getRestitutionOrthoAng() { return getRestitutionOrthoAng(objectId); } private native float getRestitutionOrthoAng(long objectId); + /** + * Alter the joint's restitution for off-axis rotation. + * + * @param restitutionOrthoAng the desired restitution (bounce) factor + * (default=0.7) + */ public void setRestitutionOrthoAng(float restitutionOrthoAng) { setRestitutionOrthoAng(objectId, restitutionOrthoAng); } private native void setRestitutionOrthoAng(long objectId, float value); + /** + * Read the joint's damping for off-axis rotation. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDampingOrthoAng() { return getDampingOrthoAng(objectId); } private native float getDampingOrthoAng(long objectId); + /** + * Alter the joint's damping for off-axis rotation. + * + * @param dampingOrthoAng the desired viscous damping ratio (0→no + * damping, 1→critically damped, default=1) + */ public void setDampingOrthoAng(float dampingOrthoAng) { setDampingOrthoAng(objectId, dampingOrthoAng); } private native void setDampingOrthoAng(long objectId, float value); + /** + * Test whether the translation motor is powered. + * + * @return true if powered, otherwise false + */ public boolean isPoweredLinMotor() { return isPoweredLinMotor(objectId); } private native boolean isPoweredLinMotor(long objectId); + /** + * Alter whether the translation motor is powered. + * + * @param poweredLinMotor true to power the motor, false to de-power it + * (default=false) + */ public void setPoweredLinMotor(boolean poweredLinMotor) { setPoweredLinMotor(objectId, poweredLinMotor); } private native void setPoweredLinMotor(long objectId, boolean value); + /** + * Read the velocity target of the translation motor. + * + * @return the velocity target + */ public float getTargetLinMotorVelocity() { return getTargetLinMotorVelocity(objectId); } private native float getTargetLinMotorVelocity(long objectId); + /** + * Alter the velocity target of the translation motor. + * + * @param targetLinMotorVelocity the desired velocity target (default=0) + */ public void setTargetLinMotorVelocity(float targetLinMotorVelocity) { setTargetLinMotorVelocity(objectId, targetLinMotorVelocity); } private native void setTargetLinMotorVelocity(long objectId, float value); + /** + * Read the maximum force of the translation motor. + * + * @return the maximum force + */ public float getMaxLinMotorForce() { return getMaxLinMotorForce(objectId); } private native float getMaxLinMotorForce(long objectId); + /** + * Alter the maximum force of the translation motor. + * + * @param maxLinMotorForce the desired maximum force (default=0) + */ public void setMaxLinMotorForce(float maxLinMotorForce) { setMaxLinMotorForce(objectId, maxLinMotorForce); } private native void setMaxLinMotorForce(long objectId, float value); + /** + * Test whether the rotation motor is powered. + * + * @return true if powered, otherwise false + */ public boolean isPoweredAngMotor() { return isPoweredAngMotor(objectId); } private native boolean isPoweredAngMotor(long objectId); + /** + * Alter whether the rotation motor is powered. + * + * @param poweredAngMotor true to power the motor, false to de-power it + * (default=false) + */ public void setPoweredAngMotor(boolean poweredAngMotor) { setPoweredAngMotor(objectId, poweredAngMotor); } private native void setPoweredAngMotor(long objectId, boolean value); + /** + * Read the velocity target of the rotation motor. + * + * @return the velocity target (in radians per second) + */ public float getTargetAngMotorVelocity() { return getTargetAngMotorVelocity(objectId); } private native float getTargetAngMotorVelocity(long objectId); + /** + * Alter the velocity target of the rotation motor. + * + * @param targetAngMotorVelocity the desired velocity target (in radians per + * second, default=0) + */ public void setTargetAngMotorVelocity(float targetAngMotorVelocity) { setTargetAngMotorVelocity(objectId, targetAngMotorVelocity); } private native void setTargetAngMotorVelocity(long objectId, float value); + /** + * Read the maximum force of the rotation motor. + * + * @return the maximum force + */ public float getMaxAngMotorForce() { return getMaxAngMotorForce(objectId); } private native float getMaxAngMotorForce(long objectId); + /** + * Alter the maximum force of the rotation motor. + * + * @param maxAngMotorForce the desired maximum force (default=0) + */ public void setMaxAngMotorForce(float maxAngMotorForce) { setMaxAngMotorForce(objectId, maxAngMotorForce); } private native void setMaxAngMotorForce(long objectId, float value); + /** + * Serialize this joint, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { super.write(ex); @@ -460,6 +796,12 @@ public class SliderJoint extends PhysicsJoint { capsule.write(useLinearReferenceFrameA, "useLinearReferenceFrameA", false); } + /** + * De-serialize this joint, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { super.read(im); @@ -533,6 +875,9 @@ public class SliderJoint extends PhysicsJoint { setUpperLinLimit(upperLinLimit); } + /** + * Instantiate the configured constraint in Bullet. + */ protected void createJoint() { objectId = createJoint(nodeA.getObjectId(), nodeB.getObjectId(), pivotA, rotA, pivotB, rotB, useLinearReferenceFrameA); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created Joint {0}", Long.toHexString(objectId)); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/RotationalLimitMotor.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/RotationalLimitMotor.java index 4b4b4aa95..88db32aba 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/RotationalLimitMotor.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/RotationalLimitMotor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,135 +32,253 @@ package com.jme3.bullet.joints.motors; /** + * A motor based on Bullet's btRotationalLimitMotor. Motors are used to drive + * joints. * * @author normenhansen */ public class RotationalLimitMotor { + /** + * Unique identifier of the btRotationalLimitMotor. The constructor sets + * this to a non-zero value. + */ private long motorId = 0; + /** + * Instantiate a motor for the identified btRotationalLimitMotor. + * + * @param motor the unique identifier (not zero) + */ public RotationalLimitMotor(long motor) { this.motorId = motor; } + /** + * Read the id of the btRotationalLimitMotor. + * + * @return the identifier of the btRotationalLimitMotor (not zero) + */ public long getMotor() { return motorId; } + /** + * Read this motor's constraint lower limit. + * + * @return the limit value + */ public float getLoLimit() { return getLoLimit(motorId); } private native float getLoLimit(long motorId); + /** + * Alter this motor's constraint lower limit. + * + * @param loLimit the desired limit value + */ public void setLoLimit(float loLimit) { setLoLimit(motorId, loLimit); } private native void setLoLimit(long motorId, float loLimit); + /** + * Read this motor's constraint upper limit. + * + * @return the limit value + */ public float getHiLimit() { return getHiLimit(motorId); } private native float getHiLimit(long motorId); + /** + * Alter this motor's constraint upper limit. + * + * @param hiLimit the desired limit value + */ public void setHiLimit(float hiLimit) { setHiLimit(motorId, hiLimit); } private native void setHiLimit(long motorId, float hiLimit); + /** + * Read this motor's target velocity. + * + * @return the target velocity (in radians per second) + */ public float getTargetVelocity() { return getTargetVelocity(motorId); } private native float getTargetVelocity(long motorId); + /** + * Alter this motor's target velocity. + * + * @param targetVelocity the desired target velocity (in radians per second) + */ public void setTargetVelocity(float targetVelocity) { setTargetVelocity(motorId, targetVelocity); } private native void setTargetVelocity(long motorId, float targetVelocity); + /** + * Read this motor's maximum force. + * + * @return the maximum force + */ public float getMaxMotorForce() { return getMaxMotorForce(motorId); } private native float getMaxMotorForce(long motorId); + /** + * Alter this motor's maximum force. + * + * @param maxMotorForce the desired maximum force on the motor + */ public void setMaxMotorForce(float maxMotorForce) { setMaxMotorForce(motorId, maxMotorForce); } private native void setMaxMotorForce(long motorId, float maxMotorForce); + /** + * Read the limit's maximum force. + * + * @return the maximum force on the limit + */ public float getMaxLimitForce() { return getMaxLimitForce(motorId); } private native float getMaxLimitForce(long motorId); + /** + * Alter the limit's maximum force. + * + * @param maxLimitForce the desired maximum force on the limit + */ public void setMaxLimitForce(float maxLimitForce) { setMaxLimitForce(motorId, maxLimitForce); } private native void setMaxLimitForce(long motorId, float maxLimitForce); + /** + * Read this motor's damping. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDamping() { return getDamping(motorId); } private native float getDamping(long motorId); + /** + * Alter this motor's damping. + * + * @param damping the desired viscous damping ratio (0→no damping, + * 1→critically damped, default=1) + */ public void setDamping(float damping) { setDamping(motorId, damping); } private native void setDamping(long motorId, float damping); + /** + * Read this motor's limit softness. + * + * @return the limit softness + */ public float getLimitSoftness() { return getLimitSoftness(motorId); } private native float getLimitSoftness(long motorId); + /** + * Alter this motor's limit softness. + * + * @param limitSoftness the desired limit softness + */ public void setLimitSoftness(float limitSoftness) { setLimitSoftness(motorId, limitSoftness); } private native void setLimitSoftness(long motorId, float limitSoftness); + /** + * Read this motor's error tolerance at limits. + * + * @return the error tolerance (>0) + */ public float getERP() { return getERP(motorId); } private native float getERP(long motorId); + /** + * Alter this motor's error tolerance at limits. + * + * @param ERP the desired error tolerance (>0) + */ public void setERP(float ERP) { setERP(motorId, ERP); } private native void setERP(long motorId, float ERP); + /** + * Read this motor's bounce. + * + * @return the bounce (restitution factor) + */ public float getBounce() { return getBounce(motorId); } private native float getBounce(long motorId); + /** + * Alter this motor's bounce. + * + * @param bounce the desired bounce (restitution factor) + */ public void setBounce(float bounce) { setBounce(motorId, bounce); } private native void setBounce(long motorId, float limitSoftness); + /** + * Test whether this motor is enabled. + * + * @return true if enabled, otherwise false + */ public boolean isEnableMotor() { return isEnableMotor(motorId); } private native boolean isEnableMotor(long motorId); + /** + * Enable or disable this motor. + * + * @param enableMotor true→enable, false→disable + */ public void setEnableMotor(boolean enableMotor) { setEnableMotor(motorId, enableMotor); } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java b/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java index 3b7690697..97993edd3 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -34,21 +34,42 @@ package com.jme3.bullet.joints.motors; import com.jme3.math.Vector3f; /** + * A motor based on Bullet's btTranslationalLimitMotor. Motors are used to drive + * joints. * * @author normenhansen */ public class TranslationalLimitMotor { + /** + * Unique identifier of the btTranslationalLimitMotor. The constructor sets + * this to a non-zero value. After that, the id never changes. + */ private long motorId = 0; + /** + * Instantiate a motor for the identified btTranslationalLimitMotor. + * + * @param motor the unique identifier (not zero) + */ public TranslationalLimitMotor(long motor) { this.motorId = motor; } + /** + * Read the id of the btTranslationalLimitMotor. + * + * @return the unique identifier (not zero) + */ public long getMotor() { return motorId; } + /** + * Copy this motor's constraint lower limits. + * + * @return a new vector (not null) + */ public Vector3f getLowerLimit() { Vector3f vec = new Vector3f(); getLowerLimit(motorId, vec); @@ -57,12 +78,22 @@ public class TranslationalLimitMotor { private native void getLowerLimit(long motorId, Vector3f vector); + /** + * Alter the constraint lower limits. + * + * @param lowerLimit (unaffected, not null) + */ public void setLowerLimit(Vector3f lowerLimit) { setLowerLimit(motorId, lowerLimit); } private native void setLowerLimit(long motorId, Vector3f vector); + /** + * Copy this motor's constraint upper limits. + * + * @return a new vector (not null) + */ public Vector3f getUpperLimit() { Vector3f vec = new Vector3f(); getUpperLimit(motorId, vec); @@ -71,12 +102,22 @@ public class TranslationalLimitMotor { private native void getUpperLimit(long motorId, Vector3f vector); + /** + * Alter the constraint upper limits. + * + * @param upperLimit (unaffected, not null) + */ public void setUpperLimit(Vector3f upperLimit) { setUpperLimit(motorId, upperLimit); } private native void setUpperLimit(long motorId, Vector3f vector); + /** + * Copy the accumulated impulse. + * + * @return a new vector (not null) + */ public Vector3f getAccumulatedImpulse() { Vector3f vec = new Vector3f(); getAccumulatedImpulse(motorId, vec); @@ -85,42 +126,79 @@ public class TranslationalLimitMotor { private native void getAccumulatedImpulse(long motorId, Vector3f vector); + /** + * Alter the accumulated impulse. + * + * @param accumulatedImpulse the desired vector (not null, unaffected) + */ public void setAccumulatedImpulse(Vector3f accumulatedImpulse) { setAccumulatedImpulse(motorId, accumulatedImpulse); } private native void setAccumulatedImpulse(long motorId, Vector3f vector); + /** + * Read this motor's limit softness. + * + * @return the softness + */ public float getLimitSoftness() { return getLimitSoftness(motorId); } private native float getLimitSoftness(long motorId); + /** + * Alter the limit softness. + * + * @param limitSoftness the desired limit softness (default=0.5) + */ public void setLimitSoftness(float limitSoftness) { setLimitSoftness(motorId, limitSoftness); } private native void setLimitSoftness(long motorId, float limitSoftness); + /** + * Read this motor's damping. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getDamping() { return getDamping(motorId); } private native float getDamping(long motorId); + /** + * Alter this motor's damping. + * + * @param damping the desired viscous damping ratio (0→no damping, + * 1→critically damped, default=1) + */ public void setDamping(float damping) { setDamping(motorId, damping); } private native void setDamping(long motorId, float damping); + /** + * Read this motor's restitution. + * + * @return the restitution (bounce) factor + */ public float getRestitution() { return getRestitution(motorId); } private native float getRestitution(long motorId); + /** + * Alter this motor's restitution. + * + * @param restitution the desired restitution (bounce) factor + */ public void setRestitution(float restitution) { setRestitution(motorId, restitution); } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsCharacter.java b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsCharacter.java index bf55a2909..ad6cf96b6 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsCharacter.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsCharacter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,11 +44,19 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * Basic Bullet Character + * A collision object for simplified character simulation, based on Bullet's + * btKinematicCharacterController. + * * @author normenhansen */ public class PhysicsCharacter extends PhysicsCollisionObject { + /** + * Unique identifier of btKinematicCharacterController (as opposed to its + * collision object, which is a ghost). Constructors are responsible for + * setting this to a non-zero value. The id might change if the character + * gets rebuilt. + */ protected long characterId = 0; protected float stepHeight; protected Vector3f walkDirection = new Vector3f(); @@ -59,12 +67,19 @@ public class PhysicsCharacter extends PhysicsCollisionObject { //TEMP VARIABLES protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public PhysicsCharacter() { } /** - * @param shape The CollisionShape (no Mesh or CompoundCollisionShapes) - * @param stepHeight The quantization size for vertical movement + * Instantiate a character with the specified collision shape and step + * height. + * + * @param shape the desired shape (not null, alias created) + * @param stepHeight the quantization size for vertical movement */ public PhysicsCharacter(CollisionShape shape, float stepHeight) { this.collisionShape = shape; @@ -75,6 +90,9 @@ public class PhysicsCharacter extends PhysicsCollisionObject { buildObject(); } + /** + * Create the configured character in Bullet. + */ protected void buildObject() { if (objectId == 0) { objectId = createGhostObject(); @@ -98,8 +116,9 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native long createCharacterObject(long objectId, long shapeId, float stepHeight); /** - * Sets the location of this physics character - * @param location + * Directly alter the location of this character's center of mass. + * + * @param location the desired physics location (not null, unaffected) */ public void warp(Vector3f location) { warp(characterId, location); @@ -108,11 +127,11 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void warp(long characterId, Vector3f location); /** - * Set the walk direction, works continuously. - * This should probably be called setPositionIncrementPerSimulatorStep. - * This is neither a direction nor a velocity, but the amount to - * increment the position each physics tick. So vector length = accuracy*speed in m/s - * @param vec the walk direction to set + * Alter the walk offset. The offset will continue to be applied until + * altered again. + * + * @param vec the desired position increment for each physics tick (not + * null, unaffected) */ public void setWalkDirection(Vector3f vec) { walkDirection.set(vec); @@ -122,7 +141,9 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setWalkDirection(long characterId, Vector3f vec); /** - * @return the currently set walkDirection + * Access the walk offset. + * + * @return the pre-existing instance */ public Vector3f getWalkDirection() { return walkDirection; @@ -130,6 +151,7 @@ public class PhysicsCharacter extends PhysicsCollisionObject { /** * @deprecated Deprecated in bullet 2.86.1 use setUp(Vector3f) instead + * @param axis which axis: 0→X, 1→Y, 2→Z */ @Deprecated public void setUpAxis(int axis) { @@ -147,6 +169,11 @@ public class PhysicsCharacter extends PhysicsCollisionObject { } } + /** + * Alter this character's "up" direction. + * + * @param axis the desired direction (not null, not zero, unaffected) + */ public void setUp(Vector3f axis) { setUp(characterId, axis); } @@ -154,6 +181,11 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setUp(long characterId, Vector3f axis); + /** + * Alter this character's angular velocity. + * + * @param v the desired angular velocity vector (not null, unaffected) + */ public void setAngularVelocity(Vector3f v){ setAngularVelocity(characterId,v); @@ -161,7 +193,13 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setAngularVelocity(long characterId, Vector3f v); - + /** + * Copy this character's angular velocity. + * + * @param out storage for the result (modified if not null) + * @return the velocity vector (either the provided storage or a new vector, + * not null) + */ public Vector3f getAngularVelocity(Vector3f out){ if(out==null)out=new Vector3f(); getAngularVelocity(characterId,out); @@ -171,13 +209,23 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void getAngularVelocity(long characterId, Vector3f out); + /** + * Alter the linear velocity of this character's center of mass. + * + * @param v the desired velocity vector (not null) + */ public void setLinearVelocity(Vector3f v){ setLinearVelocity(characterId,v); } private native void setLinearVelocity(long characterId, Vector3f v); - + /** + * Copy the linear velocity of this character's center of mass. + * + * @param out storage for the result (modified if not null) + * @return a vector (either the provided storage or a new vector, not null) + */ public Vector3f getLinearVelocity(Vector3f out){ if(out==null)out=new Vector3f(); getLinearVelocity(characterId,out); @@ -187,10 +235,20 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void getLinearVelocity(long characterId, Vector3f out); + /** + * Read the index of the "up" axis. + * + * @return which axis: 0→X, 1→Y, 2→Z + */ public int getUpAxis() { return upAxis; } + /** + * Alter this character's fall speed. + * + * @param fallSpeed the desired speed (default=55) + */ public void setFallSpeed(float fallSpeed) { this.fallSpeed = fallSpeed; setFallSpeed(characterId, fallSpeed); @@ -198,10 +256,20 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setFallSpeed(long characterId, float fallSpeed); + /** + * Read this character's fall speed. + * + * @return speed + */ public float getFallSpeed() { return fallSpeed; } + /** + * Alter this character's jump speed. + * + * @param jumpSpeed the desired speed (default=10) + */ public void setJumpSpeed(float jumpSpeed) { this.jumpSpeed = jumpSpeed; setJumpSpeed(characterId, jumpSpeed); @@ -209,18 +277,30 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setJumpSpeed(long characterId, float jumpSpeed); + /** + * Read this character's jump speed. + * + * @return speed + */ public float getJumpSpeed() { return jumpSpeed; } /** * @deprecated Deprecated in bullet 2.86.1. Use setGravity(Vector3f) instead. + * @param value the desired upward component of the acceleration (typically + * negative) */ @Deprecated public void setGravity(float value) { setGravity(new Vector3f(0,value,0)); } + /** + * Alter this character's gravitational acceleration. + * + * @param value the desired acceleration vector (not null, unaffected) + */ public void setGravity(Vector3f value) { setGravity(characterId, value); } @@ -229,12 +309,20 @@ public class PhysicsCharacter extends PhysicsCollisionObject { /** * @deprecated Deprecated in bullet 2.86.1. Use getGravity(Vector3f) instead. + * @return the upward component of the acceleration (typically negative) */ @Deprecated public float getGravity() { return getGravity(null).y; } + /** + * Copy this character's gravitational acceleration. + * + * @param out storage for the result (modified if not null) + * @return the acceleration vector (either the provided storage or a new + * vector, not null) + */ public Vector3f getGravity(Vector3f out) { if(out==null)out=new Vector3f(); getGravity(characterId,out); @@ -244,12 +332,24 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void getGravity(long characterId,Vector3f out); + /** + * Read this character's linear damping. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getLinearDamping(){ return getLinearDamping(characterId); } private native float getLinearDamping(long characterId); + /** + * Alter this character's linear damping. + * + * @param v the desired viscous damping ratio (0→no damping, + * 1→critically damped) + */ public void setLinearDamping(float v ){ setLinearDamping(characterId,v ); @@ -258,13 +358,24 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setLinearDamping(long characterId,float v); + /** + * Read this character's angular damping. + * + * @return the viscous damping ratio (0→no damping, 1→critically + * damped) + */ public float getAngularDamping(){ return getAngularDamping(characterId); } private native float getAngularDamping(long characterId); - + /** + * Alter this character's angular damping. + * + * @param v the desired viscous damping ratio (0→no damping, + * 1→critically damped, default=0) + */ public void setAngularDamping(float v ){ setAngularDamping(characterId,v ); } @@ -272,13 +383,22 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setAngularDamping(long characterId,float v); + /** + * Read this character's step height. + * + * @return the height (in physics-space units) + */ public float getStepHeight(){ return getStepHeight(characterId); } private native float getStepHeight(long characterId); - + /** + * Alter this character's step height. + * + * @param v the desired height (in physics-space units) + */ public void setStepHeight(float v ){ setStepHeight(characterId,v ); } @@ -286,13 +406,22 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void setStepHeight(long characterId,float v); + /** + * Read this character's maximum penetration depth. + * + * @return the depth (in physics-space units) + */ public float getMaxPenetrationDepth(){ return getMaxPenetrationDepth(characterId); } private native float getMaxPenetrationDepth(long characterId); - + /** + * Alter this character's maximum penetration depth. + * + * @param v the desired depth (in physics-space units) + */ public void setMaxPenetrationDepth(float v ){ setMaxPenetrationDepth(characterId,v ); } @@ -303,18 +432,33 @@ public class PhysicsCharacter extends PhysicsCollisionObject { + /** + * Alter this character's maximum slope angle. + * + * @param slopeRadians the desired angle (in radians) + */ public void setMaxSlope(float slopeRadians) { setMaxSlope(characterId, slopeRadians); } private native void setMaxSlope(long characterId, float slopeRadians); + /** + * Read this character's maximum slope angle. + * + * @return the angle (in radians) + */ public float getMaxSlope() { return getMaxSlope(characterId); } private native float getMaxSlope(long characterId); + /** + * Test whether this character is on the ground. + * + * @return true if on the ground, otherwise false + */ public boolean onGround() { return onGround(characterId); } @@ -330,12 +474,24 @@ public class PhysicsCharacter extends PhysicsCollisionObject { } + /** + * Jump in the specified direction. + * + * @param dir desired jump direction (not null, unaffected) + */ public void jump(Vector3f dir) { jump(characterId,dir); } private native void jump(long characterId,Vector3f v); + /** + * Apply the specified CollisionShape to this character. Note that the + * character should not be in any physics space while changing shape; the + * character gets rebuilt on the physics side. + * + * @param collisionShape the shape to apply (not null, alias created) + */ @Override public void setCollisionShape(CollisionShape collisionShape) { // if (!(collisionShape.getObjectId() instanceof ConvexShape)) { @@ -350,15 +506,21 @@ public class PhysicsCharacter extends PhysicsCollisionObject { } /** - * Set the physics location (same as warp()) - * @param location the location of the actual physics object + * Directly alter this character's location. (Same as + * {@link #warp(com.jme3.math.Vector3f)}).) + * + * @param location the desired location (not null, unaffected) */ public void setPhysicsLocation(Vector3f location) { warp(location); } /** - * @return the physicsLocation + * Copy the location of this character's center of mass. + * + * @param trans storage for the result (modified if not null) + * @return the location vector (either the provided storage or a new vector, + * not null) */ public Vector3f getPhysicsLocation(Vector3f trans) { if (trans == null) { @@ -371,36 +533,72 @@ public class PhysicsCharacter extends PhysicsCollisionObject { private native void getPhysicsLocation(long objectId, Vector3f vec); /** - * @return the physicsLocation + * Copy the location of this character's center of mass. + * + * @return a new location vector (not null) */ public Vector3f getPhysicsLocation() { return getPhysicsLocation(null); } + /** + * Alter this character's continuous collision detection (CCD) swept sphere + * radius. + * + * @param radius (≥0, default=0) + */ public void setCcdSweptSphereRadius(float radius) { setCcdSweptSphereRadius(objectId, radius); } private native void setCcdSweptSphereRadius(long objectId, float radius); + /** + * Alter the amount of motion required to activate continuous collision + * detection (CCD). + *

+ * This addresses the issue of fast objects passing through other objects + * with no collision detected. + * + * @param threshold the desired threshold velocity (>0) or zero to + * disable CCD (default=0) + */ public void setCcdMotionThreshold(float threshold) { setCcdMotionThreshold(objectId, threshold); } private native void setCcdMotionThreshold(long objectId, float threshold); + /** + * Read the radius of the sphere used for continuous collision detection + * (CCD). + * + * @return radius (≥0) + */ public float getCcdSweptSphereRadius() { return getCcdSweptSphereRadius(objectId); } private native float getCcdSweptSphereRadius(long objectId); + /** + * Calculate this character's continuous collision detection (CCD) motion + * threshold. + * + * @return the threshold velocity (≥0) + */ public float getCcdMotionThreshold() { return getCcdMotionThreshold(objectId); } private native float getCcdMotionThreshold(long objectId); + /** + * Calculate the square of this character's continuous collision detection + * (CCD) motion threshold. + * + * @return the threshold velocity squared (≥0) + */ public float getCcdSquareMotionThreshold() { return getCcdSquareMotionThreshold(objectId); } @@ -409,14 +607,25 @@ public class PhysicsCharacter extends PhysicsCollisionObject { /** * used internally + * + * @return the Bullet id */ public long getControllerId() { return characterId; } + /** + * Has no effect. + */ public void destroy() { } + /** + * Serialize this character, for example when saving to a J3O file. + * + * @param e exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter e) throws IOException { super.write(e); @@ -432,6 +641,13 @@ public class PhysicsCharacter extends PhysicsCollisionObject { capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f()); } + /** + * De-serialize this character from the specified importer, for example when + * loading from a J3O file. + * + * @param e importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter e) throws IOException { super.read(e); @@ -448,6 +664,12 @@ public class PhysicsCharacter extends PhysicsCollisionObject { setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f())); } + /** + * Finalize this physics character just before it is destroyed. Should be + * invoked only by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsGhostObject.java b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsGhostObject.java index 052e280e4..0ba0034cc 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsGhostObject.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsGhostObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,11 +48,14 @@ import java.util.logging.Level; import java.util.logging.Logger; /** + * A collision object for intangibles, based on Bullet's + * btPairCachingGhostObject. This is useful for creating a character controller, + * collision sensors/triggers, explosions etc. + *

* From Bullet manual:
- * GhostObject can keep track of all objects that are overlapping. - * By default, this overlap is based on the AABB. - * This is useful for creating a character controller, - * collision sensors/triggers, explosions etc.
+ * btGhostObject is a special btCollisionObject, useful for fast localized + * collision queries. + * * @author normenhansen */ public class PhysicsGhostObject extends PhysicsCollisionObject { @@ -61,9 +64,18 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); private List overlappingObjects = new LinkedList(); + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public PhysicsGhostObject() { } + /** + * Instantiate an object with the specified collision shape. + * + * @param shape the desired shape (not null, alias created) + */ public PhysicsGhostObject(CollisionShape shape) { collisionShape = shape; buildObject(); @@ -74,6 +86,9 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { buildObject(); } + /** + * Create the configured object in Bullet. + */ protected void buildObject() { if (objectId == 0) { // gObject = new PairCachingGhostObject(); @@ -93,6 +108,13 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native void setGhostFlags(long objectId); + /** + * Apply the specified CollisionShape to this object. Note that the object + * should not be in any physics space while changing shape; the object gets + * rebuilt on the physics side. + * + * @param collisionShape the shape to apply (not null, alias created) + */ @Override public void setCollisionShape(CollisionShape collisionShape) { super.setCollisionShape(collisionShape); @@ -104,8 +126,9 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { } /** - * Sets the physics object location - * @param location the location of the actual physics object + * Directly alter the location of this object's center. + * + * @param location the desired location (not null, unaffected) */ public void setPhysicsLocation(Vector3f location) { setPhysicsLocation(objectId, location); @@ -114,8 +137,10 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native void setPhysicsLocation(long objectId, Vector3f location); /** - * Sets the physics object rotation - * @param rotation the rotation of the actual physics object + * Directly alter this object's orientation. + * + * @param rotation the desired orientation (rotation matrix, not null, + * unaffected) */ public void setPhysicsRotation(Matrix3f rotation) { setPhysicsRotation(objectId, rotation); @@ -124,8 +149,10 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native void setPhysicsRotation(long objectId, Matrix3f rotation); /** - * Sets the physics object rotation - * @param rotation the rotation of the actual physics object + * Directly alter this object's orientation. + * + * @param rotation the desired orientation (quaternion, not null, + * unaffected) */ public void setPhysicsRotation(Quaternion rotation) { setPhysicsRotation(objectId, rotation); @@ -134,7 +161,11 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native void setPhysicsRotation(long objectId, Quaternion rotation); /** - * @return the physicsLocation + * Copy the location of this object's center. + * + * @param trans storage for the result (modified if not null) + * @return the physics location (either the provided storage or a new + * vector, not null) */ public Vector3f getPhysicsLocation(Vector3f trans) { if (trans == null) { @@ -147,7 +178,11 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native void getPhysicsLocation(long objectId, Vector3f vector); /** - * @return the physicsLocation + * Copy this object's orientation to a quaternion. + * + * @param rot storage for the result (modified if not null) + * @return the physics orientation (either the provided storage or a new + * quaternion, not null) */ public Quaternion getPhysicsRotation(Quaternion rot) { if (rot == null) { @@ -160,7 +195,11 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native void getPhysicsRotation(long objectId, Quaternion rot); /** - * @return the physicsLocation + * Copy this object's orientation to a matrix. + * + * @param rot storage for the result (modified if not null) + * @return the orientation (either the provided storage or a new matrix, not + * null) */ public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) { if (rot == null) { @@ -173,7 +212,9 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native void getPhysicsRotationMatrix(long objectId, Matrix3f rot); /** - * @return the physicsLocation + * Copy the location of this object's center. + * + * @return a new location vector (not null) */ public Vector3f getPhysicsLocation() { Vector3f vec = new Vector3f(); @@ -182,7 +223,9 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { } /** - * @return the physicsLocation + * Copy this object's orientation to a quaternion. + * + * @return a new quaternion (not null) */ public Quaternion getPhysicsRotation() { Quaternion quat = new Quaternion(); @@ -190,6 +233,11 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { return quat; } + /** + * Copy this object's orientation to a matrix. + * + * @return a new matrix (not null) + */ public Matrix3f getPhysicsRotationMatrix() { Matrix3f mtx = new Matrix3f(); getPhysicsRotationMatrix(objectId, mtx); @@ -203,16 +251,18 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { // return gObject; // } /** - * destroys this PhysicsGhostNode and removes it from memory + * Has no effect. */ public void destroy() { } /** - * Another Object is overlapping with this GhostNode, - * if and if only there CollisionShapes overlaps. - * They could be both regular PhysicsRigidBodys or PhysicsGhostObjects. - * @return All CollisionObjects overlapping with this GhostNode. + * Access a list of overlapping objects. + *

+ * Another object overlaps with this one if and if only their + * CollisionShapes overlap. + * + * @return an internal list which may get reused (not null) */ public List getOverlappingObjects() { overlappingObjects.clear(); @@ -225,13 +275,19 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { protected native void getOverlappingObjects(long objectId); + /** + * This method is invoked from native code. + * + * @param co the collision object to add + */ private void addOverlappingObject_native(PhysicsCollisionObject co) { overlappingObjects.add(co); } /** + * Count how many CollisionObjects this object overlaps. * - * @return With how many other CollisionObjects this GhostNode is currently overlapping. + * @return count (≥0) */ public int getOverlappingCount() { return getOverlappingCount(objectId); @@ -240,44 +296,84 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { private native int getOverlappingCount(long objectId); /** + * Access an overlapping collision object by its position in the list. * - * @param index The index of the overlapping Node to retrieve. - * @return The Overlapping CollisionObject at the given index. + * @param index which list position (≥0, <count) + * @return the pre-existing object */ public PhysicsCollisionObject getOverlapping(int index) { return overlappingObjects.get(index); } + /** + * Alter the continuous collision detection (CCD) swept sphere radius for + * this object. + * + * @param radius (≥0) + */ public void setCcdSweptSphereRadius(float radius) { setCcdSweptSphereRadius(objectId, radius); } private native void setCcdSweptSphereRadius(long objectId, float radius); + /** + * Alter the amount of motion required to trigger continuous collision + * detection (CCD). + *

+ * This addresses the issue of fast objects passing through other objects + * with no collision detected. + * + * @param threshold the desired threshold value (squared velocity, >0) or + * zero to disable CCD (default=0) + */ public void setCcdMotionThreshold(float threshold) { setCcdMotionThreshold(objectId, threshold); } private native void setCcdMotionThreshold(long objectId, float threshold); + /** + * Read the radius of the sphere used for continuous collision detection + * (CCD). + * + * @return radius (≥0) + */ public float getCcdSweptSphereRadius() { return getCcdSweptSphereRadius(objectId); } private native float getCcdSweptSphereRadius(long objectId); + /** + * Read the continuous collision detection (CCD) motion threshold for this + * object. + * + * @return threshold value (squared velocity, ≥0) + */ public float getCcdMotionThreshold() { return getCcdMotionThreshold(objectId); } private native float getCcdMotionThreshold(long objectId); + /** + * Read the CCD square motion threshold for this object. + * + * @return threshold value (≥0) + */ public float getCcdSquareMotionThreshold() { return getCcdSquareMotionThreshold(objectId); } private native float getCcdSquareMotionThreshold(long objectId); + /** + * Serialize this object, for example when saving to a J3O file. + * + * @param e exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter e) throws IOException { super.write(e); @@ -288,6 +384,13 @@ public class PhysicsGhostObject extends PhysicsCollisionObject { capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0); } + /** + * De-serialize this object from the specified importer, for example when + * loading from a J3O file. + * + * @param e importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter e) throws IOException { super.read(e); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java index 1330ae09d..80e3728f1 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsRigidBody.java @@ -51,29 +51,56 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - *

PhysicsRigidBody - Basic physics object

+ * A collision object for a rigid body, based on Bullet's btRigidBody. + * * @author normenhansen */ public class PhysicsRigidBody extends PhysicsCollisionObject { + /** + * motion state + */ protected RigidBodyMotionState motionState = new RigidBodyMotionState(); + /** + * copy of mass (>0) of a dynamic body, or 0 for a static body + * (default=1) + */ protected float mass = 1.0f; + /** + * copy of kinematic flag: true→set kinematic mode (spatial controls + * body), false→dynamic/static mode (body controls spatial) + * (default=false) + */ protected boolean kinematic = false; + /** + * joint list + */ protected ArrayList joints = new ArrayList(); + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public PhysicsRigidBody() { } /** - * Creates a new PhysicsRigidBody with the supplied collision shape - * @param child - * @param shape + * Instantiate a dynamic body with mass=1 and the specified collision shape. + * + * @param shape the desired shape (not null, alias created) */ public PhysicsRigidBody(CollisionShape shape) { collisionShape = shape; rebuildRigidBody(); } + /** + * Instantiate a body with the specified collision shape and mass. + * + * @param shape the desired shape (not null, alias created) + * @param mass if 0, a static body is created; otherwise a dynamic body is + * created (≥0) + */ public PhysicsRigidBody(CollisionShape shape, float mass) { collisionShape = shape; this.mass = mass; @@ -81,7 +108,7 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { } /** - * Builds/rebuilds the phyiscs body when parameters have changed + * Build/rebuild this body after parameters have changed. */ protected void rebuildRigidBody() { boolean removed = false; @@ -105,11 +132,17 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { } } + /** + * For use by subclasses. + */ protected void preRebuild() { } private native long createRigidBody(float mass, long motionStateId, long collisionShapeId); + /** + * For use by subclasses. + */ protected void postRebuild() { if (mass == 0.0f) { setStatic(objectId, true); @@ -120,12 +153,19 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { } /** - * @return the motionState + * Access this body's motion state. + * + * @return the pre-existing instance */ public RigidBodyMotionState getMotionState() { return motionState; } + /** + * Test whether this body is in a physics space. + * + * @return true→in a space, false→not in a space + */ public boolean isInWorld() { return isInWorld(objectId); } @@ -133,8 +173,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native boolean isInWorld(long objectId); /** - * Sets the physics object location - * @param location the location of the actual physics object + * Directly alter the location of this body's center of mass. + * + * @param location the desired location (not null, unaffected) */ public void setPhysicsLocation(Vector3f location) { setPhysicsLocation(objectId, location); @@ -143,8 +184,10 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setPhysicsLocation(long objectId, Vector3f location); /** - * Sets the physics object rotation - * @param rotation the rotation of the actual physics object + * Directly alter this body's orientation. + * + * @param rotation the desired orientation (rotation matrix, not null, + * unaffected) */ public void setPhysicsRotation(Matrix3f rotation) { setPhysicsRotation(objectId, rotation); @@ -153,8 +196,10 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setPhysicsRotation(long objectId, Matrix3f rotation); /** - * Sets the physics object rotation - * @param rotation the rotation of the actual physics object + * Directly alter this body's orientation. + * + * @param rotation the desired orientation (quaternion, not null, + * unaffected) */ public void setPhysicsRotation(Quaternion rotation) { setPhysicsRotation(objectId, rotation); @@ -163,7 +208,11 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setPhysicsRotation(long objectId, Quaternion rotation); /** - * @return the physicsLocation + * Copy the location of this body's center of mass. + * + * @param trans storage for the result (modified if not null) + * @return the location (either the provided storage or a new vector, not + * null) */ public Vector3f getPhysicsLocation(Vector3f trans) { if (trans == null) { @@ -176,7 +225,11 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getPhysicsLocation(long objectId, Vector3f vector); /** - * @return the physicsLocation + * Copy this body's orientation to a quaternion. + * + * @param rot storage for the result (modified if not null) + * @return the orientation (either the provided storage or a new quaternion, + * not null) */ public Quaternion getPhysicsRotation(Quaternion rot) { if (rot == null) { @@ -185,12 +238,23 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { getPhysicsRotation(objectId, rot); return rot; } - + + /** + * Alter the principal components of the local inertia tensor. + * + * @param gravity (not null, unaffected) + */ public void setInverseInertiaLocal(Vector3f gravity) { setInverseInertiaLocal(objectId, gravity); } private native void setInverseInertiaLocal(long objectId, Vector3f gravity); - + + /** + * Read the principal components of the local inverse inertia tensor. + * + * @param trans storage for the result (modified if not null) + * @return a vector (either the provided storage or a new vector, not null) + */ public Vector3f getInverseInertiaLocal(Vector3f trans) { if (trans == null) { trans = new Vector3f(); @@ -204,7 +268,11 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getPhysicsRotation(long objectId, Quaternion rot); /** - * @return the physicsLocation + * Copy this body's orientation to a matrix. + * + * @param rot storage for the result (modified if not null) + * @return the orientation (either the provided storage or a new matrix, not + * null) */ public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) { if (rot == null) { @@ -217,7 +285,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getPhysicsRotationMatrix(long objectId, Matrix3f rot); /** - * @return the physicsLocation + * Copy the location of this body's center of mass. + * + * @return a new location vector (not null) */ public Vector3f getPhysicsLocation() { Vector3f vec = new Vector3f(); @@ -226,7 +296,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { } /** - * @return the physicsLocation + * Copy this body's orientation to a quaternion. + * + * @return a new quaternion (not null) */ public Quaternion getPhysicsRotation() { Quaternion quat = new Quaternion(); @@ -234,6 +306,11 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { return quat; } + /** + * Copy this body's orientation to a matrix. + * + * @return a new matrix (not null) + */ public Matrix3f getPhysicsRotationMatrix() { Matrix3f mtx = new Matrix3f(); getPhysicsRotationMatrix(objectId, mtx); @@ -264,10 +341,14 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { // return Converter.convert(tempTrans.basis, rotation); // } /** - * Sets the node to kinematic mode. in this mode the node is not affected by physics - * but affects other physics objects. Its kinetic force is calculated by the amount - * of movement it is exposed to and its weight. - * @param kinematic + * Put this body into kinematic mode or take it out of kinematic mode. + *

+ * In kinematic mode, the body is not influenced by physics but can affect + * other physics objects. Its kinetic force is calculated based on its + * movement and weight. + * + * @param kinematic true→set kinematic mode, false→set + * dynamic/static mode (default=false) */ public void setKinematic(boolean kinematic) { this.kinematic = kinematic; @@ -276,10 +357,25 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setKinematic(long objectId, boolean kinematic); + /** + * Test whether this body is in kinematic mode. + *

+ * In kinematic mode, the body is not influenced by physics but can affect + * other physics objects. Its kinetic force is calculated based on its + * movement and weight. + * + * @return true if in kinematic mode, otherwise false (dynamic/static mode) + */ public boolean isKinematic() { return kinematic; } + /** + * Alter the radius of the swept sphere used for continuous collision + * detection (CCD). + * + * @param radius the desired radius (≥0, default=0) + */ public void setCcdSweptSphereRadius(float radius) { setCcdSweptSphereRadius(objectId, radius); } @@ -287,9 +383,14 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setCcdSweptSphereRadius(long objectId, float radius); /** - * Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection
- * This avoids the problem of fast objects moving through other objects, set to zero to disable (default) - * @param threshold + * Alter the amount of motion required to activate continuous collision + * detection (CCD). + *

+ * This addresses the issue of fast objects passing through other objects + * with no collision detected. + * + * @param threshold the desired threshold velocity (>0) or zero to + * disable CCD (default=0) */ public void setCcdMotionThreshold(float threshold) { setCcdMotionThreshold(objectId, threshold); @@ -297,31 +398,56 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setCcdMotionThreshold(long objectId, float threshold); + /** + * Read the radius of the swept sphere used for continuous collision + * detection (CCD). + * + * @return radius (≥0) + */ public float getCcdSweptSphereRadius() { return getCcdSweptSphereRadius(objectId); } private native float getCcdSweptSphereRadius(long objectId); + /** + * Calculate this body's continuous collision detection (CCD) motion + * threshold. + * + * @return the threshold velocity (≥0) + */ public float getCcdMotionThreshold() { return getCcdMotionThreshold(objectId); } private native float getCcdMotionThreshold(long objectId); + /** + * Calculate the square of this body's continuous collision detection (CCD) + * motion threshold. + * + * @return the threshold velocity squared (≥0) + */ public float getCcdSquareMotionThreshold() { return getCcdSquareMotionThreshold(objectId); } private native float getCcdSquareMotionThreshold(long objectId); + /** + * Read this body's mass. + * + * @return the mass (>0) or zero for a static body + */ public float getMass() { return mass; } /** - * Sets the mass of this PhysicsRigidBody, objects with mass=0 are static. - * @param mass + * Alter this body's mass. Bodies with mass=0 are static. For dynamic + * bodies, it is best to keep the mass around 1. + * + * @param mass the desired mass (>0) or 0 for a static body (default=1) */ public void setMass(float mass) { this.mass = mass; @@ -344,10 +470,22 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native long updateMassProps(long objectId, long collisionShapeId, float mass); + /** + * Copy this body's gravitational acceleration. + * + * @return a new acceleration vector (not null) + */ public Vector3f getGravity() { return getGravity(null); } + /** + * Copy this body's gravitational acceleration. + * + * @param gravity storage for the result (modified if not null) + * @return an acceleration vector (either the provided storage or a new + * vector, not null) + */ public Vector3f getGravity(Vector3f gravity) { if (gravity == null) { gravity = new Vector3f(); @@ -359,16 +497,23 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getGravity(long objectId, Vector3f gravity); /** - * Set the local gravity of this PhysicsRigidBody
- * Set this after adding the node to the PhysicsSpace, - * the PhysicsSpace assigns its current gravity to the physics node when its added. - * @param gravity the gravity vector to set + * Alter this body's gravitational acceleration. + *

+ * Invoke this after adding the body to a PhysicsSpace. Adding a body to a + * PhysicsSpace alters its gravity. + * + * @param gravity the desired acceleration vector (not null, unaffected) */ public void setGravity(Vector3f gravity) { setGravity(objectId, gravity); } private native void setGravity(long objectId, Vector3f gravity); + /** + * Read this body's friction. + * + * @return friction value + */ public float getFriction() { return getFriction(objectId); } @@ -376,8 +521,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native float getFriction(long objectId); /** - * Sets the friction of this physics object - * @param friction the friction of this physics object + * Alter this body's friction. + * + * @param friction the desired friction value (default=0.5) */ public void setFriction(float friction) { setFriction(objectId, friction); @@ -385,6 +531,12 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setFriction(long objectId, float friction); + /** + * Alter this body's damping. + * + * @param linearDamping the desired linear damping value (default=0) + * @param angularDamping the desired angular damping value (default=0) + */ public void setDamping(float linearDamping, float angularDamping) { setDamping(objectId, linearDamping, angularDamping); } @@ -400,27 +552,52 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { // // private native void setRestitution(long objectId, float factor); // + /** + * Alter this body's linear damping. + * + * @param linearDamping the desired linear damping value (default=0) + */ public void setLinearDamping(float linearDamping) { setDamping(objectId, linearDamping, getAngularDamping()); } - + + /** + * Alter this body's angular damping. + * + * @param angularDamping the desired angular damping value (default=0) + */ public void setAngularDamping(float angularDamping) { setAngularDamping(objectId, angularDamping); } private native void setAngularDamping(long objectId, float factor); + /** + * Read this body's linear damping. + * + * @return damping value + */ public float getLinearDamping() { return getLinearDamping(objectId); } private native float getLinearDamping(long objectId); + /** + * Read this body's angular damping. + * + * @return damping value + */ public float getAngularDamping() { return getAngularDamping(objectId); } private native float getAngularDamping(long objectId); + /** + * Read this body's restitution (bounciness). + * + * @return restitution value + */ public float getRestitution() { return getRestitution(objectId); } @@ -428,8 +605,10 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native float getRestitution(long objectId); /** - * The "bouncyness" of the PhysicsRigidBody, best performance if restitution=0 - * @param restitution + * Alter this body's restitution (bounciness). For best performance, set + * restitution=0. + * + * @param restitution the desired value (default=0) */ public void setRestitution(float restitution) { setRestitution(objectId, restitution); @@ -438,8 +617,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setRestitution(long objectId, float factor); /** - * Get the current angular velocity of this PhysicsRigidBody - * @return the current linear velocity + * Copy this body's angular velocity. + * + * @return a new velocity vector (not null) */ public Vector3f getAngularVelocity() { Vector3f vec = new Vector3f(); @@ -450,16 +630,18 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getAngularVelocity(long objectId, Vector3f vec); /** - * Get the current angular velocity of this PhysicsRigidBody - * @param vec the vector to store the velocity in + * Copy this body's angular velocity. + * + * @param vec storage for the result (not null, modified) */ public void getAngularVelocity(Vector3f vec) { getAngularVelocity(objectId, vec); } /** - * Sets the angular velocity of this PhysicsRigidBody - * @param vec the angular velocity of this PhysicsRigidBody + * Alter this body's angular velocity. + * + * @param vec the desired angular velocity vector (not null, unaffected) */ public void setAngularVelocity(Vector3f vec) { setAngularVelocity(objectId, vec); @@ -469,8 +651,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setAngularVelocity(long objectId, Vector3f vec); /** - * Get the current linear velocity of this PhysicsRigidBody - * @return the current linear velocity + * Copy the linear velocity of this body's center of mass. + * + * @return a new velocity vector (not null) */ public Vector3f getLinearVelocity() { Vector3f vec = new Vector3f(); @@ -481,16 +664,18 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getLinearVelocity(long objectId, Vector3f vec); /** - * Get the current linear velocity of this PhysicsRigidBody - * @param vec the vector to store the velocity in + * Copy the linear velocity of this body's center of mass. + * + * @param vec storage for the result (not null, modified) */ public void getLinearVelocity(Vector3f vec) { getLinearVelocity(objectId, vec); } /** - * Sets the linear velocity of this PhysicsRigidBody - * @param vec the linear velocity of this PhysicsRigidBody + * Alter the linear velocity of this body's center of mass. + * + * @param vec the desired velocity vector (not null) */ public void setLinearVelocity(Vector3f vec) { setLinearVelocity(objectId, vec); @@ -500,9 +685,12 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setLinearVelocity(long objectId, Vector3f vec); /** - * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call - * updates the physics space.
- * To apply an impulse, use applyImpulse, use applyContinuousForce to apply continuous force. + * Apply a force to the PhysicsRigidBody. Effective only if the next physics + * update steps the physics space. + *

+ * To apply an impulse, use applyImpulse, use applyContinuousForce to apply + * continuous force. + * * @param force the force * @param location the location of the force */ @@ -514,10 +702,12 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void applyForce(long objectId, Vector3f force, Vector3f location); /** - * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call - * updates the physics space.
- * To apply an impulse, use applyImpulse. - * + * Apply a force to the PhysicsRigidBody. Effective only if the next physics + * update steps the physics space. + *

+ * To apply an impulse, use + * {@link #applyImpulse(com.jme3.math.Vector3f, com.jme3.math.Vector3f)}. + * * @param force the force */ public void applyCentralForce(Vector3f force) { @@ -528,10 +718,12 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void applyCentralForce(long objectId, Vector3f force); /** - * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call - * updates the physics space.
- * To apply an impulse, use applyImpulse. - * + * Apply a force to the PhysicsRigidBody. Effective only if the next physics + * update steps the physics space. + *

+ * To apply an impulse, use + * {@link #applyImpulse(com.jme3.math.Vector3f, com.jme3.math.Vector3f)}. + * * @param torque the torque */ public void applyTorque(Vector3f torque) { @@ -542,7 +734,8 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void applyTorque(long objectId, Vector3f vec); /** - * Apply an impulse to the PhysicsRigidBody in the next physics update. + * Apply an impulse to the body the next physics update. + * * @param impulse applied impulse * @param rel_pos location relative to object */ @@ -554,8 +747,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void applyImpulse(long objectId, Vector3f impulse, Vector3f rel_pos); /** - * Apply a torque impulse to the PhysicsRigidBody in the next physics update. - * @param vec + * Apply a torque impulse to the body in the next physics update. + * + * @param vec the torque to apply */ public void applyTorqueImpulse(Vector3f vec) { applyTorqueImpulse(objectId, vec); @@ -565,8 +759,7 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void applyTorqueImpulse(long objectId, Vector3f vec); /** - * Clear all forces from the PhysicsRigidBody - * + * Clear all forces acting on this body. */ public void clearForces() { clearForces(objectId); @@ -574,6 +767,14 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void clearForces(long objectId); + /** + * Apply the specified CollisionShape to this body. + *

+ * Note that the body should not be in any physics space while changing + * shape; the body gets rebuilt on the physics side. + * + * @param collisionShape the shape to apply (not null, alias created) + */ public void setCollisionShape(CollisionShape collisionShape) { super.setCollisionShape(collisionShape); if (collisionShape instanceof MeshCollisionShape && mass != 0) { @@ -590,7 +791,7 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setCollisionShape(long objectId, long collisionShapeId); /** - * reactivates this PhysicsRigidBody when it has been deactivated because it was not moving + * Reactivates this body if it has been deactivated due to lack of motion. */ public void activate() { activate(objectId); @@ -598,6 +799,11 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void activate(long objectId); + /** + * Test whether this body has been deactivated due to lack of motion. + * + * @return true if still active, false if deactivated + */ public boolean isActive() { return isActive(objectId); } @@ -605,10 +811,13 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native boolean isActive(long objectId); /** - * sets the sleeping thresholds, these define when the object gets deactivated - * to save ressources. Low values keep the object active when it barely moves - * @param linear the linear sleeping threshold - * @param angular the angular sleeping threshold + * Alter this body's sleeping thresholds. + *

+ * These thresholds determine when the body can be deactivated to save + * resources. Low values keep the body active when it barely moves. + * + * @param linear the desired linear sleeping threshold (≥0, default=0.8) + * @param angular the desired angular sleeping threshold (≥0, default=1) */ public void setSleepingThresholds(float linear, float angular) { setSleepingThresholds(objectId, linear, angular); @@ -616,36 +825,67 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void setSleepingThresholds(long objectId, float linear, float angular); + /** + * Alter this body's linear sleeping threshold. + * + * @param linearSleepingThreshold the desired threshold (≥0, default=0.8) + */ public void setLinearSleepingThreshold(float linearSleepingThreshold) { setLinearSleepingThreshold(objectId, linearSleepingThreshold); } private native void setLinearSleepingThreshold(long objectId, float linearSleepingThreshold); + /** + * Alter this body's angular sleeping threshold. + * + * @param angularSleepingThreshold the desired threshold (≥0, default=1) + */ public void setAngularSleepingThreshold(float angularSleepingThreshold) { setAngularSleepingThreshold(objectId, angularSleepingThreshold); } private native void setAngularSleepingThreshold(long objectId, float angularSleepingThreshold); + /** + * Read this body's linear sleeping threshold. + * + * @return the linear sleeping threshold (≥0) + */ public float getLinearSleepingThreshold() { return getLinearSleepingThreshold(objectId); } private native float getLinearSleepingThreshold(long objectId); + /** + * Read this body's angular sleeping threshold. + * + * @return the angular sleeping threshold (≥0) + */ public float getAngularSleepingThreshold() { return getAngularSleepingThreshold(objectId); } private native float getAngularSleepingThreshold(long objectId); + /** + * Read the X-component of this body's angular factor. + * + * @return the X-component of the angular factor + */ public float getAngularFactor() { return getAngularFactor(null).getX(); } + /** + * Copy this body's angular factors. + * + * @param store storage for the result (modified if not null) + * @return a vector (either the provided storage or a new vector, not null) + */ public Vector3f getAngularFactor(Vector3f store) { - // doing like this prevent from breaking the API + // Done this way to prevent breaking the API. if (store == null) { store = new Vector3f(); } @@ -655,16 +895,33 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getAngularFactor(long objectId, Vector3f vec); + /** + * Alter this body's angular factor. + * + * @param factor the desired angular factor for all axes (not null, + * unaffected, default=1) + */ public void setAngularFactor(float factor) { setAngularFactor(objectId, new Vector3f(factor, factor, factor)); } + /** + * Alter this body's angular factors. + * + * @param factor the desired angular factor for each axis (not null, + * unaffected, default=(1,1,1)) + */ public void setAngularFactor(Vector3f factor) { setAngularFactor(objectId, factor); } private native void setAngularFactor(long objectId, Vector3f factor); + /** + * Copy this body's linear factors. + * + * @return a new vector (not null) + */ public Vector3f getLinearFactor() { Vector3f vec = new Vector3f(); getLinearFactor(objectId, vec); @@ -673,6 +930,12 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { private native void getLinearFactor(long objectId, Vector3f vec); + /** + * Alter this body's linear factors. + * + * @param factor the desired linear factor for each axis (not null, + * unaffected, default=(1,1,1)) + */ public void setLinearFactor(Vector3f factor) { setLinearFactor(objectId, factor); } @@ -681,7 +944,9 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { /** - * do not use manually, joints are added automatically + * Do not invoke directly! Joints are added automatically when created. + * + * @param joint the joint to add (not null) */ public void addJoint(PhysicsJoint joint) { if (!joints.contains(joint)) { @@ -690,21 +955,32 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { } /** - * + * Do not invoke directly! Joints are removed automatically when destroyed. + * + * @param joint the joint to remove (not null) */ public void removeJoint(PhysicsJoint joint) { joints.remove(joint); } /** - * Returns a list of connected joints. This list is only filled when - * the PhysicsRigidBody is actually added to the physics space or loaded from disk. - * @return list of active joints connected to this PhysicsRigidBody + * Access the list of joints connected with this body. + *

+ * This list is only filled when the PhysicsRigidBody is added to a physics + * space. + * + * @return the pre-existing list (not null) */ public List getJoints() { return joints; } + /** + * Serialize this body, for example when saving to a J3O file. + * + * @param e exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter e) throws IOException { super.write(e); @@ -738,6 +1014,12 @@ public class PhysicsRigidBody extends PhysicsCollisionObject { capsule.writeSavableArrayList(joints, "joints", null); } + /** + * De-serialize this body, for example when loading from a J3O file. + * + * @param e importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter e) throws IOException { super.read(e); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java index d18fd0401..c59884102 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/objects/PhysicsVehicle.java @@ -46,34 +46,68 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - *

PhysicsVehicleNode - Special PhysicsNode that implements vehicle functions

+ * A collision object for simplified vehicle simulation based on Bullet's + * btRaycastVehicle. *

- * From bullet manual:
- * For most vehicle simulations, it is recommended to use the simplified Bullet - * vehicle model as provided in btRaycastVehicle. Instead of simulation each wheel - * and chassis as separate rigid bodies, connected by constraints, it uses a simplified model. - * This simplified model has many benefits, and is widely used in commercial driving games.
- * The entire vehicle is represented as a single rigidbody, the chassis. - * The collision detection of the wheels is approximated by ray casts, - * and the tire friction is a basic anisotropic friction model. - *

+ * From Bullet manual:
+ * For arcade style vehicle simulations, it is recommended to use the simplified + * Bullet vehicle model as provided in btRaycastVehicle. Instead of simulation + * each wheel and chassis as separate rigid bodies, connected by constraints, it + * uses a simplified model. This simplified model has many benefits, and is + * widely used in commercial driving games. + *

+ * The entire vehicle is represented as a single rigidbody, the chassis. The + * collision detection of the wheels is approximated by ray casts, and the tire + * friction is a basic anisotropic friction model. + * * @author normenhansen */ public class PhysicsVehicle extends PhysicsRigidBody { + /** + * Unique identifier of the btRaycastVehicle. The constructor sets this to a + * non-zero value. + */ protected long vehicleId = 0; + /** + * Unique identifier of the ray caster. + */ protected long rayCasterId = 0; + /** + * tuning parameters + */ protected VehicleTuning tuning = new VehicleTuning(); + /** + * list of wheels + */ protected ArrayList wheels = new ArrayList(); + /** + * physics space where this vehicle is added, or null if none + */ protected PhysicsSpace physicsSpace; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public PhysicsVehicle() { } + /** + * Instantiate a vehicle with the specified collision shape and mass=1. + * + * @param shape the desired shape (not null, alias created) + */ public PhysicsVehicle(CollisionShape shape) { super(shape); } + /** + * Instantiate a vehicle with the specified collision shape and mass. + * + * @param shape the desired shape (not null, alias created) + * @param mass (>0) + */ public PhysicsVehicle(CollisionShape shape, float mass) { super(shape, mass); } @@ -111,7 +145,10 @@ public class PhysicsVehicle extends PhysicsRigidBody { } /** - * Used internally, creates the actual vehicle constraint when vehicle is added to phyicsspace + * Used internally, creates the actual vehicle constraint when vehicle is + * added to physics space. + * + * @param space which physics space */ public void createVehicle(PhysicsSpace space) { physicsSpace = space; @@ -145,14 +182,20 @@ public class PhysicsVehicle extends PhysicsRigidBody { private native int addWheel(long objectId, Vector3f location, Vector3f direction, Vector3f axle, float restLength, float radius, VehicleTuning tuning, boolean frontWheel); /** - * Add a wheel to this vehicle - * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space) - * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car) - * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car) - * @param suspensionRestLength The current length of the suspension (metres) - * @param wheelRadius the wheel radius - * @param isFrontWheel sets if this wheel is a front wheel (steering) - * @return the PhysicsVehicleWheel object to get/set infos on the wheel + * Add a wheel to this vehicle. + * + * @param connectionPoint the location where the suspension connects to the + * chassis (in chassis coordinates, not null, unaffected) + * @param direction the suspension direction (in chassis coordinates, not + * null, unaffected, typically down/0,-1,0) + * @param axle the axis direction (in chassis coordinates, not null, + * unaffected, typically -1,0,0) + * @param suspensionRestLength the rest length of the suspension (in + * physics-space units) + * @param wheelRadius the wheel radius (in physics-space units, >0) + * @param isFrontWheel true→front (steering) wheel, + * false→non-front wheel + * @return a new VehicleWheel for access (not null) */ public VehicleWheel addWheel(Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) { return addWheel(null, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel); @@ -160,14 +203,20 @@ public class PhysicsVehicle extends PhysicsRigidBody { /** * Add a wheel to this vehicle - * @param spat the wheel Geometry - * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space) - * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car) - * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car) - * @param suspensionRestLength The current length of the suspension (metres) - * @param wheelRadius the wheel radius - * @param isFrontWheel sets if this wheel is a front wheel (steering) - * @return the PhysicsVehicleWheel object to get/set infos on the wheel + * + * @param spat the associated spatial, or null if none + * @param connectionPoint the location where the suspension connects to the + * chassis (in chassis coordinates, not null, unaffected) + * @param direction the suspension direction (in chassis coordinates, not + * null, unaffected, typically down/0,-1,0) + * @param axle the axis direction (in chassis coordinates, not null, + * unaffected, typically -1,0,0) + * @param suspensionRestLength the rest length of the suspension (in + * physics-space units) + * @param wheelRadius the wheel radius (in physics-space units, >0) + * @param isFrontWheel true→front (steering) wheel, + * false→non-front wheel + * @return a new VehicleWheel for access (not null) */ public VehicleWheel addWheel(Spatial spat, Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) { VehicleWheel wheel = null; @@ -190,8 +239,9 @@ public class PhysicsVehicle extends PhysicsRigidBody { } /** - * This rebuilds the vehicle as there is no way in bullet to remove a wheel. - * @param wheel + * Remove a wheel. + * + * @param wheel the index of the wheel to remove (≥0) */ public void removeWheel(int wheel) { wheels.remove(wheel); @@ -200,195 +250,276 @@ public class PhysicsVehicle extends PhysicsRigidBody { } /** - * You can get access to the single wheels via this method. - * @param wheel the wheel index - * @return the WheelInfo of the selected wheel + * Access the indexed wheel of this vehicle. + * + * @param wheel the index of the wheel to access (≥0, <count) + * @return the pre-existing instance */ public VehicleWheel getWheel(int wheel) { return wheels.get(wheel); } + /** + * Read the number of wheels on this vehicle. + * + * @return count (≥0) + */ public int getNumWheels() { return wheels.size(); } /** - * @return the frictionSlip + * Read the initial friction for new wheels. + * + * @return the coefficient of friction between tyre and ground + * (0.8→realistic car, 10000→kart racer) */ public float getFrictionSlip() { return tuning.frictionSlip; } /** - * Use before adding wheels, this is the default used when adding wheels. - * After adding the wheel, use direct wheel access.
- * The coefficient of friction between the tyre and the ground. - * Should be about 0.8 for realistic cars, but can increased for better handling. - * Set large (10000.0) for kart racers - * @param frictionSlip the frictionSlip to set + * Alter the initial friction for new wheels. Effective only before adding + * wheels. After adding a wheel, use {@link #setFrictionSlip(int, float)}. + *

+ * For better handling, increase the friction. + * + * @param frictionSlip the desired coefficient of friction between tyre and + * ground (0.8→realistic car, 10000→kart racer, default=10.5) */ public void setFrictionSlip(float frictionSlip) { tuning.frictionSlip = frictionSlip; } /** - * The coefficient of friction between the tyre and the ground. - * Should be about 0.8 for realistic cars, but can increased for better handling. - * Set large (10000.0) for kart racers - * @param wheel - * @param frictionSlip + * Alter the friction of the indexed wheel. + *

+ * For better handling, increase the friction. + * + * @param wheel the index of the wheel to modify (≥0) + * @param frictionSlip the desired coefficient of friction between tyre and + * ground (0.8→realistic car, 10000→kart racer) */ public void setFrictionSlip(int wheel, float frictionSlip) { wheels.get(wheel).setFrictionSlip(frictionSlip); } /** - * Reduces the rolling torque applied from the wheels that cause the vehicle to roll over. - * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour. - * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over. - * You should also try lowering the vehicle's centre of mass + * Alter the roll influence of the indexed wheel. + *

+ * The roll-influence factor reduces (or magnifies) any torque contributed + * by the wheel that would tend to cause the vehicle to roll over. This is a + * bit of a hack, but it's quite effective. + *

+ * If the friction between the tyres and the ground is too high, you may + * reduce this factor to prevent the vehicle from rolling over. You should + * also try lowering the vehicle's center of mass. + * + * @param wheel the index of the wheel to modify (≥0) + * @param rollInfluence the desired roll-influence factor (0→no roll + * torque, 1→realistic behavior, default=1) */ public void setRollInfluence(int wheel, float rollInfluence) { wheels.get(wheel).setRollInfluence(rollInfluence); } /** - * @return the maxSuspensionTravelCm + * Read the initial maximum suspension travel distance for new wheels. + * + * @return the maximum distance the suspension can be compressed (in + * centimeters) */ public float getMaxSuspensionTravelCm() { return tuning.maxSuspensionTravelCm; } /** - * Use before adding wheels, this is the default used when adding wheels. - * After adding the wheel, use direct wheel access.
- * The maximum distance the suspension can be compressed (centimetres) - * @param maxSuspensionTravelCm the maxSuspensionTravelCm to set + * Alter the initial maximum suspension travel distance for new wheels. + * Effective only before adding wheels. After adding a wheel, use + * {@link #setMaxSuspensionTravelCm(int, float)}. + * + * @param maxSuspensionTravelCm the desired maximum distance the suspension + * can be compressed (in centimeters, default=500) */ public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) { tuning.maxSuspensionTravelCm = maxSuspensionTravelCm; } /** - * The maximum distance the suspension can be compressed (centimetres) - * @param wheel - * @param maxSuspensionTravelCm + * Alter the maximum suspension travel distance for the indexed wheel. + * + * @param wheel the index of the wheel to modify (≥0) + * @param maxSuspensionTravelCm the desired maximum distance the suspension + * can be compressed (in centimeters) */ public void setMaxSuspensionTravelCm(int wheel, float maxSuspensionTravelCm) { wheels.get(wheel).setMaxSuspensionTravelCm(maxSuspensionTravelCm); } + /** + * Read the initial maximum suspension force for new wheels. + * + * @return the maximum force per wheel + */ public float getMaxSuspensionForce() { return tuning.maxSuspensionForce; } /** - * This value caps the maximum suspension force, raise this above the default 6000 if your suspension cannot - * handle the weight of your vehicle. - * @param maxSuspensionForce + * Alter the initial maximum suspension force for new wheels. Effective only + * before adding wheels. After adding a wheel, use + * {@link #setMaxSuspensionForce(int, float)}. + *

+ * If the suspension cannot handle the vehicle's weight, increase this + * limit. + * + * @param maxSuspensionForce the desired maximum force per wheel + * (default=6000) */ public void setMaxSuspensionForce(float maxSuspensionForce) { tuning.maxSuspensionForce = maxSuspensionForce; } /** - * This value caps the maximum suspension force, raise this above the default 6000 if your suspension cannot - * handle the weight of your vehicle. - * @param wheel - * @param maxSuspensionForce + * Alter the maximum suspension force for the specified wheel. + *

+ * If the suspension cannot handle the vehicle's weight, increase this + * limit. + * + * @param wheel the index of the wheel to modify (≥0) + * @param maxSuspensionForce the desired maximum force per wheel + * (default=6000) */ public void setMaxSuspensionForce(int wheel, float maxSuspensionForce) { wheels.get(wheel).setMaxSuspensionForce(maxSuspensionForce); } /** - * @return the suspensionCompression + * Read the initial damping (when the suspension is compressed) for new + * wheels. + * + * @return the damping coefficient */ public float getSuspensionCompression() { return tuning.suspensionCompression; } /** - * Use before adding wheels, this is the default used when adding wheels. - * After adding the wheel, use direct wheel access.
- * The damping coefficient for when the suspension is compressed. - * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.
- * k = 0.0 undamped & bouncy, k = 1.0 critical damping
- * 0.1 to 0.3 are good values - * @param suspensionCompression the suspensionCompression to set + * Alter the initial damping (when the suspension is compressed) for new + * wheels. Effective only before adding wheels. After adding a wheel, use + * {@link #setSuspensionCompression(int, float)}. + *

+ * Set to k * 2 * FastMath.sqrt(m_suspensionStiffness) where k is the + * damping ratio: + *

+ * k = 0.0 undamped and bouncy, k = 1.0 critical damping, k between 0.1 and + * 0.3 are good values + * + * @param suspensionCompression the desired damping coefficient + * (default=0.83) */ public void setSuspensionCompression(float suspensionCompression) { tuning.suspensionCompression = suspensionCompression; } /** - * The damping coefficient for when the suspension is compressed. - * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.
- * k = 0.0 undamped & bouncy, k = 1.0 critical damping
- * 0.1 to 0.3 are good values - * @param wheel - * @param suspensionCompression + * Alter the damping (when the suspension is compressed) for the indexed + * wheel. + *

+ * Set to k * 2 * FastMath.sqrt(m_suspensionStiffness) where k is the + * damping ratio: + *

+ * k = 0.0 undamped and bouncy, k = 1.0 critical damping, k between 0.1 and + * 0.3 are good values + * + * @param wheel the index of the wheel to modify (≥0) + * @param suspensionCompression the desired damping coefficient */ public void setSuspensionCompression(int wheel, float suspensionCompression) { wheels.get(wheel).setWheelsDampingCompression(suspensionCompression); } /** - * @return the suspensionDamping + * Read the initial damping (when the suspension is expanded) for new + * wheels. + * + * @return the damping coefficient */ public float getSuspensionDamping() { return tuning.suspensionDamping; } /** - * Use before adding wheels, this is the default used when adding wheels. - * After adding the wheel, use direct wheel access.
- * The damping coefficient for when the suspension is expanding. - * See the comments for setSuspensionCompression for how to set k. - * @param suspensionDamping the suspensionDamping to set + * Alter the initial damping (when the suspension is expanded) for new + * wheels. Effective only before adding wheels. After adding a wheel, use + * {@link #setSuspensionCompression(int, float)}. + *

+ * Set to k * 2 * FastMath.sqrt(m_suspensionStiffness) where k is the + * damping ratio: + *

+ * k = 0.0 undamped and bouncy, k = 1.0 critical damping, k between 0.1 and + * 0.3 are good values + * + * @param suspensionDamping the desired damping coefficient (default=0.88) */ public void setSuspensionDamping(float suspensionDamping) { tuning.suspensionDamping = suspensionDamping; } /** - * The damping coefficient for when the suspension is expanding. - * See the comments for setSuspensionCompression for how to set k. - * @param wheel - * @param suspensionDamping + * Alter the damping (when the suspension is expanded) for the indexed + * wheel. + *

+ * Set to k * 2 * FastMath.sqrt(m_suspensionStiffness) where k is the + * damping ratio: + *

+ * k = 0.0 undamped and bouncy, k = 1.0 critical damping, k between 0.1 and + * 0.3 are good values + * + * @param wheel the index of the wheel to modify (≥0) + * @param suspensionDamping the desired damping coefficient */ public void setSuspensionDamping(int wheel, float suspensionDamping) { wheels.get(wheel).setWheelsDampingRelaxation(suspensionDamping); } /** - * @return the suspensionStiffness + * Read the initial suspension stiffness for new wheels. + * + * @return the stiffness constant (10→off-road buggy, 50→sports + * car, 200→Formula-1 race car) */ public float getSuspensionStiffness() { return tuning.suspensionStiffness; } /** - * Use before adding wheels, this is the default used when adding wheels. - * After adding the wheel, use direct wheel access.
- * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car - * @param suspensionStiffness + * Alter the initial suspension stiffness for new wheels. Effective only + * before adding wheels. After adding a wheel, use + * {@link #setSuspensionStiffness(int, float)}. + * + * @param suspensionStiffness the desired stiffness coefficient + * (10→off-road buggy, 50→sports car, 200→Formula-1 race car, + * default=5.88) */ public void setSuspensionStiffness(float suspensionStiffness) { tuning.suspensionStiffness = suspensionStiffness; } /** - * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car - * @param wheel - * @param suspensionStiffness + * Alter the suspension stiffness of the indexed wheel. + * + * @param wheel the index of the wheel to modify (≥0) + * @param suspensionStiffness the desired stiffness coefficient + * (10→off-road buggy, 50→sports car, 200→Formula-1 race car, + * default=5.88) */ public void setSuspensionStiffness(int wheel, float suspensionStiffness) { wheels.get(wheel).setSuspensionStiffness(suspensionStiffness); } /** - * Reset the suspension + * Reset this vehicle's suspension. */ public void resetSuspension() { resetSuspension(vehicleId); @@ -397,8 +528,9 @@ public class PhysicsVehicle extends PhysicsRigidBody { private native void resetSuspension(long vehicleId); /** - * Apply the given engine force to all wheels, works continuously - * @param force the force + * Apply the specified engine force to all wheels. Works continuously. + * + * @param force the desired amount of force */ public void accelerate(float force) { for (int i = 0; i < wheels.size(); i++) { @@ -407,9 +539,10 @@ public class PhysicsVehicle extends PhysicsRigidBody { } /** - * Apply the given engine force, works continuously - * @param wheel the wheel to apply the force on - * @param force the force + * Apply the given engine force to the indexed wheel. Works continuously. + * + * @param wheel the index of the wheel to apply the force to (≥0) + * @param force the desired amount of force */ public void accelerate(int wheel, float force) { applyEngineForce(vehicleId, wheel, force); @@ -419,8 +552,9 @@ public class PhysicsVehicle extends PhysicsRigidBody { private native void applyEngineForce(long vehicleId, int wheel, float force); /** - * Set the given steering value to all front wheels (0 = forward) - * @param value the steering angle of the front wheels (Pi = 360deg) + * Alter the steering angle of all front wheels. + * + * @param value the desired steering angle (in radians, 0=straight) */ public void steer(float value) { for (int i = 0; i < wheels.size(); i++) { @@ -431,9 +565,10 @@ public class PhysicsVehicle extends PhysicsRigidBody { } /** - * Set the given steering value to the given wheel (0 = forward) - * @param wheel the wheel to set the steering on - * @param value the steering angle of the front wheels (Pi = 360deg) + * Alter the steering angle of the indexed wheel. + * + * @param wheel the index of the wheel to steer (≥0) + * @param value the desired steering angle (in radians, 0=straight) */ public void steer(int wheel, float value) { steer(vehicleId, wheel, value); @@ -442,8 +577,9 @@ public class PhysicsVehicle extends PhysicsRigidBody { private native void steer(long vehicleId, int wheel, float value); /** - * Apply the given brake force to all wheels, works continuously - * @param force the force + * Apply the given brake force to all wheels. Works continuously. + * + * @param force the desired amount of force */ public void brake(float force) { for (int i = 0; i < wheels.size(); i++) { @@ -452,9 +588,10 @@ public class PhysicsVehicle extends PhysicsRigidBody { } /** - * Apply the given brake force, works continuously - * @param wheel the wheel to apply the force on - * @param force the force + * Apply the given brake force to the indexed wheel. Works continuously. + * + * @param wheel the index of the wheel to apply the force to (≥0) + * @param force the desired amount of force */ public void brake(int wheel, float force) { brake(vehicleId, wheel, force); @@ -463,8 +600,9 @@ public class PhysicsVehicle extends PhysicsRigidBody { private native void brake(long vehicleId, int wheel, float force); /** - * Get the current speed of the vehicle in km/h - * @return + * Read the vehicle's speed in km/h. + * + * @return speed (in kilometers per hour) */ public float getCurrentVehicleSpeedKmHour() { return getCurrentVehicleSpeedKmHour(vehicleId); @@ -473,9 +611,11 @@ public class PhysicsVehicle extends PhysicsRigidBody { private native float getCurrentVehicleSpeedKmHour(long vehicleId); /** - * Get the current forward vector of the vehicle in world coordinates - * @param vector - * @return + * Copy the vehicle's forward direction. + * + * @param vector storage for the result (modified if not null) + * @return a direction vector (in physics-space coordinates, either the + * provided storage or a new vector) */ public Vector3f getForwardVector(Vector3f vector) { if (vector == null) { @@ -489,11 +629,19 @@ public class PhysicsVehicle extends PhysicsRigidBody { /** * used internally + * + * @return the unique identifier */ public long getVehicleId() { return vehicleId; } + /** + * De-serialize this vehicle, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { InputCapsule capsule = im.getCapsule(this); @@ -509,6 +657,12 @@ public class PhysicsVehicle extends PhysicsRigidBody { super.read(im); } + /** + * Serialize this vehicle, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { OutputCapsule capsule = ex.getCapsule(this); @@ -522,6 +676,12 @@ public class PhysicsVehicle extends PhysicsRigidBody { super.write(ex); } + /** + * Finalize this vehicle just before it is destroyed. Should be invoked only + * by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java b/jme3-bullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java index 1c7820907..25fa2c103 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/objects/VehicleWheel.java @@ -40,42 +40,142 @@ import com.jme3.scene.Spatial; import java.io.IOException; /** - * Stores info about one wheel of a PhysicsVehicle + * Information about one wheel of a PhysicsVehicle. + * * @author normenhansen */ public class VehicleWheel implements Savable { + /** + * unique identifier of the btRaycastVehicle + */ protected long wheelId = 0; + /** + * 0-origin index among the vehicle's wheels (≥0) + */ protected int wheelIndex = 0; + /** + * copy of wheel type: true→front (steering) wheel, + * false→non-front wheel + */ protected boolean frontWheel; + /** + * location where the suspension connects to the chassis (in chassis + * coordinates) + */ protected Vector3f location = new Vector3f(); + /** + * suspension direction (in chassis coordinates, typically down/0,-1,0) + */ protected Vector3f direction = new Vector3f(); + /** + * axis direction (in chassis coordinates, typically to the right/-1,0,0) + */ protected Vector3f axle = new Vector3f(); + /** + * copy of suspension stiffness constant (10→off-road buggy, + * 50→sports car, 200→Formula-1 race car, default=20) + */ protected float suspensionStiffness = 20.0f; + /** + * copy of suspension damping when expanded (0→no damping, default=2.3) + */ protected float wheelsDampingRelaxation = 2.3f; + /** + * copy of suspension damping when compressed (0→no damping, + * default=4.4) + */ protected float wheelsDampingCompression = 4.4f; + /** + * copy of coefficient of friction between tyre and ground + * (0.8→realistic car, 10000→kart racer, default=10.5) + */ protected float frictionSlip = 10.5f; + /** + * copy of roll-influence factor (0→no roll torque, 1→realistic + * behavior, default=1) + */ protected float rollInfluence = 1.0f; + /** + * copy of maximum suspension travel distance (in centimeters, default=500) + */ protected float maxSuspensionTravelCm = 500f; + /** + * copy of maximum force exerted by the suspension (default=6000) + */ protected float maxSuspensionForce = 6000f; + /** + * copy of wheel radius (in physics-space units, >0) + */ protected float radius = 0.5f; + /** + * copy of rest length of the suspension (in physics-space units) + */ protected float restLength = 1f; + /** + * wheel location in physics-space coordinates + */ protected Vector3f wheelWorldLocation = new Vector3f(); + /** + * wheel orientation in physics-space coordinates + */ protected Quaternion wheelWorldRotation = new Quaternion(); + /** + * associated spatial, or null if none + */ protected Spatial wheelSpatial; protected Matrix3f tmp_Matrix = new com.jme3.math.Matrix3f(); protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); + /** + * true → physics coordinates match local transform, false → + * physics coordinates match world transform + */ private boolean applyLocal = false; + /** + * No-argument constructor needed by SavableClassUtil. Do not invoke + * directly! + */ public VehicleWheel() { } + /** + * Instantiate a wheel. + * + * @param spat the associated spatial, or null if none + * @param location the location where the suspension connects to the chassis + * (in chassis coordinates, not null, unaffected) + * @param direction the suspension direction (in chassis coordinates, not + * null, unaffected, typically down/0,-1,0) + * @param axle the axis direction (in chassis coordinates, not null, + * unaffected, typically right/-1,0,0) + * @param restLength the rest length of the suspension (in physics-space + * units) + * @param radius the wheel's radius (in physics-space units, ≥0) + * @param frontWheel true→front (steering) wheel, false→non-front + * wheel + */ public VehicleWheel(Spatial spat, Vector3f location, Vector3f direction, Vector3f axle, float restLength, float radius, boolean frontWheel) { this(location, direction, axle, restLength, radius, frontWheel); wheelSpatial = spat; } + /** + * Instantiate a wheel without an associated spatial. + * + * @param location the location where the suspension connects to the chassis + * (in chassis coordinates, not null, unaffected) + * @param direction the suspension direction (in chassis coordinates, not + * null, unaffected, typically down/0,-1,0) + * @param axle the axis direction (in chassis coordinates, not null, + * unaffected, typically right/-1,0,0) + * @param restLength the rest length of the suspension (in physics-space + * units) + * @param radius the wheel's radius (in physics-space units, ≥0) + * @param frontWheel true→front (steering) wheel, false→non-front + * wheel + */ public VehicleWheel(Vector3f location, Vector3f direction, Vector3f axle, float restLength, float radius, boolean frontWheel) { this.location.set(location); @@ -86,6 +186,9 @@ public class VehicleWheel implements Savable { this.radius = radius; } + /** + * Update this wheel's location and orientation in physics space. + */ public void updatePhysicsState() { getWheelLocation(wheelId, wheelIndex, wheelWorldLocation); getWheelRotation(wheelId, wheelIndex, tmp_Matrix); @@ -96,6 +199,10 @@ public class VehicleWheel implements Savable { private native void getWheelRotation(long vehicleId, int wheelId, Matrix3f location); + /** + * Apply this wheel's physics location and orientation to its associated + * spatial, if any. + */ public void applyWheelTransform() { if (wheelSpatial == null) { return; @@ -118,132 +225,232 @@ public class VehicleWheel implements Savable { } } + /** + * Read the id of the btRaycastVehicle. + * + * @return the unique identifier (not zero) + */ public long getWheelId() { return wheelId; } + /** + * Assign this wheel to a vehicle. + * + * @param vehicleId the id of the btRaycastVehicle (not zero) + * @param wheelIndex index among the vehicle's wheels (≥0) + */ public void setVehicleId(long vehicleId, int wheelIndex) { this.wheelId = vehicleId; this.wheelIndex = wheelIndex; applyInfo(); } + /** + * Test whether this wheel is a front wheel. + * + * @return true if front wheel, otherwise false + */ public boolean isFrontWheel() { return frontWheel; } + /** + * Alter whether this wheel is a front (steering) wheel. + * + * @param frontWheel true→front wheel, false→non-front wheel + */ public void setFrontWheel(boolean frontWheel) { this.frontWheel = frontWheel; applyInfo(); } + /** + * Access the location where the suspension connects to the chassis. + * + * @return the pre-existing location vector (in chassis coordinates, not + * null) + */ public Vector3f getLocation() { return location; } + /** + * Access this wheel's suspension direction. + * + * @return the pre-existing direction vector (in chassis coordinates, not + * null) + */ public Vector3f getDirection() { return direction; } + /** + * Access this wheel's axle direction. + * + * @return the pre-existing direction vector (not null) + */ public Vector3f getAxle() { return axle; } + /** + * Read the stiffness constant for this wheel's suspension. + * + * @return the stiffness constant + */ public float getSuspensionStiffness() { return suspensionStiffness; } /** - * the stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car - * @param suspensionStiffness + * Alter the stiffness constant for this wheel's suspension. + * + * @param suspensionStiffness the desired stiffness constant + * (10→off-road buggy, 50→sports car, 200→Formula-1 race car, + * default=20) */ public void setSuspensionStiffness(float suspensionStiffness) { this.suspensionStiffness = suspensionStiffness; applyInfo(); } + /** + * Read this wheel's damping when the suspension is expanded. + * + * @return the damping + */ public float getWheelsDampingRelaxation() { return wheelsDampingRelaxation; } /** - * the damping coefficient for when the suspension is expanding. - * See the comments for setWheelsDampingCompression for how to set k. - * @param wheelsDampingRelaxation + * Alter this wheel's damping when the suspension is expanded. + *

+ * Set to k * 2 * FastMath.sqrt(m_suspensionStiffness) where k is the + * damping ratio: + *

+ * k = 0.0 undamped and bouncy, k = 1.0 critical damping, k between 0.1 and + * 0.3 are good values + * + * @param wheelsDampingRelaxation the desired damping (default=2.3) */ public void setWheelsDampingRelaxation(float wheelsDampingRelaxation) { this.wheelsDampingRelaxation = wheelsDampingRelaxation; applyInfo(); } + /** + * Read this wheel's damping when the suspension is compressed. + * + * @return the damping + */ public float getWheelsDampingCompression() { return wheelsDampingCompression; } /** - * the damping coefficient for when the suspension is compressed. - * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.
- * k = 0.0 undamped & bouncy, k = 1.0 critical damping
- * 0.1 to 0.3 are good values - * @param wheelsDampingCompression + * Alter this wheel's damping when the suspension is compressed. + *

+ * Set to k * 2 * FastMath.sqrt(m_suspensionStiffness) where k is the + * damping ratio: + *

+ * k = 0.0 undamped and bouncy, k = 1.0 critical damping, k between 0.1 and + * 0.3 are good values + * + * @param wheelsDampingCompression the desired damping (default=4.4) */ public void setWheelsDampingCompression(float wheelsDampingCompression) { this.wheelsDampingCompression = wheelsDampingCompression; applyInfo(); } + /** + * Read the friction between this wheel's tyre and the ground. + * + * @return the coefficient of friction + */ public float getFrictionSlip() { return frictionSlip; } /** - * the coefficient of friction between the tyre and the ground. - * Should be about 0.8 for realistic cars, but can increased for better handling. - * Set large (10000.0) for kart racers - * @param frictionSlip + * Alter the friction between this wheel's tyre and the ground. + *

+ * Should be about 0.8 for realistic cars, but can increased for better + * handling. Set large (10000.0) for kart racers. + * + * @param frictionSlip the desired coefficient of friction (default=10.5) */ public void setFrictionSlip(float frictionSlip) { this.frictionSlip = frictionSlip; applyInfo(); } + /** + * Read this wheel's roll influence. + * + * @return the roll-influence factor + */ public float getRollInfluence() { return rollInfluence; } /** - * reduces the rolling torque applied from the wheels that cause the vehicle to roll over. - * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour. - * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over. - * You should also try lowering the vehicle's centre of mass - * @param rollInfluence the rollInfluence to set + * Alter this wheel's roll influence. + *

+ * The roll-influence factor reduces (or magnifies) the torque contributed + * by this wheel that tends to cause the vehicle to roll over. This is a bit + * of a hack, but it's quite effective. + *

+ * If the friction between the tyres and the ground is too high, you may + * reduce this factor to prevent the vehicle from rolling over. You should + * also try lowering the vehicle's centre of mass. + * + * @param rollInfluence the desired roll-influence factor (0→no roll + * torque, 1→realistic behaviour, default=1) */ public void setRollInfluence(float rollInfluence) { this.rollInfluence = rollInfluence; applyInfo(); } + /** + * Read the travel distance for this wheel's suspension. + * + * @return the maximum travel distance (in centimeters) + */ public float getMaxSuspensionTravelCm() { return maxSuspensionTravelCm; } /** - * the maximum distance the suspension can be compressed (centimetres) - * @param maxSuspensionTravelCm + * Alter the travel distance for this wheel's suspension. + * + * @param maxSuspensionTravelCm the desired maximum travel distance (in + * centimetres, default=500) */ public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) { this.maxSuspensionTravelCm = maxSuspensionTravelCm; applyInfo(); } + /** + * Read the maximum force exerted by this wheel's suspension. + * + * @return the maximum force + */ public float getMaxSuspensionForce() { return maxSuspensionForce; } /** - * The maximum suspension force, raise this above the default 6000 if your suspension cannot - * handle the weight of your vehicle. - * @param maxSuspensionTravelCm + * Alter the maximum force exerted by this wheel's suspension. + *

+ * Increase this if your suspension cannot handle the weight of your + * vehicle. + * + * @param maxSuspensionForce the desired maximum force (default=6000) */ public void setMaxSuspensionForce(float maxSuspensionForce) { this.maxSuspensionForce = maxSuspensionForce; @@ -269,19 +476,40 @@ public class VehicleWheel implements Savable { boolean frontWheel, float suspensionRestLength); + /** + * Read the radius of this wheel. + * + * @return the radius (in physics-space units, ≥0) + */ public float getRadius() { return radius; } + /** + * Alter the radius of this wheel. + * + * @param radius the desired radius (in physics-space units, ≥0, + * default=0.5) + */ public void setRadius(float radius) { this.radius = radius; applyInfo(); } + /** + * Read the rest length of this wheel. + * + * @return the length + */ public float getRestLength() { return restLength; } + /** + * Alter the rest length of the suspension of this wheel. + * + * @param restLength the desired length (default=1) + */ public void setRestLength(float restLength) { this.restLength = restLength; applyInfo(); @@ -303,7 +531,10 @@ public class VehicleWheel implements Savable { } /** - * returns the location where the wheel collides with the ground (world space) + * Copy the location where the wheel touches the ground. + * + * @param vec storage for the result (not null, modified) + * @return a new location vector (in physics-space coordinates, not null) */ public Vector3f getCollisionLocation(Vector3f vec) { getCollisionLocation(wheelId, wheelIndex, vec); @@ -313,7 +544,9 @@ public class VehicleWheel implements Savable { private native void getCollisionLocation(long wheelId, int wheelIndex, Vector3f vec); /** - * returns the location where the wheel collides with the ground (world space) + * Copy the location where the wheel collides with the ground. + * + * @return a new location vector (in physics-space coordinates) */ public Vector3f getCollisionLocation() { Vector3f vec = new Vector3f(); @@ -322,7 +555,10 @@ public class VehicleWheel implements Savable { } /** - * returns the normal where the wheel collides with the ground (world space) + * Copy the normal where the wheel touches the ground. + * + * @param vec storage for the result (not null, modified) + * @return a unit vector (in physics-space coordinates, not null) */ public Vector3f getCollisionNormal(Vector3f vec) { getCollisionNormal(wheelId, wheelIndex, vec); @@ -332,7 +568,9 @@ public class VehicleWheel implements Savable { private native void getCollisionNormal(long wheelId, int wheelIndex, Vector3f vec); /** - * returns the normal where the wheel collides with the ground (world space) + * Copy the normal where the wheel touches the ground. + * + * @return a new unit vector (in physics-space coordinates, not null) */ public Vector3f getCollisionNormal() { Vector3f vec = new Vector3f(); @@ -341,8 +579,11 @@ public class VehicleWheel implements Savable { } /** - * returns how much the wheel skids on the ground (for skid sounds/smoke etc.)
- * 0.0 = wheels are sliding, 1.0 = wheels have traction. + * Calculate to what extent the wheel is skidding (for skid sounds/smoke + * etc.) + * + * @return the relative amount of traction (0→wheel is sliding, + * 1→wheel has full traction) */ public float getSkidInfo() { return getSkidInfo(wheelId, wheelIndex); @@ -351,8 +592,9 @@ public class VehicleWheel implements Savable { public native float getSkidInfo(long wheelId, int wheelIndex); /** - * returns how many degrees the wheel has turned since the last physics - * step. + * Calculate how much this wheel has turned since the last physics step. + * + * @return the rotation angle (in radians) */ public float getDeltaRotation() { return getDeltaRotation(wheelId, wheelIndex); @@ -360,6 +602,12 @@ public class VehicleWheel implements Savable { public native float getDeltaRotation(long wheelId, int wheelIndex); + /** + * De-serialize this wheel, for example when loading from a J3O file. + * + * @param im importer (not null) + * @throws IOException from importer + */ @Override public void read(JmeImporter im) throws IOException { InputCapsule capsule = im.getCapsule(this); @@ -379,6 +627,12 @@ public class VehicleWheel implements Savable { restLength = capsule.readFloat("restLength", 1f); } + /** + * Serialize this wheel, for example when saving to a J3O file. + * + * @param ex exporter (not null) + * @throws IOException from exporter + */ @Override public void write(JmeExporter ex) throws IOException { OutputCapsule capsule = ex.getCapsule(this); @@ -399,41 +653,59 @@ public class VehicleWheel implements Savable { } /** - * @return the wheelSpatial + * Access the spatial associated with this wheel. + * + * @return the pre-existing instance, or null */ public Spatial getWheelSpatial() { return wheelSpatial; } /** - * @param wheelSpatial the wheelSpatial to set + * Alter which spatial is associated with this wheel. + * + * @param wheelSpatial the desired spatial, or null for none */ public void setWheelSpatial(Spatial wheelSpatial) { this.wheelSpatial = wheelSpatial; } + /** + * Test whether physics coordinates should match the local transform of the + * Spatial. + * + * @return true if matching local transform, false if matching world + * transform + */ public boolean isApplyLocal() { return applyLocal; } + /** + * Alter whether physics coordinates should match the local transform of the + * Spatial. + * + * @param applyLocal true→match local transform, false→match world + * transform (default=false) + */ public void setApplyLocal(boolean applyLocal) { this.applyLocal = applyLocal; } /** - * write the content of the wheelWorldRotation into the store - * - * @param store - */ + * Copy this wheel's physics-space orientation to the specified quaternion. + * + * @param store storage for the result (not null, modified) + */ public void getWheelWorldRotation(final Quaternion store) { store.set(this.wheelWorldRotation); } /** - * write the content of the wheelWorldLocation into the store - * - * @param store - */ + * Copy this wheel's physics-space location to the specified vector. + * + * @param store storage for the result (not null, modified) + */ public void getWheelWorldLocation(final Vector3f store) { store.set(this.wheelWorldLocation); } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/RigidBodyMotionState.java b/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/RigidBodyMotionState.java index 3406e7ae5..51a7f4698 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/RigidBodyMotionState.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/RigidBodyMotionState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -40,8 +40,8 @@ import java.util.logging.Level; import java.util.logging.Logger; /** - * stores transform info of a PhysicsNode in a threadsafe manner to - * allow multithreaded access from the jme scenegraph and the bullet physicsspace + * The motion state (transform) of a rigid body, with thread-safe access. + * * @author normenhansen */ public class RigidBodyMotionState { @@ -51,9 +51,16 @@ public class RigidBodyMotionState { private Quaternion worldRotationQuat = new Quaternion(); private Quaternion tmp_inverseWorldRotation = new Quaternion(); private PhysicsVehicle vehicle; + /** + * true → physics coordinates match local transform, false → + * physics coordinates match world transform + */ private boolean applyPhysicsLocal = false; // protected LinkedList listeners = new LinkedList(); + /** + * Instantiate a motion state. + */ public RigidBodyMotionState() { this.motionStateId = createMotionState(); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created MotionState {0}", Long.toHexString(motionStateId)); @@ -62,8 +69,11 @@ public class RigidBodyMotionState { private native long createMotionState(); /** - * applies the current transform to the given jme Node if the location has been updated on the physics side - * @param spatial + * If the motion state has been updated, apply the new transform to the + * specified spatial. + * + * @param spatial where to apply the physics transform (not null, modified) + * @return true if changed */ public boolean applyTransform(Spatial spatial) { Vector3f localLocation = spatial.getLocalTranslation(); @@ -97,7 +107,10 @@ public class RigidBodyMotionState { private native boolean applyTransform(long stateId, Vector3f location, Quaternion rotation); /** - * @return the worldLocation + * Copy the location from this motion state. + * + * @return the pre-existing location vector (in physics-space coordinates, + * not null) */ public Vector3f getWorldLocation() { getWorldLocation(motionStateId, worldLocation); @@ -107,7 +120,10 @@ public class RigidBodyMotionState { private native void getWorldLocation(long stateId, Vector3f vec); /** - * @return the worldRotation + * Read the rotation of this motion state (as a matrix). + * + * @return the pre-existing rotation matrix (in physics-space coordinates, + * not null) */ public Matrix3f getWorldRotation() { getWorldRotation(motionStateId, worldRotation); @@ -117,7 +133,10 @@ public class RigidBodyMotionState { private native void getWorldRotation(long stateId, Matrix3f vec); /** - * @return the worldRotationQuat + * Read the rotation of this motion state (as a quaternion). + * + * @return the pre-existing instance (in physics-space coordinates, not + * null) */ public Quaternion getWorldRotationQuat() { getWorldRotationQuat(motionStateId, worldRotationQuat); @@ -127,20 +146,39 @@ public class RigidBodyMotionState { private native void getWorldRotationQuat(long stateId, Quaternion vec); /** - * @param vehicle the vehicle to set + * @param vehicle which vehicle will use this motion state */ public void setVehicle(PhysicsVehicle vehicle) { this.vehicle = vehicle; } + /** + * Test whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @return true if matching local coordinates, false if matching world + * coordinates + */ public boolean isApplyPhysicsLocal() { return applyPhysicsLocal; } + /** + * Alter whether physics-space coordinates should match the spatial's local + * coordinates. + * + * @param applyPhysicsLocal true→match local coordinates, + * false→match world coordinates (default is false) + */ public void setApplyPhysicsLocal(boolean applyPhysicsLocal) { this.applyPhysicsLocal = applyPhysicsLocal; } + /** + * Read the unique id of the native object. + * + * @return id (not zero) + */ public long getObjectId(){ return motionStateId; } @@ -152,6 +190,12 @@ public class RigidBodyMotionState { // listeners.remove(listener); // } + /** + * Finalize this motion state just before it is destroyed. Should be invoked + * only by a subclass or by the garbage collector. + * + * @throws Throwable ignored by the garbage collector + */ @Override protected void finalize() throws Throwable { super.finalize(); diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/VehicleTuning.java b/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/VehicleTuning.java index 47f344bd7..3a6dabbc7 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/VehicleTuning.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/objects/infos/VehicleTuning.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,14 +32,35 @@ package com.jme3.bullet.objects.infos; /** - * + * Typical tuning parameters for a PhysicsVehicle. + * * @author normenhansen */ public class VehicleTuning { + /** + * suspension stiffness constant (10→off-road buggy, 50→sports + * car, 200→Formula-1 race car, default=5.88) + */ public float suspensionStiffness = 5.88f; + /** + * suspension damping when compressed (0→no damping, default=0.83) + */ public float suspensionCompression = 0.83f; + /** + * suspension damping when expanded (0→no damping, default=0.88) + */ public float suspensionDamping = 0.88f; + /** + * maximum suspension travel distance (in centimeters, default=500) + */ public float maxSuspensionTravelCm = 500f; + /** + * maximum force exerted by each wheel's suspension (default=6000) + */ public float maxSuspensionForce = 6000f; + /** + * coefficient of friction between tyres and ground (0.8→realistic car, + * 10000→kart racer, default=10.5) + */ public float frictionSlip = 10.5f; } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugMeshCallback.java b/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugMeshCallback.java index fbec02c9c..f0bc3c45f 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugMeshCallback.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugMeshCallback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2012 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,6 +44,17 @@ public class DebugMeshCallback { private ArrayList list = new ArrayList(); + /** + * Add a vertex to the mesh under construction. + *

+ * This method is invoked from native code. + * + * @param x local X coordinate of new vertex + * @param y local Y coordinate of new vertex + * @param z local Z coordinate of new vertex + * @param part ignored + * @param index ignored + */ public void addVector(float x, float y, float z, int part, int index) { list.add(new Vector3f(x, y, z)); } diff --git a/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java b/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java index 939be4083..935130685 100644 --- a/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java +++ b/jme3-bullet/src/main/java/com/jme3/bullet/util/DebugShapeFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009-2017 jMonkeyEngine + * Copyright (c) 2009-2018 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -57,10 +57,13 @@ public class DebugShapeFactory { // private static final Vector3f aabbMin = new Vector3f(-1e30f, -1e30f, -1e30f); /** - * Creates a debug shape from the given collision shape. This is mostly used internally.
- * To attach a debug shape to a physics object, call attachDebugShape(AssetManager manager); on it. - * @param collisionShape - * @return + * Create a debug spatial from the specified collision shape. + *

+ * This is mostly used internally. To attach a debug shape to a physics + * object, call attachDebugShape(AssetManager manager); on it. + * + * @param collisionShape the shape to visualize (may be null, unaffected) + * @return a new tree of geometries, or null */ public static Spatial getDebugShape(CollisionShape collisionShape) { if (collisionShape == null) { @@ -102,6 +105,12 @@ public class DebugShapeFactory { return debugShape; } + /** + * Create a geometry for visualizing the specified shape. + * + * @param shape (not null, unaffected) + * @return a new geometry (not null) + */ private static Geometry createDebugShape(CollisionShape shape) { Geometry geom = new Geometry(); geom.setMesh(DebugShapeFactory.getDebugMesh(shape)); @@ -110,6 +119,12 @@ public class DebugShapeFactory { return geom; } + /** + * Create a mesh for visualizing the specified shape. + * + * @param shape (not null, unaffected) + * @return a new mesh (not null) + */ public static Mesh getDebugMesh(CollisionShape shape) { Mesh mesh = new Mesh(); DebugMeshCallback callback = new DebugMeshCallback();