2 * Copyright (c) 2009-2010 jMonkeyEngine
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 package com.jme3.bullet.objects;
34 import com.jme3.math.Quaternion;
35 import com.jme3.math.Vector3f;
36 import com.jme3.scene.Spatial;
37 import com.jme3.bullet.collision.PhysicsCollisionObject;
38 import com.jme3.export.InputCapsule;
39 import com.jme3.export.JmeExporter;
40 import com.jme3.export.JmeImporter;
41 import com.jme3.export.OutputCapsule;
42 import com.jme3.export.Savable;
43 import com.jme3.math.Matrix3f;
44 import java.io.IOException;
45 import java.util.logging.Level;
46 import java.util.logging.Logger;
49 * Stores info about one wheel of a PhysicsVehicle
50 * @author normenhansen
52 public class VehicleWheel implements Savable {
54 protected long wheelId = 0;
55 protected int wheelIndex = 0;
56 protected boolean frontWheel;
57 protected Vector3f location = new Vector3f();
58 protected Vector3f direction = new Vector3f();
59 protected Vector3f axle = new Vector3f();
60 protected float suspensionStiffness = 20.0f;
61 protected float wheelsDampingRelaxation = 2.3f;
62 protected float wheelsDampingCompression = 4.4f;
63 protected float frictionSlip = 10.5f;
64 protected float rollInfluence = 1.0f;
65 protected float maxSuspensionTravelCm = 500f;
66 protected float maxSuspensionForce = 6000f;
67 protected float radius = 0.5f;
68 protected float restLength = 1f;
69 protected Vector3f wheelWorldLocation = new Vector3f();
70 protected Quaternion wheelWorldRotation = new Quaternion();
71 protected Spatial wheelSpatial;
72 protected Matrix3f tmp_Matrix = new com.jme3.math.Matrix3f();
73 protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
74 private boolean applyLocal = false;
76 public VehicleWheel() {
79 public VehicleWheel(Spatial spat, Vector3f location, Vector3f direction, Vector3f axle,
80 float restLength, float radius, boolean frontWheel) {
81 this(location, direction, axle, restLength, radius, frontWheel);
85 public VehicleWheel(Vector3f location, Vector3f direction, Vector3f axle,
86 float restLength, float radius, boolean frontWheel) {
87 this.location.set(location);
88 this.direction.set(direction);
90 this.frontWheel = frontWheel;
91 this.restLength = restLength;
95 public synchronized void updatePhysicsState() {
96 getWheelLocation(wheelId, wheelIndex, wheelWorldLocation);
97 getWheelRotation(wheelId, wheelIndex, tmp_Matrix);
98 wheelWorldRotation.fromRotationMatrix(tmp_Matrix);
101 private native void getWheelLocation(long vehicleId, int wheelId, Vector3f location);
103 private native void getWheelRotation(long vehicleId, int wheelId, Matrix3f location);
105 public synchronized void applyWheelTransform() {
106 if (wheelSpatial == null) {
109 Quaternion localRotationQuat = wheelSpatial.getLocalRotation();
110 Vector3f localLocation = wheelSpatial.getLocalTranslation();
111 if (!applyLocal && wheelSpatial.getParent() != null) {
112 localLocation.set(wheelWorldLocation).subtractLocal(wheelSpatial.getParent().getWorldTranslation());
113 localLocation.divideLocal(wheelSpatial.getParent().getWorldScale());
114 tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
116 localRotationQuat.set(wheelWorldRotation);
117 tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat);
119 wheelSpatial.setLocalTranslation(localLocation);
120 wheelSpatial.setLocalRotation(localRotationQuat);
122 wheelSpatial.setLocalTranslation(wheelWorldLocation);
123 wheelSpatial.setLocalRotation(wheelWorldRotation);
127 public long getWheelId() {
131 public void setVehicleId(long vehicleId, int wheelIndex) {
132 this.wheelId = vehicleId;
133 this.wheelIndex = wheelIndex;
137 public boolean isFrontWheel() {
141 public void setFrontWheel(boolean frontWheel) {
142 this.frontWheel = frontWheel;
146 public Vector3f getLocation() {
150 public Vector3f getDirection() {
154 public Vector3f getAxle() {
158 public float getSuspensionStiffness() {
159 return suspensionStiffness;
163 * the stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car
164 * @param suspensionStiffness
166 public void setSuspensionStiffness(float suspensionStiffness) {
167 this.suspensionStiffness = suspensionStiffness;
171 public float getWheelsDampingRelaxation() {
172 return wheelsDampingRelaxation;
176 * the damping coefficient for when the suspension is expanding.
177 * See the comments for setWheelsDampingCompression for how to set k.
178 * @param wheelsDampingRelaxation
180 public void setWheelsDampingRelaxation(float wheelsDampingRelaxation) {
181 this.wheelsDampingRelaxation = wheelsDampingRelaxation;
185 public float getWheelsDampingCompression() {
186 return wheelsDampingCompression;
190 * the damping coefficient for when the suspension is compressed.
191 * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br>
192 * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br>
193 * 0.1 to 0.3 are good values
194 * @param wheelsDampingCompression
196 public void setWheelsDampingCompression(float wheelsDampingCompression) {
197 this.wheelsDampingCompression = wheelsDampingCompression;
201 public float getFrictionSlip() {
206 * the coefficient of friction between the tyre and the ground.
207 * Should be about 0.8 for realistic cars, but can increased for better handling.
208 * Set large (10000.0) for kart racers
209 * @param frictionSlip
211 public void setFrictionSlip(float frictionSlip) {
212 this.frictionSlip = frictionSlip;
216 public float getRollInfluence() {
217 return rollInfluence;
221 * reduces the rolling torque applied from the wheels that cause the vehicle to roll over.
222 * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour.
223 * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over.
224 * You should also try lowering the vehicle's centre of mass
225 * @param rollInfluence the rollInfluence to set
227 public void setRollInfluence(float rollInfluence) {
228 this.rollInfluence = rollInfluence;
232 public float getMaxSuspensionTravelCm() {
233 return maxSuspensionTravelCm;
237 * the maximum distance the suspension can be compressed (centimetres)
238 * @param maxSuspensionTravelCm
240 public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) {
241 this.maxSuspensionTravelCm = maxSuspensionTravelCm;
245 public float getMaxSuspensionForce() {
246 return maxSuspensionForce;
250 * The maximum suspension force, raise this above the default 6000 if your suspension cannot
251 * handle the weight of your vehcile.
252 * @param maxSuspensionTravelCm
254 public void setMaxSuspensionForce(float maxSuspensionForce) {
255 this.maxSuspensionForce = maxSuspensionForce;
259 private void applyInfo() {
263 applyInfo(wheelId, wheelIndex, suspensionStiffness, wheelsDampingRelaxation, wheelsDampingCompression, frictionSlip, rollInfluence, maxSuspensionTravelCm, maxSuspensionForce, radius, frontWheel, restLength);
266 private native void applyInfo(long wheelId, int wheelIndex,
267 float suspensionStiffness,
268 float wheelsDampingRelaxation,
269 float wheelsDampingCompression,
272 float maxSuspensionTravelCm,
273 float maxSuspensionForce,
276 float suspensionRestLength);
278 public float getRadius() {
282 public void setRadius(float radius) {
283 this.radius = radius;
287 public float getRestLength() {
291 public void setRestLength(float restLength) {
292 this.restLength = restLength;
297 * returns the object this wheel is in contact with or null if no contact
298 * @return the PhysicsCollisionObject (PhysicsRigidBody, PhysicsGhostObject)
300 public PhysicsCollisionObject getGroundObject() {
301 // if (wheelInfo.raycastInfo.groundObject == null) {
303 // } else if (wheelInfo.raycastInfo.groundObject instanceof RigidBody) {
304 // System.out.println("RigidBody");
305 // return (PhysicsRigidBody) ((RigidBody) wheelInfo.raycastInfo.groundObject).getUserPointer();
312 * returns the location where the wheel collides with the ground (world space)
314 public Vector3f getCollisionLocation(Vector3f vec) {
315 getCollisionLocation(wheelId, wheelIndex, vec);
319 private native void getCollisionLocation(long wheelId, int wheelIndex, Vector3f vec);
322 * returns the location where the wheel collides with the ground (world space)
324 public Vector3f getCollisionLocation() {
325 Vector3f vec = new Vector3f();
326 getCollisionLocation(wheelId, wheelIndex, vec);
331 * returns the normal where the wheel collides with the ground (world space)
333 public Vector3f getCollisionNormal(Vector3f vec) {
334 getCollisionNormal(wheelId, wheelIndex, vec);
338 private native void getCollisionNormal(long wheelId, int wheelIndex, Vector3f vec);
341 * returns the normal where the wheel collides with the ground (world space)
343 public Vector3f getCollisionNormal() {
344 Vector3f vec = new Vector3f();
345 getCollisionNormal(wheelId, wheelIndex, vec);
350 * returns how much the wheel skids on the ground (for skid sounds/smoke etc.)<br>
351 * 0.0 = wheels are sliding, 1.0 = wheels have traction.
353 public float getSkidInfo() {
354 return getSkidInfo(wheelId, wheelIndex);
357 public native float getSkidInfo(long wheelId, int wheelIndex);
360 public void read(JmeImporter im) throws IOException {
361 InputCapsule capsule = im.getCapsule(this);
362 wheelSpatial = (Spatial) capsule.readSavable("wheelSpatial", null);
363 frontWheel = capsule.readBoolean("frontWheel", false);
364 location = (Vector3f) capsule.readSavable("wheelLocation", new Vector3f());
365 direction = (Vector3f) capsule.readSavable("wheelDirection", new Vector3f());
366 axle = (Vector3f) capsule.readSavable("wheelAxle", new Vector3f());
367 suspensionStiffness = capsule.readFloat("suspensionStiffness", 20.0f);
368 wheelsDampingRelaxation = capsule.readFloat("wheelsDampingRelaxation", 2.3f);
369 wheelsDampingCompression = capsule.readFloat("wheelsDampingCompression", 4.4f);
370 frictionSlip = capsule.readFloat("frictionSlip", 10.5f);
371 rollInfluence = capsule.readFloat("rollInfluence", 1.0f);
372 maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f);
373 maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f);
374 radius = capsule.readFloat("wheelRadius", 0.5f);
375 restLength = capsule.readFloat("restLength", 1f);
379 public void write(JmeExporter ex) throws IOException {
380 OutputCapsule capsule = ex.getCapsule(this);
381 capsule.write(wheelSpatial, "wheelSpatial", null);
382 capsule.write(frontWheel, "frontWheel", false);
383 capsule.write(location, "wheelLocation", new Vector3f());
384 capsule.write(direction, "wheelDirection", new Vector3f());
385 capsule.write(axle, "wheelAxle", new Vector3f());
386 capsule.write(suspensionStiffness, "suspensionStiffness", 20.0f);
387 capsule.write(wheelsDampingRelaxation, "wheelsDampingRelaxation", 2.3f);
388 capsule.write(wheelsDampingCompression, "wheelsDampingCompression", 4.4f);
389 capsule.write(frictionSlip, "frictionSlip", 10.5f);
390 capsule.write(rollInfluence, "rollInfluence", 1.0f);
391 capsule.write(maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f);
392 capsule.write(maxSuspensionForce, "maxSuspensionForce", 6000f);
393 capsule.write(radius, "wheelRadius", 0.5f);
394 capsule.write(restLength, "restLength", 1f);
398 * @return the wheelSpatial
400 public Spatial getWheelSpatial() {
405 * @param wheelSpatial the wheelSpatial to set
407 public void setWheelSpatial(Spatial wheelSpatial) {
408 this.wheelSpatial = wheelSpatial;
411 public boolean isApplyLocal() {
415 public void setApplyLocal(boolean applyLocal) {
416 this.applyLocal = applyLocal;
420 protected void finalize() throws Throwable {
422 Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing Wheel {0}", Long.toHexString(wheelId));
423 // finalizeNative(wheelId);
426 private native void finalizeNative(long wheelId, int wheelIndex);