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 */
32package com.jme3.bullet.objects;
33
34import com.bulletphysics.collision.dispatch.CollisionFlags;
35import com.bulletphysics.collision.dispatch.PairCachingGhostObject;
36import com.bulletphysics.collision.shapes.ConvexShape;
37import com.bulletphysics.dynamics.character.KinematicCharacterController;
38import com.bulletphysics.linearmath.Transform;
39import com.jme3.bullet.collision.PhysicsCollisionObject;
40import com.jme3.bullet.collision.shapes.CollisionShape;
41import com.jme3.bullet.util.Converter;
42import com.jme3.export.InputCapsule;
43import com.jme3.export.JmeExporter;
44import com.jme3.export.JmeImporter;
45import com.jme3.export.OutputCapsule;
46import com.jme3.math.Matrix3f;
47import com.jme3.math.Quaternion;
48import com.jme3.math.Vector3f;
49import java.io.IOException;
50
51/**
52 * Basic Bullet Character
53 * @author normenhansen
54 */
55public 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
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     */
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
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     */
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     */
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     */
121    public Vector3f getWalkDirection() {
122        return walkDirection;
123    }
124
125    public void setUpAxis(int axis) {
126        upAxis = axis;
127        character.setUpAxis(axis);
128    }
129
130    public int getUpAxis() {
131        return upAxis;
132    }
133
134    public void setFallSpeed(float fallSpeed) {
135        this.fallSpeed = fallSpeed;
136        character.setFallSpeed(fallSpeed);
137    }
138
139    public float getFallSpeed() {
140        return fallSpeed;
141    }
142
143    public void setJumpSpeed(float jumpSpeed) {
144        this.jumpSpeed = jumpSpeed;
145        character.setJumpSpeed(jumpSpeed);
146    }
147
148    public float getJumpSpeed() {
149        return jumpSpeed;
150    }
151
152    //does nothing..
153//    public void setMaxJumpHeight(float height) {
154//        character.setMaxJumpHeight(height);
155//    }
156    public void setGravity(float value) {
157        character.setGravity(value);
158    }
159
160    public float getGravity() {
161        return character.getGravity();
162    }
163
164    public void setMaxSlope(float slopeRadians) {
165        character.setMaxSlope(slopeRadians);
166    }
167
168    public float getMaxSlope() {
169        return character.getMaxSlope();
170    }
171
172    public boolean onGround() {
173        return character.onGround();
174    }
175
176    public void jump() {
177        character.jump();
178    }
179
180    @Override
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     */
197    public void setPhysicsLocation(Vector3f location) {
198        warp(location);
199    }
200
201    /**
202     * @return the physicsLocation
203     */
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     */
216    public Vector3f getPhysicsLocation() {
217        gObject.getWorldTransform(tempTrans);
218        Converter.convert(tempTrans.origin, physicsLocation.getTranslation());
219        return physicsLocation.getTranslation();
220    }
221
222    public void setCcdSweptSphereRadius(float radius) {
223        gObject.setCcdSweptSphereRadius(radius);
224    }
225
226    public void setCcdMotionThreshold(float threshold) {
227        gObject.setCcdMotionThreshold(threshold);
228    }
229
230    public float getCcdSweptSphereRadius() {
231        return gObject.getCcdSweptSphereRadius();
232    }
233
234    public float getCcdMotionThreshold() {
235        return gObject.getCcdMotionThreshold();
236    }
237
238    public float getCcdSquareMotionThreshold() {
239        return gObject.getCcdSquareMotionThreshold();
240    }
241
242    /**
243     * used internally
244     */
245    public KinematicCharacterController getControllerId() {
246        return character;
247    }
248
249    /**
250     * used internally
251     */
252    public PairCachingGhostObject getObjectId() {
253        return gObject;
254    }
255
256    public void destroy() {
257    }
258
259    @Override
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
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