| /* |
| * Copyright (c) 2009-2012 jMonkeyEngine |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| package com.jme3.bullet.objects; |
| |
| import com.bulletphysics.dynamics.RigidBody; |
| import com.jme3.bullet.collision.PhysicsCollisionObject; |
| import com.jme3.bullet.util.Converter; |
| import com.jme3.export.*; |
| import com.jme3.math.Quaternion; |
| import com.jme3.math.Vector3f; |
| import com.jme3.scene.Spatial; |
| import java.io.IOException; |
| |
| /** |
| * Stores info about one wheel of a PhysicsVehicle |
| * @author normenhansen |
| */ |
| public class VehicleWheel implements Savable { |
| |
| protected com.bulletphysics.dynamics.vehicle.WheelInfo wheelInfo; |
| protected boolean frontWheel; |
| protected Vector3f location = new Vector3f(); |
| protected Vector3f direction = new Vector3f(); |
| protected Vector3f axle = new Vector3f(); |
| protected float suspensionStiffness = 20.0f; |
| protected float wheelsDampingRelaxation = 2.3f; |
| protected float wheelsDampingCompression = 4.4f; |
| protected float frictionSlip = 10.5f; |
| protected float rollInfluence = 1.0f; |
| protected float maxSuspensionTravelCm = 500f; |
| protected float maxSuspensionForce = 6000f; |
| protected float radius = 0.5f; |
| protected float restLength = 1f; |
| protected Vector3f wheelWorldLocation = new Vector3f(); |
| protected Quaternion wheelWorldRotation = new Quaternion(); |
| protected Spatial wheelSpatial; |
| protected com.jme3.math.Matrix3f tmp_Matrix = new com.jme3.math.Matrix3f(); |
| protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); |
| private boolean applyLocal = false; |
| |
| public VehicleWheel() { |
| } |
| |
| public VehicleWheel(Spatial spat, Vector3f location, Vector3f direction, Vector3f axle, |
| float restLength, float radius, boolean frontWheel) { |
| this(location, direction, axle, restLength, radius, frontWheel); |
| wheelSpatial = spat; |
| } |
| |
| public VehicleWheel(Vector3f location, Vector3f direction, Vector3f axle, |
| float restLength, float radius, boolean frontWheel) { |
| this.location.set(location); |
| this.direction.set(direction); |
| this.axle.set(axle); |
| this.frontWheel = frontWheel; |
| this.restLength = restLength; |
| this.radius = radius; |
| } |
| |
| public synchronized void updatePhysicsState() { |
| Converter.convert(wheelInfo.worldTransform.origin, wheelWorldLocation); |
| Converter.convert(wheelInfo.worldTransform.basis, tmp_Matrix); |
| wheelWorldRotation.fromRotationMatrix(tmp_Matrix); |
| } |
| |
| public synchronized void applyWheelTransform() { |
| if (wheelSpatial == null) { |
| return; |
| } |
| Quaternion localRotationQuat = wheelSpatial.getLocalRotation(); |
| Vector3f localLocation = wheelSpatial.getLocalTranslation(); |
| if (!applyLocal && wheelSpatial.getParent() != null) { |
| localLocation.set(wheelWorldLocation).subtractLocal(wheelSpatial.getParent().getWorldTranslation()); |
| localLocation.divideLocal(wheelSpatial.getParent().getWorldScale()); |
| tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation); |
| |
| localRotationQuat.set(wheelWorldRotation); |
| tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat); |
| |
| wheelSpatial.setLocalTranslation(localLocation); |
| wheelSpatial.setLocalRotation(localRotationQuat); |
| } else { |
| wheelSpatial.setLocalTranslation(wheelWorldLocation); |
| wheelSpatial.setLocalRotation(wheelWorldRotation); |
| } |
| } |
| |
| public com.bulletphysics.dynamics.vehicle.WheelInfo getWheelInfo() { |
| return wheelInfo; |
| } |
| |
| public void setWheelInfo(com.bulletphysics.dynamics.vehicle.WheelInfo wheelInfo) { |
| this.wheelInfo = wheelInfo; |
| applyInfo(); |
| } |
| |
| public boolean isFrontWheel() { |
| return frontWheel; |
| } |
| |
| public void setFrontWheel(boolean frontWheel) { |
| this.frontWheel = frontWheel; |
| applyInfo(); |
| } |
| |
| public Vector3f getLocation() { |
| return location; |
| } |
| |
| public Vector3f getDirection() { |
| return direction; |
| } |
| |
| public Vector3f getAxle() { |
| return axle; |
| } |
| |
| public float getSuspensionStiffness() { |
| return suspensionStiffness; |
| } |
| |
| /** |
| * the stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car |
| * @param suspensionStiffness |
| */ |
| public void setSuspensionStiffness(float suspensionStiffness) { |
| this.suspensionStiffness = suspensionStiffness; |
| applyInfo(); |
| } |
| |
| public float getWheelsDampingRelaxation() { |
| return wheelsDampingRelaxation; |
| } |
| |
| /** |
| * the damping coefficient for when the suspension is expanding. |
| * See the comments for setWheelsDampingCompression for how to set k. |
| * @param wheelsDampingRelaxation |
| */ |
| public void setWheelsDampingRelaxation(float wheelsDampingRelaxation) { |
| this.wheelsDampingRelaxation = wheelsDampingRelaxation; |
| applyInfo(); |
| } |
| |
| public float getWheelsDampingCompression() { |
| return wheelsDampingCompression; |
| } |
| |
| /** |
| * the damping coefficient for when the suspension is compressed. |
| * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br> |
| * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br> |
| * 0.1 to 0.3 are good values |
| * @param wheelsDampingCompression |
| */ |
| public void setWheelsDampingCompression(float wheelsDampingCompression) { |
| this.wheelsDampingCompression = wheelsDampingCompression; |
| applyInfo(); |
| } |
| |
| public float getFrictionSlip() { |
| return frictionSlip; |
| } |
| |
| /** |
| * the coefficient of friction between the tyre and the ground. |
| * Should be about 0.8 for realistic cars, but can increased for better handling. |
| * Set large (10000.0) for kart racers |
| * @param frictionSlip |
| */ |
| public void setFrictionSlip(float frictionSlip) { |
| this.frictionSlip = frictionSlip; |
| applyInfo(); |
| } |
| |
| public float getRollInfluence() { |
| return rollInfluence; |
| } |
| |
| /** |
| * reduces the rolling torque applied from the wheels that cause the vehicle to roll over. |
| * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour. |
| * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over. |
| * You should also try lowering the vehicle's centre of mass |
| * @param rollInfluence the rollInfluence to set |
| */ |
| public void setRollInfluence(float rollInfluence) { |
| this.rollInfluence = rollInfluence; |
| applyInfo(); |
| } |
| |
| public float getMaxSuspensionTravelCm() { |
| return maxSuspensionTravelCm; |
| } |
| |
| /** |
| * the maximum distance the suspension can be compressed (centimetres) |
| * @param maxSuspensionTravelCm |
| */ |
| public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) { |
| this.maxSuspensionTravelCm = maxSuspensionTravelCm; |
| applyInfo(); |
| } |
| |
| public float getMaxSuspensionForce() { |
| return maxSuspensionForce; |
| } |
| |
| /** |
| * The maximum suspension force, raise this above the default 6000 if your suspension cannot |
| * handle the weight of your vehcile. |
| * @param maxSuspensionForce |
| */ |
| public void setMaxSuspensionForce(float maxSuspensionForce) { |
| this.maxSuspensionForce = maxSuspensionForce; |
| applyInfo(); |
| } |
| |
| private void applyInfo() { |
| if (wheelInfo == null) { |
| return; |
| } |
| wheelInfo.suspensionStiffness = suspensionStiffness; |
| wheelInfo.wheelsDampingRelaxation = wheelsDampingRelaxation; |
| wheelInfo.wheelsDampingCompression = wheelsDampingCompression; |
| wheelInfo.frictionSlip = frictionSlip; |
| wheelInfo.rollInfluence = rollInfluence; |
| wheelInfo.maxSuspensionTravelCm = maxSuspensionTravelCm; |
| wheelInfo.maxSuspensionForce = maxSuspensionForce; |
| wheelInfo.wheelsRadius = radius; |
| wheelInfo.bIsFrontWheel = frontWheel; |
| wheelInfo.suspensionRestLength1 = restLength; |
| } |
| |
| public float getRadius() { |
| return radius; |
| } |
| |
| public void setRadius(float radius) { |
| this.radius = radius; |
| applyInfo(); |
| } |
| |
| public float getRestLength() { |
| return restLength; |
| } |
| |
| public void setRestLength(float restLength) { |
| this.restLength = restLength; |
| applyInfo(); |
| } |
| |
| /** |
| * returns the object this wheel is in contact with or null if no contact |
| * @return the PhysicsCollisionObject (PhysicsRigidBody, PhysicsGhostObject) |
| */ |
| public PhysicsCollisionObject getGroundObject() { |
| if (wheelInfo.raycastInfo.groundObject == null) { |
| return null; |
| } else if (wheelInfo.raycastInfo.groundObject instanceof RigidBody) { |
| System.out.println("RigidBody"); |
| return (PhysicsRigidBody) ((RigidBody) wheelInfo.raycastInfo.groundObject).getUserPointer(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * returns the location where the wheel collides with the ground (world space) |
| */ |
| public Vector3f getCollisionLocation(Vector3f vec) { |
| Converter.convert(wheelInfo.raycastInfo.contactPointWS, vec); |
| return vec; |
| } |
| |
| /** |
| * returns the location where the wheel collides with the ground (world space) |
| */ |
| public Vector3f getCollisionLocation() { |
| return Converter.convert(wheelInfo.raycastInfo.contactPointWS); |
| } |
| |
| /** |
| * returns the normal where the wheel collides with the ground (world space) |
| */ |
| public Vector3f getCollisionNormal(Vector3f vec) { |
| Converter.convert(wheelInfo.raycastInfo.contactNormalWS, vec); |
| return vec; |
| } |
| |
| /** |
| * returns the normal where the wheel collides with the ground (world space) |
| */ |
| public Vector3f getCollisionNormal() { |
| return Converter.convert(wheelInfo.raycastInfo.contactNormalWS); |
| } |
| |
| /** |
| * returns how much the wheel skids on the ground (for skid sounds/smoke etc.)<br> |
| * 0.0 = wheels are sliding, 1.0 = wheels have traction. |
| */ |
| public float getSkidInfo() { |
| return wheelInfo.skidInfo; |
| } |
| |
| /** |
| * returns how many degrees the wheel has turned since the last physics |
| * step. |
| */ |
| public float getDeltaRotation() { |
| return wheelInfo.deltaRotation; |
| } |
| |
| @Override |
| public void read(JmeImporter im) throws IOException { |
| InputCapsule capsule = im.getCapsule(this); |
| wheelSpatial = (Spatial) capsule.readSavable("wheelSpatial", null); |
| frontWheel = capsule.readBoolean("frontWheel", false); |
| location = (Vector3f) capsule.readSavable("wheelLocation", new Vector3f()); |
| direction = (Vector3f) capsule.readSavable("wheelDirection", new Vector3f()); |
| axle = (Vector3f) capsule.readSavable("wheelAxle", new Vector3f()); |
| suspensionStiffness = capsule.readFloat("suspensionStiffness", 20.0f); |
| wheelsDampingRelaxation = capsule.readFloat("wheelsDampingRelaxation", 2.3f); |
| wheelsDampingCompression = capsule.readFloat("wheelsDampingCompression", 4.4f); |
| frictionSlip = capsule.readFloat("frictionSlip", 10.5f); |
| rollInfluence = capsule.readFloat("rollInfluence", 1.0f); |
| maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f); |
| maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f); |
| radius = capsule.readFloat("wheelRadius", 0.5f); |
| restLength = capsule.readFloat("restLength", 1f); |
| } |
| |
| @Override |
| public void write(JmeExporter ex) throws IOException { |
| OutputCapsule capsule = ex.getCapsule(this); |
| capsule.write(wheelSpatial, "wheelSpatial", null); |
| capsule.write(frontWheel, "frontWheel", false); |
| capsule.write(location, "wheelLocation", new Vector3f()); |
| capsule.write(direction, "wheelDirection", new Vector3f()); |
| capsule.write(axle, "wheelAxle", new Vector3f()); |
| capsule.write(suspensionStiffness, "suspensionStiffness", 20.0f); |
| capsule.write(wheelsDampingRelaxation, "wheelsDampingRelaxation", 2.3f); |
| capsule.write(wheelsDampingCompression, "wheelsDampingCompression", 4.4f); |
| capsule.write(frictionSlip, "frictionSlip", 10.5f); |
| capsule.write(rollInfluence, "rollInfluence", 1.0f); |
| capsule.write(maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f); |
| capsule.write(maxSuspensionForce, "maxSuspensionForce", 6000f); |
| capsule.write(radius, "wheelRadius", 0.5f); |
| capsule.write(restLength, "restLength", 1f); |
| } |
| |
| /** |
| * @return the wheelSpatial |
| */ |
| public Spatial getWheelSpatial() { |
| return wheelSpatial; |
| } |
| |
| /** |
| * @param wheelSpatial the wheelSpatial to set |
| */ |
| public void setWheelSpatial(Spatial wheelSpatial) { |
| this.wheelSpatial = wheelSpatial; |
| } |
| |
| public boolean isApplyLocal() { |
| return applyLocal; |
| } |
| |
| public void setApplyLocal(boolean applyLocal) { |
| this.applyLocal = applyLocal; |
| } |
| } |