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 */ 32 package com.jme3.bullet.objects; 33 34 import com.jme3.bullet.PhysicsSpace; 35 import com.jme3.bullet.collision.shapes.CollisionShape; 36 import com.jme3.bullet.objects.infos.VehicleTuning; 37 import com.jme3.export.InputCapsule; 38 import com.jme3.export.JmeExporter; 39 import com.jme3.export.JmeImporter; 40 import com.jme3.export.OutputCapsule; 41 import com.jme3.math.Vector3f; 42 import com.jme3.scene.Geometry; 43 import com.jme3.scene.Node; 44 import com.jme3.scene.Spatial; 45 import com.jme3.scene.debug.Arrow; 46 import java.io.IOException; 47 import java.util.ArrayList; 48 import java.util.Iterator; 49 import java.util.logging.Level; 50 import java.util.logging.Logger; 51 52 /** 53 * <p>PhysicsVehicleNode - Special PhysicsNode that implements vehicle functions</p> 54 * <p> 55 * <i>From bullet manual:</i><br> 56 * For most vehicle simulations, it is recommended to use the simplified Bullet 57 * vehicle model as provided in btRaycastVehicle. Instead of simulation each wheel 58 * and chassis as separate rigid bodies, connected by constraints, it uses a simplified model. 59 * This simplified model has many benefits, and is widely used in commercial driving games.<br> 60 * The entire vehicle is represented as a single rigidbody, the chassis. 61 * The collision detection of the wheels is approximated by ray casts, 62 * and the tire friction is a basic anisotropic friction model. 63 * </p> 64 * @author normenhansen 65 */ 66 public class PhysicsVehicle extends PhysicsRigidBody { 67 68 protected long vehicleId = 0; 69 protected long rayCasterId = 0; 70 protected VehicleTuning tuning = new VehicleTuning(); 71 protected ArrayList<VehicleWheel> wheels = new ArrayList<VehicleWheel>(); 72 protected PhysicsSpace physicsSpace; 73 PhysicsVehicle()74 public PhysicsVehicle() { 75 } 76 PhysicsVehicle(CollisionShape shape)77 public PhysicsVehicle(CollisionShape shape) { 78 super(shape); 79 } 80 PhysicsVehicle(CollisionShape shape, float mass)81 public PhysicsVehicle(CollisionShape shape, float mass) { 82 super(shape, mass); 83 } 84 85 /** 86 * used internally 87 */ updateWheels()88 public void updateWheels() { 89 if (vehicleId != 0) { 90 for (int i = 0; i < wheels.size(); i++) { 91 updateWheelTransform(vehicleId, i, true); 92 wheels.get(i).updatePhysicsState(); 93 } 94 } 95 } 96 updateWheelTransform(long vehicleId, int wheel, boolean interpolated)97 private native void updateWheelTransform(long vehicleId, int wheel, boolean interpolated); 98 99 /** 100 * used internally 101 */ applyWheelTransforms()102 public void applyWheelTransforms() { 103 if (wheels != null) { 104 for (int i = 0; i < wheels.size(); i++) { 105 wheels.get(i).applyWheelTransform(); 106 } 107 } 108 } 109 110 @Override postRebuild()111 protected void postRebuild() { 112 super.postRebuild(); 113 motionState.setVehicle(this); 114 createVehicle(physicsSpace); 115 } 116 117 /** 118 * Used internally, creates the actual vehicle constraint when vehicle is added to phyicsspace 119 */ createVehicle(PhysicsSpace space)120 public void createVehicle(PhysicsSpace space) { 121 physicsSpace = space; 122 if (space == null) { 123 return; 124 } 125 if (space.getSpaceId() == 0) { 126 throw new IllegalStateException("Physics space is not initialized!"); 127 } 128 if (rayCasterId != 0) { 129 Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing RayCaster {0}", Long.toHexString(rayCasterId)); 130 Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing Vehicle {0}", Long.toHexString(vehicleId)); 131 finalizeNative(rayCasterId, vehicleId); 132 } 133 rayCasterId = createVehicleRaycaster(objectId, space.getSpaceId()); 134 Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created RayCaster {0}", Long.toHexString(rayCasterId)); 135 vehicleId = createRaycastVehicle(objectId, rayCasterId); 136 Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Vehicle {0}", Long.toHexString(vehicleId)); 137 setCoordinateSystem(vehicleId, 0, 1, 2); 138 for (VehicleWheel wheel : wheels) { 139 wheel.setVehicleId(vehicleId, addWheel(vehicleId, wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel())); 140 } 141 } 142 createVehicleRaycaster(long objectId, long physicsSpaceId)143 private native long createVehicleRaycaster(long objectId, long physicsSpaceId); 144 createRaycastVehicle(long objectId, long rayCasterId)145 private native long createRaycastVehicle(long objectId, long rayCasterId); 146 setCoordinateSystem(long objectId, int a, int b, int c)147 private native void setCoordinateSystem(long objectId, int a, int b, int c); 148 addWheel(long objectId, Vector3f location, Vector3f direction, Vector3f axle, float restLength, float radius, VehicleTuning tuning, boolean frontWheel)149 private native int addWheel(long objectId, Vector3f location, Vector3f direction, Vector3f axle, float restLength, float radius, VehicleTuning tuning, boolean frontWheel); 150 151 /** 152 * Add a wheel to this vehicle 153 * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space) 154 * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car) 155 * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car) 156 * @param suspensionRestLength The current length of the suspension (metres) 157 * @param wheelRadius the wheel radius 158 * @param isFrontWheel sets if this wheel is a front wheel (steering) 159 * @return the PhysicsVehicleWheel object to get/set infos on the wheel 160 */ addWheel(Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel)161 public VehicleWheel addWheel(Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) { 162 return addWheel(null, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel); 163 } 164 165 /** 166 * Add a wheel to this vehicle 167 * @param spat the wheel Geometry 168 * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space) 169 * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car) 170 * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car) 171 * @param suspensionRestLength The current length of the suspension (metres) 172 * @param wheelRadius the wheel radius 173 * @param isFrontWheel sets if this wheel is a front wheel (steering) 174 * @return the PhysicsVehicleWheel object to get/set infos on the wheel 175 */ addWheel(Spatial spat, Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel)176 public VehicleWheel addWheel(Spatial spat, Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) { 177 VehicleWheel wheel = null; 178 if (spat == null) { 179 wheel = new VehicleWheel(connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel); 180 } else { 181 wheel = new VehicleWheel(spat, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel); 182 } 183 wheel.setFrictionSlip(tuning.frictionSlip); 184 wheel.setMaxSuspensionTravelCm(tuning.maxSuspensionTravelCm); 185 wheel.setSuspensionStiffness(tuning.suspensionStiffness); 186 wheel.setWheelsDampingCompression(tuning.suspensionCompression); 187 wheel.setWheelsDampingRelaxation(tuning.suspensionDamping); 188 wheel.setMaxSuspensionForce(tuning.maxSuspensionForce); 189 wheels.add(wheel); 190 if (vehicleId != 0) { 191 wheel.setVehicleId(vehicleId, addWheel(vehicleId, wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel())); 192 } 193 if (debugShape != null) { 194 updateDebugShape(); 195 } 196 return wheel; 197 } 198 199 /** 200 * This rebuilds the vehicle as there is no way in bullet to remove a wheel. 201 * @param wheel 202 */ removeWheel(int wheel)203 public void removeWheel(int wheel) { 204 wheels.remove(wheel); 205 rebuildRigidBody(); 206 // updateDebugShape(); 207 } 208 209 /** 210 * You can get access to the single wheels via this method. 211 * @param wheel the wheel index 212 * @return the WheelInfo of the selected wheel 213 */ getWheel(int wheel)214 public VehicleWheel getWheel(int wheel) { 215 return wheels.get(wheel); 216 } 217 getNumWheels()218 public int getNumWheels() { 219 return wheels.size(); 220 } 221 222 /** 223 * @return the frictionSlip 224 */ getFrictionSlip()225 public float getFrictionSlip() { 226 return tuning.frictionSlip; 227 } 228 229 /** 230 * Use before adding wheels, this is the default used when adding wheels. 231 * After adding the wheel, use direct wheel access.<br> 232 * The coefficient of friction between the tyre and the ground. 233 * Should be about 0.8 for realistic cars, but can increased for better handling. 234 * Set large (10000.0) for kart racers 235 * @param frictionSlip the frictionSlip to set 236 */ setFrictionSlip(float frictionSlip)237 public void setFrictionSlip(float frictionSlip) { 238 tuning.frictionSlip = frictionSlip; 239 } 240 241 /** 242 * The coefficient of friction between the tyre and the ground. 243 * Should be about 0.8 for realistic cars, but can increased for better handling. 244 * Set large (10000.0) for kart racers 245 * @param wheel 246 * @param frictionSlip 247 */ setFrictionSlip(int wheel, float frictionSlip)248 public void setFrictionSlip(int wheel, float frictionSlip) { 249 wheels.get(wheel).setFrictionSlip(frictionSlip); 250 } 251 252 /** 253 * Reduces the rolling torque applied from the wheels that cause the vehicle to roll over. 254 * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour. 255 * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over. 256 * You should also try lowering the vehicle's centre of mass 257 */ setRollInfluence(int wheel, float rollInfluence)258 public void setRollInfluence(int wheel, float rollInfluence) { 259 wheels.get(wheel).setRollInfluence(rollInfluence); 260 } 261 262 /** 263 * @return the maxSuspensionTravelCm 264 */ getMaxSuspensionTravelCm()265 public float getMaxSuspensionTravelCm() { 266 return tuning.maxSuspensionTravelCm; 267 } 268 269 /** 270 * Use before adding wheels, this is the default used when adding wheels. 271 * After adding the wheel, use direct wheel access.<br> 272 * The maximum distance the suspension can be compressed (centimetres) 273 * @param maxSuspensionTravelCm the maxSuspensionTravelCm to set 274 */ setMaxSuspensionTravelCm(float maxSuspensionTravelCm)275 public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) { 276 tuning.maxSuspensionTravelCm = maxSuspensionTravelCm; 277 } 278 279 /** 280 * The maximum distance the suspension can be compressed (centimetres) 281 * @param wheel 282 * @param maxSuspensionTravelCm 283 */ setMaxSuspensionTravelCm(int wheel, float maxSuspensionTravelCm)284 public void setMaxSuspensionTravelCm(int wheel, float maxSuspensionTravelCm) { 285 wheels.get(wheel).setMaxSuspensionTravelCm(maxSuspensionTravelCm); 286 } 287 getMaxSuspensionForce()288 public float getMaxSuspensionForce() { 289 return tuning.maxSuspensionForce; 290 } 291 292 /** 293 * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot 294 * handle the weight of your vehcile. 295 * @param maxSuspensionForce 296 */ setMaxSuspensionForce(float maxSuspensionForce)297 public void setMaxSuspensionForce(float maxSuspensionForce) { 298 tuning.maxSuspensionForce = maxSuspensionForce; 299 } 300 301 /** 302 * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot 303 * handle the weight of your vehcile. 304 * @param wheel 305 * @param maxSuspensionForce 306 */ setMaxSuspensionForce(int wheel, float maxSuspensionForce)307 public void setMaxSuspensionForce(int wheel, float maxSuspensionForce) { 308 wheels.get(wheel).setMaxSuspensionForce(maxSuspensionForce); 309 } 310 311 /** 312 * @return the suspensionCompression 313 */ getSuspensionCompression()314 public float getSuspensionCompression() { 315 return tuning.suspensionCompression; 316 } 317 318 /** 319 * Use before adding wheels, this is the default used when adding wheels. 320 * After adding the wheel, use direct wheel access.<br> 321 * The damping coefficient for when the suspension is compressed. 322 * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br> 323 * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br> 324 * 0.1 to 0.3 are good values 325 * @param suspensionCompression the suspensionCompression to set 326 */ setSuspensionCompression(float suspensionCompression)327 public void setSuspensionCompression(float suspensionCompression) { 328 tuning.suspensionCompression = suspensionCompression; 329 } 330 331 /** 332 * The damping coefficient for when the suspension is compressed. 333 * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br> 334 * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br> 335 * 0.1 to 0.3 are good values 336 * @param wheel 337 * @param suspensionCompression 338 */ setSuspensionCompression(int wheel, float suspensionCompression)339 public void setSuspensionCompression(int wheel, float suspensionCompression) { 340 wheels.get(wheel).setWheelsDampingCompression(suspensionCompression); 341 } 342 343 /** 344 * @return the suspensionDamping 345 */ getSuspensionDamping()346 public float getSuspensionDamping() { 347 return tuning.suspensionDamping; 348 } 349 350 /** 351 * Use before adding wheels, this is the default used when adding wheels. 352 * After adding the wheel, use direct wheel access.<br> 353 * The damping coefficient for when the suspension is expanding. 354 * See the comments for setSuspensionCompression for how to set k. 355 * @param suspensionDamping the suspensionDamping to set 356 */ setSuspensionDamping(float suspensionDamping)357 public void setSuspensionDamping(float suspensionDamping) { 358 tuning.suspensionDamping = suspensionDamping; 359 } 360 361 /** 362 * The damping coefficient for when the suspension is expanding. 363 * See the comments for setSuspensionCompression for how to set k. 364 * @param wheel 365 * @param suspensionDamping 366 */ setSuspensionDamping(int wheel, float suspensionDamping)367 public void setSuspensionDamping(int wheel, float suspensionDamping) { 368 wheels.get(wheel).setWheelsDampingRelaxation(suspensionDamping); 369 } 370 371 /** 372 * @return the suspensionStiffness 373 */ getSuspensionStiffness()374 public float getSuspensionStiffness() { 375 return tuning.suspensionStiffness; 376 } 377 378 /** 379 * Use before adding wheels, this is the default used when adding wheels. 380 * After adding the wheel, use direct wheel access.<br> 381 * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car 382 * @param suspensionStiffness 383 */ setSuspensionStiffness(float suspensionStiffness)384 public void setSuspensionStiffness(float suspensionStiffness) { 385 tuning.suspensionStiffness = suspensionStiffness; 386 } 387 388 /** 389 * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car 390 * @param wheel 391 * @param suspensionStiffness 392 */ setSuspensionStiffness(int wheel, float suspensionStiffness)393 public void setSuspensionStiffness(int wheel, float suspensionStiffness) { 394 wheels.get(wheel).setSuspensionStiffness(suspensionStiffness); 395 } 396 397 /** 398 * Reset the suspension 399 */ resetSuspension()400 public void resetSuspension() { 401 resetSuspension(vehicleId); 402 } 403 resetSuspension(long vehicleId)404 private native void resetSuspension(long vehicleId); 405 406 /** 407 * Apply the given engine force to all wheels, works continuously 408 * @param force the force 409 */ accelerate(float force)410 public void accelerate(float force) { 411 for (int i = 0; i < wheels.size(); i++) { 412 accelerate(i, force); 413 } 414 } 415 416 /** 417 * Apply the given engine force, works continuously 418 * @param wheel the wheel to apply the force on 419 * @param force the force 420 */ accelerate(int wheel, float force)421 public void accelerate(int wheel, float force) { 422 applyEngineForce(vehicleId, wheel, force); 423 424 } 425 applyEngineForce(long vehicleId, int wheel, float force)426 private native void applyEngineForce(long vehicleId, int wheel, float force); 427 428 /** 429 * Set the given steering value to all front wheels (0 = forward) 430 * @param value the steering angle of the front wheels (Pi = 360deg) 431 */ steer(float value)432 public void steer(float value) { 433 for (int i = 0; i < wheels.size(); i++) { 434 if (getWheel(i).isFrontWheel()) { 435 steer(i, value); 436 } 437 } 438 } 439 440 /** 441 * Set the given steering value to the given wheel (0 = forward) 442 * @param wheel the wheel to set the steering on 443 * @param value the steering angle of the front wheels (Pi = 360deg) 444 */ steer(int wheel, float value)445 public void steer(int wheel, float value) { 446 steer(vehicleId, wheel, value); 447 } 448 steer(long vehicleId, int wheel, float value)449 private native void steer(long vehicleId, int wheel, float value); 450 451 /** 452 * Apply the given brake force to all wheels, works continuously 453 * @param force the force 454 */ brake(float force)455 public void brake(float force) { 456 for (int i = 0; i < wheels.size(); i++) { 457 brake(i, force); 458 } 459 } 460 461 /** 462 * Apply the given brake force, works continuously 463 * @param wheel the wheel to apply the force on 464 * @param force the force 465 */ brake(int wheel, float force)466 public void brake(int wheel, float force) { 467 brake(vehicleId, wheel, force); 468 } 469 brake(long vehicleId, int wheel, float force)470 private native void brake(long vehicleId, int wheel, float force); 471 472 /** 473 * Get the current speed of the vehicle in km/h 474 * @return 475 */ getCurrentVehicleSpeedKmHour()476 public float getCurrentVehicleSpeedKmHour() { 477 return getCurrentVehicleSpeedKmHour(vehicleId); 478 } 479 getCurrentVehicleSpeedKmHour(long vehicleId)480 private native float getCurrentVehicleSpeedKmHour(long vehicleId); 481 482 /** 483 * Get the current forward vector of the vehicle in world coordinates 484 * @param vector 485 * @return 486 */ getForwardVector(Vector3f vector)487 public Vector3f getForwardVector(Vector3f vector) { 488 if (vector == null) { 489 vector = new Vector3f(); 490 } 491 getForwardVector(vehicleId, vector); 492 return vector; 493 } 494 getForwardVector(long objectId, Vector3f vector)495 private native void getForwardVector(long objectId, Vector3f vector); 496 497 /** 498 * used internally 499 */ getVehicleId()500 public long getVehicleId() { 501 return vehicleId; 502 } 503 504 @Override getDebugShape()505 protected Spatial getDebugShape() { 506 Spatial shape = super.getDebugShape(); 507 Node node = null; 508 if (shape instanceof Node) { 509 node = (Node) shape; 510 } else { 511 node = new Node("DebugShapeNode"); 512 node.attachChild(shape); 513 } 514 int i = 0; 515 for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) { 516 VehicleWheel physicsVehicleWheel = it.next(); 517 Vector3f location = physicsVehicleWheel.getLocation().clone(); 518 Vector3f direction = physicsVehicleWheel.getDirection().clone(); 519 Vector3f axle = physicsVehicleWheel.getAxle().clone(); 520 float restLength = physicsVehicleWheel.getRestLength(); 521 float radius = physicsVehicleWheel.getRadius(); 522 523 Arrow locArrow = new Arrow(location); 524 Arrow axleArrow = new Arrow(axle.normalizeLocal().multLocal(0.3f)); 525 Arrow wheelArrow = new Arrow(direction.normalizeLocal().multLocal(radius)); 526 Arrow dirArrow = new Arrow(direction.normalizeLocal().multLocal(restLength)); 527 Geometry locGeom = new Geometry("WheelLocationDebugShape" + i, locArrow); 528 Geometry dirGeom = new Geometry("WheelDirectionDebugShape" + i, dirArrow); 529 Geometry axleGeom = new Geometry("WheelAxleDebugShape" + i, axleArrow); 530 Geometry wheelGeom = new Geometry("WheelRadiusDebugShape" + i, wheelArrow); 531 dirGeom.setLocalTranslation(location); 532 axleGeom.setLocalTranslation(location.add(direction)); 533 wheelGeom.setLocalTranslation(location.add(direction)); 534 locGeom.setMaterial(debugMaterialGreen); 535 dirGeom.setMaterial(debugMaterialGreen); 536 axleGeom.setMaterial(debugMaterialGreen); 537 wheelGeom.setMaterial(debugMaterialGreen); 538 node.attachChild(locGeom); 539 node.attachChild(dirGeom); 540 node.attachChild(axleGeom); 541 node.attachChild(wheelGeom); 542 i++; 543 } 544 return node; 545 } 546 547 @Override read(JmeImporter im)548 public void read(JmeImporter im) throws IOException { 549 InputCapsule capsule = im.getCapsule(this); 550 tuning = new VehicleTuning(); 551 tuning.frictionSlip = capsule.readFloat("frictionSlip", 10.5f); 552 tuning.maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f); 553 tuning.maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f); 554 tuning.suspensionCompression = capsule.readFloat("suspensionCompression", 0.83f); 555 tuning.suspensionDamping = capsule.readFloat("suspensionDamping", 0.88f); 556 tuning.suspensionStiffness = capsule.readFloat("suspensionStiffness", 5.88f); 557 wheels = capsule.readSavableArrayList("wheelsList", new ArrayList<VehicleWheel>()); 558 motionState.setVehicle(this); 559 super.read(im); 560 } 561 562 @Override write(JmeExporter ex)563 public void write(JmeExporter ex) throws IOException { 564 OutputCapsule capsule = ex.getCapsule(this); 565 capsule.write(tuning.frictionSlip, "frictionSlip", 10.5f); 566 capsule.write(tuning.maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f); 567 capsule.write(tuning.maxSuspensionForce, "maxSuspensionForce", 6000f); 568 capsule.write(tuning.suspensionCompression, "suspensionCompression", 0.83f); 569 capsule.write(tuning.suspensionDamping, "suspensionDamping", 0.88f); 570 capsule.write(tuning.suspensionStiffness, "suspensionStiffness", 5.88f); 571 capsule.writeSavableArrayList(wheels, "wheelsList", new ArrayList<VehicleWheel>()); 572 super.write(ex); 573 } 574 575 @Override finalize()576 protected void finalize() throws Throwable { 577 super.finalize(); 578 Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing RayCaster {0}", Long.toHexString(rayCasterId)); 579 Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing Vehicle {0}", Long.toHexString(vehicleId)); 580 finalizeNative(rayCasterId, vehicleId); 581 } 582 finalizeNative(long rayCaster, long vehicle)583 private native void finalizeNative(long rayCaster, long vehicle); 584 } 585