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