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.bulletphysics.collision.dispatch.CollisionFlags;
35 import com.bulletphysics.collision.dispatch.PairCachingGhostObject;
36 import com.bulletphysics.collision.shapes.ConvexShape;
37 import com.bulletphysics.dynamics.character.KinematicCharacterController;
38 import com.bulletphysics.linearmath.Transform;
39 import com.jme3.bullet.collision.PhysicsCollisionObject;
40 import com.jme3.bullet.collision.shapes.CollisionShape;
41 import com.jme3.bullet.util.Converter;
42 import com.jme3.export.InputCapsule;
43 import com.jme3.export.JmeExporter;
44 import com.jme3.export.JmeImporter;
45 import com.jme3.export.OutputCapsule;
46 import com.jme3.math.Matrix3f;
47 import com.jme3.math.Quaternion;
48 import com.jme3.math.Vector3f;
49 import java.io.IOException;
50 
51 /**
52  * Basic Bullet Character
53  * @author normenhansen
54  */
55 public class PhysicsCharacter extends PhysicsCollisionObject {
56 
57     protected KinematicCharacterController character;
58     protected float stepHeight;
59     protected Vector3f walkDirection = new Vector3f();
60     protected float fallSpeed = 55.0f;
61     protected float jumpSpeed = 10.0f;
62     protected int upAxis = 1;
63     protected PairCachingGhostObject gObject;
64     protected boolean locationDirty = false;
65     //TEMP VARIABLES
66     protected final Quaternion tmp_inverseWorldRotation = new Quaternion();
67     private Transform tempTrans = new Transform(Converter.convert(new Matrix3f()));
68     private com.jme3.math.Transform physicsLocation = new com.jme3.math.Transform();
69     private javax.vecmath.Vector3f tempVec = new javax.vecmath.Vector3f();
70 
PhysicsCharacter()71     public PhysicsCharacter() {
72     }
73 
74     /**
75      * @param shape The CollisionShape (no Mesh or CompoundCollisionShapes)
76      * @param stepHeight The quantization size for vertical movement
77      */
PhysicsCharacter(CollisionShape shape, float stepHeight)78     public PhysicsCharacter(CollisionShape shape, float stepHeight) {
79         this.collisionShape = shape;
80         if (!(shape.getCShape() instanceof ConvexShape)) {
81             throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes"));
82         }
83         this.stepHeight = stepHeight;
84         buildObject();
85     }
86 
buildObject()87     protected void buildObject() {
88         if (gObject == null) {
89             gObject = new PairCachingGhostObject();
90         }
91         gObject.setCollisionFlags(CollisionFlags.CHARACTER_OBJECT);
92         gObject.setCollisionFlags(gObject.getCollisionFlags() & ~CollisionFlags.NO_CONTACT_RESPONSE);
93         gObject.setCollisionShape(collisionShape.getCShape());
94         gObject.setUserPointer(this);
95         character = new KinematicCharacterController(gObject, (ConvexShape) collisionShape.getCShape(), stepHeight);
96     }
97 
98     /**
99      * Sets the location of this physics character
100      * @param location
101      */
warp(Vector3f location)102     public void warp(Vector3f location) {
103         character.warp(Converter.convert(location, tempVec));
104     }
105 
106     /**
107      * Set the walk direction, works continuously.
108      * This should probably be called setPositionIncrementPerSimulatorStep.
109      * This is neither a direction nor a velocity, but the amount to
110      * increment the position each physics tick. So vector length = accuracy*speed in m/s
111      * @param vec the walk direction to set
112      */
setWalkDirection(Vector3f vec)113     public void setWalkDirection(Vector3f vec) {
114         walkDirection.set(vec);
115         character.setWalkDirection(Converter.convert(walkDirection, tempVec));
116     }
117 
118     /**
119      * @return the currently set walkDirection
120      */
getWalkDirection()121     public Vector3f getWalkDirection() {
122         return walkDirection;
123     }
124 
setUpAxis(int axis)125     public void setUpAxis(int axis) {
126         upAxis = axis;
127         character.setUpAxis(axis);
128     }
129 
getUpAxis()130     public int getUpAxis() {
131         return upAxis;
132     }
133 
setFallSpeed(float fallSpeed)134     public void setFallSpeed(float fallSpeed) {
135         this.fallSpeed = fallSpeed;
136         character.setFallSpeed(fallSpeed);
137     }
138 
getFallSpeed()139     public float getFallSpeed() {
140         return fallSpeed;
141     }
142 
setJumpSpeed(float jumpSpeed)143     public void setJumpSpeed(float jumpSpeed) {
144         this.jumpSpeed = jumpSpeed;
145         character.setJumpSpeed(jumpSpeed);
146     }
147 
getJumpSpeed()148     public float getJumpSpeed() {
149         return jumpSpeed;
150     }
151 
152     //does nothing..
153 //    public void setMaxJumpHeight(float height) {
154 //        character.setMaxJumpHeight(height);
155 //    }
setGravity(float value)156     public void setGravity(float value) {
157         character.setGravity(value);
158     }
159 
getGravity()160     public float getGravity() {
161         return character.getGravity();
162     }
163 
setMaxSlope(float slopeRadians)164     public void setMaxSlope(float slopeRadians) {
165         character.setMaxSlope(slopeRadians);
166     }
167 
getMaxSlope()168     public float getMaxSlope() {
169         return character.getMaxSlope();
170     }
171 
onGround()172     public boolean onGround() {
173         return character.onGround();
174     }
175 
jump()176     public void jump() {
177         character.jump();
178     }
179 
180     @Override
setCollisionShape(CollisionShape collisionShape)181     public void setCollisionShape(CollisionShape collisionShape) {
182         if (!(collisionShape.getCShape() instanceof ConvexShape)) {
183             throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes"));
184         }
185         super.setCollisionShape(collisionShape);
186         if (gObject == null) {
187             buildObject();
188         }else{
189             gObject.setCollisionShape(collisionShape.getCShape());
190         }
191     }
192 
193     /**
194      * Set the physics location (same as warp())
195      * @param location the location of the actual physics object
196      */
setPhysicsLocation(Vector3f location)197     public void setPhysicsLocation(Vector3f location) {
198         warp(location);
199     }
200 
201     /**
202      * @return the physicsLocation
203      */
getPhysicsLocation(Vector3f trans)204     public Vector3f getPhysicsLocation(Vector3f trans) {
205         if (trans == null) {
206             trans = new Vector3f();
207         }
208         gObject.getWorldTransform(tempTrans);
209         Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
210         return trans.set(physicsLocation.getTranslation());
211     }
212 
213     /**
214      * @return the physicsLocation
215      */
getPhysicsLocation()216     public Vector3f getPhysicsLocation() {
217         gObject.getWorldTransform(tempTrans);
218         Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
219         return physicsLocation.getTranslation();
220     }
221 
setCcdSweptSphereRadius(float radius)222     public void setCcdSweptSphereRadius(float radius) {
223         gObject.setCcdSweptSphereRadius(radius);
224     }
225 
setCcdMotionThreshold(float threshold)226     public void setCcdMotionThreshold(float threshold) {
227         gObject.setCcdMotionThreshold(threshold);
228     }
229 
getCcdSweptSphereRadius()230     public float getCcdSweptSphereRadius() {
231         return gObject.getCcdSweptSphereRadius();
232     }
233 
getCcdMotionThreshold()234     public float getCcdMotionThreshold() {
235         return gObject.getCcdMotionThreshold();
236     }
237 
getCcdSquareMotionThreshold()238     public float getCcdSquareMotionThreshold() {
239         return gObject.getCcdSquareMotionThreshold();
240     }
241 
242     /**
243      * used internally
244      */
getControllerId()245     public KinematicCharacterController getControllerId() {
246         return character;
247     }
248 
249     /**
250      * used internally
251      */
getObjectId()252     public PairCachingGhostObject getObjectId() {
253         return gObject;
254     }
255 
destroy()256     public void destroy() {
257     }
258 
259     @Override
write(JmeExporter e)260     public void write(JmeExporter e) throws IOException {
261         super.write(e);
262         OutputCapsule capsule = e.getCapsule(this);
263         capsule.write(stepHeight, "stepHeight", 1.0f);
264         capsule.write(getGravity(), "gravity", 9.8f * 3);
265         capsule.write(getMaxSlope(), "maxSlope", 1.0f);
266         capsule.write(fallSpeed, "fallSpeed", 55.0f);
267         capsule.write(jumpSpeed, "jumpSpeed", 10.0f);
268         capsule.write(upAxis, "upAxis", 1);
269         capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0);
270         capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0);
271         capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f());
272     }
273 
274     @Override
read(JmeImporter e)275     public void read(JmeImporter e) throws IOException {
276         super.read(e);
277         InputCapsule capsule = e.getCapsule(this);
278         stepHeight = capsule.readFloat("stepHeight", 1.0f);
279         buildObject();
280         character = new KinematicCharacterController(gObject, (ConvexShape) collisionShape.getCShape(), stepHeight);
281         setGravity(capsule.readFloat("gravity", 9.8f * 3));
282         setMaxSlope(capsule.readFloat("maxSlope", 1.0f));
283         setFallSpeed(capsule.readFloat("fallSpeed", 55.0f));
284         setJumpSpeed(capsule.readFloat("jumpSpeed", 10.0f));
285         setUpAxis(capsule.readInt("upAxis", 1));
286         setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0));
287         setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0));
288         setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f()));
289     }
290 }
291