com.jme3.bullet.objects.VehicleWheel Maven / Gradle / Ivy
/*
* Copyright (c) 2009-2018 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.bullet.objects;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.export.*;
import com.jme3.math.Matrix3f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import java.io.IOException;
/**
* 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);
this.direction.set(direction);
this.axle.set(axle);
this.frontWheel = frontWheel;
this.restLength = restLength;
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);
wheelWorldRotation.fromRotationMatrix(tmp_Matrix);
}
private native void getWheelLocation(long vehicleId, int wheelId, Vector3f location);
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;
}
Quaternion localRotationQuat = wheelSpatial.getLocalRotation();
Vector3f localLocation = wheelSpatial.getLocalTranslation();
if (!applyLocal && wheelSpatial.getParent() != null) {
localLocation.set(wheelWorldLocation).subtractLocal(wheelSpatial.getParent().getWorldTranslation());
localLocation.divideLocal(wheelSpatial.getParent().getWorldScale());
tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
localRotationQuat.set(wheelWorldRotation);
tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat);
wheelSpatial.setLocalTranslation(localLocation);
wheelSpatial.setLocalRotation(localRotationQuat);
} else {
wheelSpatial.setLocalTranslation(wheelWorldLocation);
wheelSpatial.setLocalRotation(wheelWorldRotation);
}
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
}
/**
* 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;
applyInfo();
}
private void applyInfo() {
if (wheelId == 0) {
return;
}
applyInfo(wheelId, wheelIndex, suspensionStiffness, wheelsDampingRelaxation, wheelsDampingCompression, frictionSlip, rollInfluence, maxSuspensionTravelCm, maxSuspensionForce, radius, frontWheel, restLength);
}
private native void applyInfo(long wheelId, int wheelIndex,
float suspensionStiffness,
float wheelsDampingRelaxation,
float wheelsDampingCompression,
float frictionSlip,
float rollInfluence,
float maxSuspensionTravelCm,
float maxSuspensionForce,
float wheelsRadius,
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();
}
/**
* returns the object this wheel is in contact with or null if no contact
* @return the PhysicsCollisionObject (PhysicsRigidBody, PhysicsGhostObject)
*/
public PhysicsCollisionObject getGroundObject() {
// if (wheelInfo.raycastInfo.groundObject == null) {
// return null;
// } else if (wheelInfo.raycastInfo.groundObject instanceof RigidBody) {
// System.out.println("RigidBody");
// return (PhysicsRigidBody) ((RigidBody) wheelInfo.raycastInfo.groundObject).getUserPointer();
// } else {
return null;
// }
}
/**
* 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);
return vec;
}
private native void getCollisionLocation(long wheelId, int wheelIndex, Vector3f vec);
/**
* 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();
getCollisionLocation(wheelId, wheelIndex, vec);
return vec;
}
/**
* 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);
return vec;
}
private native void getCollisionNormal(long wheelId, int wheelIndex, Vector3f vec);
/**
* 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();
getCollisionNormal(wheelId, wheelIndex, vec);
return vec;
}
/**
* 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);
}
public native float getSkidInfo(long wheelId, int wheelIndex);
/**
* 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);
}
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);
wheelSpatial = (Spatial) capsule.readSavable("wheelSpatial", null);
frontWheel = capsule.readBoolean("frontWheel", false);
location = (Vector3f) capsule.readSavable("wheelLocation", new Vector3f());
direction = (Vector3f) capsule.readSavable("wheelDirection", new Vector3f());
axle = (Vector3f) capsule.readSavable("wheelAxle", new Vector3f());
suspensionStiffness = capsule.readFloat("suspensionStiffness", 20.0f);
wheelsDampingRelaxation = capsule.readFloat("wheelsDampingRelaxation", 2.3f);
wheelsDampingCompression = capsule.readFloat("wheelsDampingCompression", 4.4f);
frictionSlip = capsule.readFloat("frictionSlip", 10.5f);
rollInfluence = capsule.readFloat("rollInfluence", 1.0f);
maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f);
maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f);
radius = capsule.readFloat("wheelRadius", 0.5f);
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);
capsule.write(wheelSpatial, "wheelSpatial", null);
capsule.write(frontWheel, "frontWheel", false);
capsule.write(location, "wheelLocation", new Vector3f());
capsule.write(direction, "wheelDirection", new Vector3f());
capsule.write(axle, "wheelAxle", new Vector3f());
capsule.write(suspensionStiffness, "suspensionStiffness", 20.0f);
capsule.write(wheelsDampingRelaxation, "wheelsDampingRelaxation", 2.3f);
capsule.write(wheelsDampingCompression, "wheelsDampingCompression", 4.4f);
capsule.write(frictionSlip, "frictionSlip", 10.5f);
capsule.write(rollInfluence, "rollInfluence", 1.0f);
capsule.write(maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f);
capsule.write(maxSuspensionForce, "maxSuspensionForce", 6000f);
capsule.write(radius, "wheelRadius", 0.5f);
capsule.write(restLength, "restLength", 1f);
}
/**
* Access the spatial associated with this wheel.
*
* @return the pre-existing instance, or null
*/
public Spatial getWheelSpatial() {
return wheelSpatial;
}
/**
* 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;
}
/**
* 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);
}
/**
* 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);
}
}