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 jme3test.bullet; 33 34 import com.jme3.bullet.PhysicsSpace; 35 import com.jme3.bullet.PhysicsTickListener; 36 import com.jme3.bullet.collision.shapes.CollisionShape; 37 import com.jme3.bullet.control.PhysicsControl; 38 import com.jme3.bullet.objects.PhysicsVehicle; 39 import com.jme3.export.InputCapsule; 40 import com.jme3.export.JmeExporter; 41 import com.jme3.export.JmeImporter; 42 import com.jme3.export.OutputCapsule; 43 import com.jme3.math.FastMath; 44 import com.jme3.math.Vector3f; 45 import com.jme3.renderer.RenderManager; 46 import com.jme3.renderer.ViewPort; 47 import com.jme3.scene.Spatial; 48 import com.jme3.scene.control.Control; 49 import java.io.IOException; 50 51 /** 52 * PhysicsHoverControl uses a RayCast Vehicle with "slippery wheels" to simulate a hovering tank 53 * @author normenhansen 54 */ 55 public class PhysicsHoverControl extends PhysicsVehicle implements PhysicsControl, PhysicsTickListener { 56 57 protected Spatial spatial; 58 protected boolean enabled = true; 59 protected PhysicsSpace space = null; 60 protected float steeringValue = 0; 61 protected float accelerationValue = 0; 62 protected int xw = 3; 63 protected int zw = 5; 64 protected int yw = 2; 65 protected Vector3f HOVER_HEIGHT_LF_START = new Vector3f(xw, 1, zw); 66 protected Vector3f HOVER_HEIGHT_RF_START = new Vector3f(-xw, 1, zw); 67 protected Vector3f HOVER_HEIGHT_LR_START = new Vector3f(xw, 1, -zw); 68 protected Vector3f HOVER_HEIGHT_RR_START = new Vector3f(-xw, 1, -zw); 69 protected Vector3f HOVER_HEIGHT_LF = new Vector3f(xw, -yw, zw); 70 protected Vector3f HOVER_HEIGHT_RF = new Vector3f(-xw, -yw, zw); 71 protected Vector3f HOVER_HEIGHT_LR = new Vector3f(xw, -yw, -zw); 72 protected Vector3f HOVER_HEIGHT_RR = new Vector3f(-xw, -yw, -zw); 73 protected Vector3f tempVect1 = new Vector3f(0, 0, 0); 74 protected Vector3f tempVect2 = new Vector3f(0, 0, 0); 75 protected Vector3f tempVect3 = new Vector3f(0, 0, 0); 76 // protected float rotationCounterForce = 10000f; 77 // protected float speedCounterMult = 2000f; 78 // protected float multiplier = 1000f; 79 80 public PhysicsHoverControl() { 81 } 82 83 /** 84 * Creates a new PhysicsNode with the supplied collision shape 85 * @param shape 86 */ 87 public PhysicsHoverControl(CollisionShape shape) { 88 super(shape); 89 createWheels(); 90 } 91 92 public PhysicsHoverControl(CollisionShape shape, float mass) { 93 super(shape, mass); 94 createWheels(); 95 } 96 97 public Control cloneForSpatial(Spatial spatial) { 98 throw new UnsupportedOperationException("Not supported yet."); 99 } 100 101 public void setSpatial(Spatial spatial) { 102 this.spatial = spatial; 103 setUserObject(spatial); 104 if (spatial == null) { 105 return; 106 } 107 setPhysicsLocation(spatial.getWorldTranslation()); 108 setPhysicsRotation(spatial.getWorldRotation().toRotationMatrix()); 109 } 110 111 public void setEnabled(boolean enabled) { 112 this.enabled = enabled; 113 } 114 115 public boolean isEnabled() { 116 return enabled; 117 } 118 119 private void createWheels() { 120 addWheel(HOVER_HEIGHT_LF_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false); 121 addWheel(HOVER_HEIGHT_RF_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false); 122 addWheel(HOVER_HEIGHT_LR_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false); 123 addWheel(HOVER_HEIGHT_RR_START, new Vector3f(0, -1, 0), new Vector3f(-1, 0, 0), yw, yw, false); 124 for (int i = 0; i < 4; i++) { 125 getWheel(i).setFrictionSlip(0.001f); 126 } 127 } 128 129 public void prePhysicsTick(PhysicsSpace space, float f) { 130 Vector3f angVel = getAngularVelocity(); 131 float rotationVelocity = angVel.getY(); 132 Vector3f dir = getForwardVector(tempVect2).multLocal(1, 0, 1).normalizeLocal(); 133 getLinearVelocity(tempVect3); 134 Vector3f linearVelocity = tempVect3.multLocal(1, 0, 1); 135 136 if (steeringValue != 0) { 137 if (rotationVelocity < 1 && rotationVelocity > -1) { 138 applyTorque(tempVect1.set(0, steeringValue, 0)); 139 } 140 } else { 141 // counter the steering value! 142 if (rotationVelocity > 0.2f) { 143 applyTorque(tempVect1.set(0, -mass * 20, 0)); 144 } else if (rotationVelocity < -0.2f) { 145 applyTorque(tempVect1.set(0, mass * 20, 0)); 146 } 147 } 148 if (accelerationValue > 0) { 149 // counter force that will adjust velocity 150 // if we are not going where we want to go. 151 // this will prevent "drifting" and thus improve control 152 // of the vehicle 153 float d = dir.dot(linearVelocity.normalize()); 154 Vector3f counter = dir.project(linearVelocity).normalizeLocal().negateLocal().multLocal(1 - d); 155 applyForce(counter.multLocal(mass * 10), Vector3f.ZERO); 156 157 if (linearVelocity.length() < 30) { 158 applyForce(dir.multLocal(accelerationValue), Vector3f.ZERO); 159 } 160 } else { 161 // counter the acceleration value 162 if (linearVelocity.length() > FastMath.ZERO_TOLERANCE) { 163 linearVelocity.normalizeLocal().negateLocal(); 164 applyForce(linearVelocity.mult(mass * 10), Vector3f.ZERO); 165 } 166 } 167 } 168 169 public void physicsTick(PhysicsSpace space, float f) { 170 } 171 172 public void update(float tpf) { 173 if (enabled && spatial != null) { 174 getMotionState().applyTransform(spatial); 175 } 176 } 177 178 public void render(RenderManager rm, ViewPort vp) { 179 if (enabled && space != null && space.getDebugManager() != null) { 180 if (debugShape == null) { 181 attachDebugShape(space.getDebugManager()); 182 } 183 debugShape.setLocalTranslation(motionState.getWorldLocation()); 184 debugShape.setLocalRotation(motionState.getWorldRotation()); 185 debugShape.updateLogicalState(0); 186 debugShape.updateGeometricState(); 187 rm.renderScene(debugShape, vp); 188 } 189 } 190 191 public void setPhysicsSpace(PhysicsSpace space) { 192 if (space == null) { 193 if (this.space != null) { 194 this.space.removeCollisionObject(this); 195 this.space.removeTickListener(this); 196 } 197 this.space = space; 198 } else { 199 space.addCollisionObject(this); 200 space.addTickListener(this); 201 } 202 this.space = space; 203 } 204 205 public PhysicsSpace getPhysicsSpace() { 206 return space; 207 } 208 209 @Override 210 public void write(JmeExporter ex) throws IOException { 211 super.write(ex); 212 OutputCapsule oc = ex.getCapsule(this); 213 oc.write(enabled, "enabled", true); 214 oc.write(spatial, "spatial", null); 215 } 216 217 @Override 218 public void read(JmeImporter im) throws IOException { 219 super.read(im); 220 InputCapsule ic = im.getCapsule(this); 221 enabled = ic.readBoolean("enabled", true); 222 spatial = (Spatial) ic.readSavable("spatial", null); 223 } 224 225 /** 226 * @param steeringValue the steeringValue to set 227 */ 228 @Override 229 public void steer(float steeringValue) { 230 this.steeringValue = steeringValue * getMass(); 231 } 232 233 /** 234 * @param accelerationValue the accelerationValue to set 235 */ 236 @Override 237 public void accelerate(float accelerationValue) { 238 this.accelerationValue = accelerationValue * getMass(); 239 } 240 } 241