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