Home | History | Annotate | Download | only in objects
      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.collision.PhysicsCollisionObject;
     35 import com.jme3.export.*;
     36 import com.jme3.math.Matrix3f;
     37 import com.jme3.math.Quaternion;
     38 import com.jme3.math.Vector3f;
     39 import com.jme3.scene.Spatial;
     40 import java.io.IOException;
     41 
     42 /**
     43  * Stores info about one wheel of a PhysicsVehicle
     44  * @author normenhansen
     45  */
     46 public 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