1/* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 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. 15 * 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. 19 * 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. 31 */ 32package com.jme3.bullet.objects; 33 34import com.jme3.bullet.collision.PhysicsCollisionObject; 35import com.jme3.export.*; 36import com.jme3.math.Matrix3f; 37import com.jme3.math.Quaternion; 38import com.jme3.math.Vector3f; 39import com.jme3.scene.Spatial; 40import java.io.IOException; 41 42/** 43 * Stores info about one wheel of a PhysicsVehicle 44 * @author normenhansen 45 */ 46public class VehicleWheel implements Savable { 47 48 protected long wheelId = 0; 49 protected int wheelIndex = 0; 50 protected boolean frontWheel; 51 protected Vector3f location = new Vector3f(); 52 protected Vector3f direction = new Vector3f(); 53 protected Vector3f axle = new Vector3f(); 54 protected float suspensionStiffness = 20.0f; 55 protected float wheelsDampingRelaxation = 2.3f; 56 protected float wheelsDampingCompression = 4.4f; 57 protected float frictionSlip = 10.5f; 58 protected float rollInfluence = 1.0f; 59 protected float maxSuspensionTravelCm = 500f; 60 protected float maxSuspensionForce = 6000f; 61 protected float radius = 0.5f; 62 protected float restLength = 1f; 63 protected Vector3f wheelWorldLocation = new Vector3f(); 64 protected Quaternion wheelWorldRotation = new Quaternion(); 65 protected Spatial wheelSpatial; 66 protected Matrix3f tmp_Matrix = new com.jme3.math.Matrix3f(); 67 protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); 68 private boolean applyLocal = false; 69 70 public VehicleWheel() { 71 } 72 73 public VehicleWheel(Spatial spat, Vector3f location, Vector3f direction, Vector3f axle, 74 float restLength, float radius, boolean frontWheel) { 75 this(location, direction, axle, restLength, radius, frontWheel); 76 wheelSpatial = spat; 77 } 78 79 public VehicleWheel(Vector3f location, Vector3f direction, Vector3f axle, 80 float restLength, float radius, boolean frontWheel) { 81 this.location.set(location); 82 this.direction.set(direction); 83 this.axle.set(axle); 84 this.frontWheel = frontWheel; 85 this.restLength = restLength; 86 this.radius = radius; 87 } 88 89 public synchronized void updatePhysicsState() { 90 getWheelLocation(wheelId, wheelIndex, wheelWorldLocation); 91 getWheelRotation(wheelId, wheelIndex, tmp_Matrix); 92 wheelWorldRotation.fromRotationMatrix(tmp_Matrix); 93 } 94 95 private native void getWheelLocation(long vehicleId, int wheelId, Vector3f location); 96 97 private native void getWheelRotation(long vehicleId, int wheelId, Matrix3f location); 98 99 public synchronized void applyWheelTransform() { 100 if (wheelSpatial == null) { 101 return; 102 } 103 Quaternion localRotationQuat = wheelSpatial.getLocalRotation(); 104 Vector3f localLocation = wheelSpatial.getLocalTranslation(); 105 if (!applyLocal && wheelSpatial.getParent() != null) { 106 localLocation.set(wheelWorldLocation).subtractLocal(wheelSpatial.getParent().getWorldTranslation()); 107 localLocation.divideLocal(wheelSpatial.getParent().getWorldScale()); 108 tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation); 109 110 localRotationQuat.set(wheelWorldRotation); 111 tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat); 112 113 wheelSpatial.setLocalTranslation(localLocation); 114 wheelSpatial.setLocalRotation(localRotationQuat); 115 } else { 116 wheelSpatial.setLocalTranslation(wheelWorldLocation); 117 wheelSpatial.setLocalRotation(wheelWorldRotation); 118 } 119 } 120 121 public long getWheelId() { 122 return wheelId; 123 } 124 125 public void setVehicleId(long vehicleId, int wheelIndex) { 126 this.wheelId = vehicleId; 127 this.wheelIndex = wheelIndex; 128 applyInfo(); 129 } 130 131 public boolean isFrontWheel() { 132 return frontWheel; 133 } 134 135 public void setFrontWheel(boolean frontWheel) { 136 this.frontWheel = frontWheel; 137 applyInfo(); 138 } 139 140 public Vector3f getLocation() { 141 return location; 142 } 143 144 public Vector3f getDirection() { 145 return direction; 146 } 147 148 public Vector3f getAxle() { 149 return axle; 150 } 151 152 public float getSuspensionStiffness() { 153 return suspensionStiffness; 154 } 155 156 /** 157 * the stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car 158 * @param suspensionStiffness 159 */ 160 public void setSuspensionStiffness(float suspensionStiffness) { 161 this.suspensionStiffness = suspensionStiffness; 162 applyInfo(); 163 } 164 165 public float getWheelsDampingRelaxation() { 166 return wheelsDampingRelaxation; 167 } 168 169 /** 170 * the damping coefficient for when the suspension is expanding. 171 * See the comments for setWheelsDampingCompression for how to set k. 172 * @param wheelsDampingRelaxation 173 */ 174 public void setWheelsDampingRelaxation(float wheelsDampingRelaxation) { 175 this.wheelsDampingRelaxation = wheelsDampingRelaxation; 176 applyInfo(); 177 } 178 179 public float getWheelsDampingCompression() { 180 return wheelsDampingCompression; 181 } 182 183 /** 184 * the damping coefficient for when the suspension is compressed. 185 * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br> 186 * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br> 187 * 0.1 to 0.3 are good values 188 * @param wheelsDampingCompression 189 */ 190 public void setWheelsDampingCompression(float wheelsDampingCompression) { 191 this.wheelsDampingCompression = wheelsDampingCompression; 192 applyInfo(); 193 } 194 195 public float getFrictionSlip() { 196 return frictionSlip; 197 } 198 199 /** 200 * the coefficient of friction between the tyre and the ground. 201 * Should be about 0.8 for realistic cars, but can increased for better handling. 202 * Set large (10000.0) for kart racers 203 * @param frictionSlip 204 */ 205 public void setFrictionSlip(float frictionSlip) { 206 this.frictionSlip = frictionSlip; 207 applyInfo(); 208 } 209 210 public float getRollInfluence() { 211 return rollInfluence; 212 } 213 214 /** 215 * reduces the rolling torque applied from the wheels that cause the vehicle to roll over. 216 * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour. 217 * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over. 218 * You should also try lowering the vehicle's centre of mass 219 * @param rollInfluence the rollInfluence to set 220 */ 221 public void setRollInfluence(float rollInfluence) { 222 this.rollInfluence = rollInfluence; 223 applyInfo(); 224 } 225 226 public float getMaxSuspensionTravelCm() { 227 return maxSuspensionTravelCm; 228 } 229 230 /** 231 * the maximum distance the suspension can be compressed (centimetres) 232 * @param maxSuspensionTravelCm 233 */ 234 public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) { 235 this.maxSuspensionTravelCm = maxSuspensionTravelCm; 236 applyInfo(); 237 } 238 239 public float getMaxSuspensionForce() { 240 return maxSuspensionForce; 241 } 242 243 /** 244 * The maximum suspension force, raise this above the default 6000 if your suspension cannot 245 * handle the weight of your vehcile. 246 * @param maxSuspensionTravelCm 247 */ 248 public void setMaxSuspensionForce(float maxSuspensionForce) { 249 this.maxSuspensionForce = maxSuspensionForce; 250 applyInfo(); 251 } 252 253 private void applyInfo() { 254 if (wheelId == 0) { 255 return; 256 } 257 applyInfo(wheelId, wheelIndex, suspensionStiffness, wheelsDampingRelaxation, wheelsDampingCompression, frictionSlip, rollInfluence, maxSuspensionTravelCm, maxSuspensionForce, radius, frontWheel, restLength); 258 } 259 260 private native void applyInfo(long wheelId, int wheelIndex, 261 float suspensionStiffness, 262 float wheelsDampingRelaxation, 263 float wheelsDampingCompression, 264 float frictionSlip, 265 float rollInfluence, 266 float maxSuspensionTravelCm, 267 float maxSuspensionForce, 268 float wheelsRadius, 269 boolean frontWheel, 270 float suspensionRestLength); 271 272 public float getRadius() { 273 return radius; 274 } 275 276 public void setRadius(float radius) { 277 this.radius = radius; 278 applyInfo(); 279 } 280 281 public float getRestLength() { 282 return restLength; 283 } 284 285 public void setRestLength(float restLength) { 286 this.restLength = restLength; 287 applyInfo(); 288 } 289 290 /** 291 * returns the object this wheel is in contact with or null if no contact 292 * @return the PhysicsCollisionObject (PhysicsRigidBody, PhysicsGhostObject) 293 */ 294 public PhysicsCollisionObject getGroundObject() { 295// if (wheelInfo.raycastInfo.groundObject == null) { 296// return null; 297// } else if (wheelInfo.raycastInfo.groundObject instanceof RigidBody) { 298// System.out.println("RigidBody"); 299// return (PhysicsRigidBody) ((RigidBody) wheelInfo.raycastInfo.groundObject).getUserPointer(); 300// } else { 301 return null; 302// } 303 } 304 305 /** 306 * returns the location where the wheel collides with the ground (world space) 307 */ 308 public Vector3f getCollisionLocation(Vector3f vec) { 309 getCollisionLocation(wheelId, wheelIndex, vec); 310 return vec; 311 } 312 313 private native void getCollisionLocation(long wheelId, int wheelIndex, Vector3f vec); 314 315 /** 316 * returns the location where the wheel collides with the ground (world space) 317 */ 318 public Vector3f getCollisionLocation() { 319 Vector3f vec = new Vector3f(); 320 getCollisionLocation(wheelId, wheelIndex, vec); 321 return vec; 322 } 323 324 /** 325 * returns the normal where the wheel collides with the ground (world space) 326 */ 327 public Vector3f getCollisionNormal(Vector3f vec) { 328 getCollisionNormal(wheelId, wheelIndex, vec); 329 return vec; 330 } 331 332 private native void getCollisionNormal(long wheelId, int wheelIndex, Vector3f vec); 333 334 /** 335 * returns the normal where the wheel collides with the ground (world space) 336 */ 337 public Vector3f getCollisionNormal() { 338 Vector3f vec = new Vector3f(); 339 getCollisionNormal(wheelId, wheelIndex, vec); 340 return vec; 341 } 342 343 /** 344 * returns how much the wheel skids on the ground (for skid sounds/smoke etc.)<br> 345 * 0.0 = wheels are sliding, 1.0 = wheels have traction. 346 */ 347 public float getSkidInfo() { 348 return getSkidInfo(wheelId, wheelIndex); 349 } 350 351 public native float getSkidInfo(long wheelId, int wheelIndex); 352 353 /** 354 * returns how many degrees the wheel has turned since the last physics 355 * step. 356 */ 357 public float getDeltaRotation() { 358 return getDeltaRotation(wheelId, wheelIndex); 359 } 360 361 public native float getDeltaRotation(long wheelId, int wheelIndex); 362 363 @Override 364 public void read(JmeImporter im) throws IOException { 365 InputCapsule capsule = im.getCapsule(this); 366 wheelSpatial = (Spatial) capsule.readSavable("wheelSpatial", null); 367 frontWheel = capsule.readBoolean("frontWheel", false); 368 location = (Vector3f) capsule.readSavable("wheelLocation", new Vector3f()); 369 direction = (Vector3f) capsule.readSavable("wheelDirection", new Vector3f()); 370 axle = (Vector3f) capsule.readSavable("wheelAxle", new Vector3f()); 371 suspensionStiffness = capsule.readFloat("suspensionStiffness", 20.0f); 372 wheelsDampingRelaxation = capsule.readFloat("wheelsDampingRelaxation", 2.3f); 373 wheelsDampingCompression = capsule.readFloat("wheelsDampingCompression", 4.4f); 374 frictionSlip = capsule.readFloat("frictionSlip", 10.5f); 375 rollInfluence = capsule.readFloat("rollInfluence", 1.0f); 376 maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f); 377 maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f); 378 radius = capsule.readFloat("wheelRadius", 0.5f); 379 restLength = capsule.readFloat("restLength", 1f); 380 } 381 382 @Override 383 public void write(JmeExporter ex) throws IOException { 384 OutputCapsule capsule = ex.getCapsule(this); 385 capsule.write(wheelSpatial, "wheelSpatial", null); 386 capsule.write(frontWheel, "frontWheel", false); 387 capsule.write(location, "wheelLocation", new Vector3f()); 388 capsule.write(direction, "wheelDirection", new Vector3f()); 389 capsule.write(axle, "wheelAxle", new Vector3f()); 390 capsule.write(suspensionStiffness, "suspensionStiffness", 20.0f); 391 capsule.write(wheelsDampingRelaxation, "wheelsDampingRelaxation", 2.3f); 392 capsule.write(wheelsDampingCompression, "wheelsDampingCompression", 4.4f); 393 capsule.write(frictionSlip, "frictionSlip", 10.5f); 394 capsule.write(rollInfluence, "rollInfluence", 1.0f); 395 capsule.write(maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f); 396 capsule.write(maxSuspensionForce, "maxSuspensionForce", 6000f); 397 capsule.write(radius, "wheelRadius", 0.5f); 398 capsule.write(restLength, "restLength", 1f); 399 } 400 401 /** 402 * @return the wheelSpatial 403 */ 404 public Spatial getWheelSpatial() { 405 return wheelSpatial; 406 } 407 408 /** 409 * @param wheelSpatial the wheelSpatial to set 410 */ 411 public void setWheelSpatial(Spatial wheelSpatial) { 412 this.wheelSpatial = wheelSpatial; 413 } 414 415 public boolean isApplyLocal() { 416 return applyLocal; 417 } 418 419 public void setApplyLocal(boolean applyLocal) { 420 this.applyLocal = applyLocal; 421 } 422 423} 424