1/*
2 * To change this template, choose Tools | Templates
3 * and open the template in the editor.
4 */
5package com.jme3.bullet.control;
6
7import com.jme3.bullet.PhysicsSpace;
8import com.jme3.bullet.collision.shapes.BoxCollisionShape;
9import com.jme3.bullet.collision.shapes.CollisionShape;
10import com.jme3.bullet.collision.shapes.SphereCollisionShape;
11import com.jme3.bullet.objects.PhysicsRigidBody;
12import com.jme3.bullet.util.CollisionShapeFactory;
13import com.jme3.export.InputCapsule;
14import com.jme3.export.JmeExporter;
15import com.jme3.export.JmeImporter;
16import com.jme3.export.OutputCapsule;
17import com.jme3.math.Quaternion;
18import com.jme3.math.Vector3f;
19import com.jme3.renderer.RenderManager;
20import com.jme3.renderer.ViewPort;
21import com.jme3.scene.Geometry;
22import com.jme3.scene.Mesh;
23import com.jme3.scene.Spatial;
24import com.jme3.scene.control.Control;
25import com.jme3.scene.shape.Box;
26import com.jme3.scene.shape.Sphere;
27import java.io.IOException;
28
29/**
30 *
31 * @author normenhansen
32 */
33public class RigidBodyControl extends PhysicsRigidBody implements PhysicsControl {
34
35    protected Spatial spatial;
36    protected boolean enabled = true;
37    protected boolean added = false;
38    protected PhysicsSpace space = null;
39    protected boolean kinematicSpatial = true;
40
41    public RigidBodyControl() {
42    }
43
44    /**
45     * When using this constructor, the CollisionShape for the RigidBody is generated
46     * automatically when the Control is added to a Spatial.
47     * @param mass When not 0, a HullCollisionShape is generated, otherwise a MeshCollisionShape is used. For geometries with box or sphere meshes the proper box or sphere collision shape is used.
48     */
49    public RigidBodyControl(float mass) {
50        this.mass = mass;
51    }
52
53    /**
54     * Creates a new PhysicsNode with the supplied collision shape and mass 1
55     * @param shape
56     */
57    public RigidBodyControl(CollisionShape shape) {
58        super(shape);
59    }
60
61    public RigidBodyControl(CollisionShape shape, float mass) {
62        super(shape, mass);
63    }
64
65    public Control cloneForSpatial(Spatial spatial) {
66        RigidBodyControl control = new RigidBodyControl(collisionShape, mass);
67        control.setAngularFactor(getAngularFactor());
68        control.setAngularSleepingThreshold(getAngularSleepingThreshold());
69        control.setCcdMotionThreshold(getCcdMotionThreshold());
70        control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
71        control.setCollideWithGroups(getCollideWithGroups());
72        control.setCollisionGroup(getCollisionGroup());
73        control.setDamping(getLinearDamping(), getAngularDamping());
74        control.setFriction(getFriction());
75        control.setGravity(getGravity());
76        control.setKinematic(isKinematic());
77        control.setKinematicSpatial(isKinematicSpatial());
78        control.setLinearSleepingThreshold(getLinearSleepingThreshold());
79        control.setPhysicsLocation(getPhysicsLocation(null));
80        control.setPhysicsRotation(getPhysicsRotationMatrix(null));
81        control.setRestitution(getRestitution());
82
83        if (mass > 0) {
84            control.setAngularVelocity(getAngularVelocity());
85            control.setLinearVelocity(getLinearVelocity());
86        }
87        control.setApplyPhysicsLocal(isApplyPhysicsLocal());
88
89        control.setSpatial(spatial);
90        return control;
91    }
92
93    public void setSpatial(Spatial spatial) {
94        if (getUserObject() == null || getUserObject() == this.spatial) {
95            setUserObject(spatial);
96        }
97        this.spatial = spatial;
98        if (spatial == null) {
99            if (getUserObject() == spatial) {
100                setUserObject(null);
101            }
102            spatial = null;
103            collisionShape = null;
104            return;
105        }
106        if (collisionShape == null) {
107            createCollisionShape();
108            rebuildRigidBody();
109        }
110        setPhysicsLocation(getSpatialTranslation());
111        setPhysicsRotation(getSpatialRotation());
112    }
113
114    protected void createCollisionShape() {
115        if (spatial == null) {
116            return;
117        }
118        if (spatial instanceof Geometry) {
119            Geometry geom = (Geometry) spatial;
120            Mesh mesh = geom.getMesh();
121            if (mesh instanceof Sphere) {
122                collisionShape = new SphereCollisionShape(((Sphere) mesh).getRadius());
123                return;
124            } else if (mesh instanceof Box) {
125                collisionShape = new BoxCollisionShape(new Vector3f(((Box) mesh).getXExtent(), ((Box) mesh).getYExtent(), ((Box) mesh).getZExtent()));
126                return;
127            }
128        }
129        if (mass > 0) {
130            collisionShape = CollisionShapeFactory.createDynamicMeshShape(spatial);
131        } else {
132            collisionShape = CollisionShapeFactory.createMeshShape(spatial);
133        }
134    }
135
136    public void setEnabled(boolean enabled) {
137        this.enabled = enabled;
138        if (space != null) {
139            if (enabled && !added) {
140                if (spatial != null) {
141                    setPhysicsLocation(getSpatialTranslation());
142                    setPhysicsRotation(getSpatialRotation());
143                }
144                space.addCollisionObject(this);
145                added = true;
146            } else if (!enabled && added) {
147                space.removeCollisionObject(this);
148                added = false;
149            }
150        }
151    }
152
153    public boolean isEnabled() {
154        return enabled;
155    }
156
157    /**
158     * Checks if this control is in kinematic spatial mode.
159     * @return true if the spatial location is applied to this kinematic rigidbody
160     */
161    public boolean isKinematicSpatial() {
162        return kinematicSpatial;
163    }
164
165    /**
166     * Sets this control to kinematic spatial mode so that the spatials transform will
167     * be applied to the rigidbody in kinematic mode, defaults to true.
168     * @param kinematicSpatial
169     */
170    public void setKinematicSpatial(boolean kinematicSpatial) {
171        this.kinematicSpatial = kinematicSpatial;
172    }
173
174    public boolean isApplyPhysicsLocal() {
175        return motionState.isApplyPhysicsLocal();
176    }
177
178    /**
179     * When set to true, the physics coordinates will be applied to the local
180     * translation of the Spatial instead of the world traslation.
181     * @param applyPhysicsLocal
182     */
183    public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
184        motionState.setApplyPhysicsLocal(applyPhysicsLocal);
185    }
186
187    private Vector3f getSpatialTranslation(){
188        if(motionState.isApplyPhysicsLocal()){
189            return spatial.getLocalTranslation();
190        }
191        return spatial.getWorldTranslation();
192    }
193
194    private Quaternion getSpatialRotation(){
195        if(motionState.isApplyPhysicsLocal()){
196            return spatial.getLocalRotation();
197        }
198        return spatial.getWorldRotation();
199    }
200
201    public void update(float tpf) {
202        if (enabled && spatial != null) {
203            if (isKinematic() && kinematicSpatial) {
204                super.setPhysicsLocation(getSpatialTranslation());
205                super.setPhysicsRotation(getSpatialRotation());
206            } else {
207                getMotionState().applyTransform(spatial);
208            }
209        }
210    }
211
212    public void render(RenderManager rm, ViewPort vp) {
213        if (enabled && space != null && space.getDebugManager() != null) {
214            if (debugShape == null) {
215                attachDebugShape(space.getDebugManager());
216            }
217            //TODO: using spatial traslation/rotation..
218            debugShape.setLocalTranslation(spatial.getWorldTranslation());
219            debugShape.setLocalRotation(spatial.getWorldRotation());
220            debugShape.updateLogicalState(0);
221            debugShape.updateGeometricState();
222            rm.renderScene(debugShape, vp);
223        }
224    }
225
226    public void setPhysicsSpace(PhysicsSpace space) {
227        if (space == null) {
228            if (this.space != null) {
229                this.space.removeCollisionObject(this);
230                added = false;
231            }
232        } else {
233            if(this.space==space) return;
234            space.addCollisionObject(this);
235            added = true;
236        }
237        this.space = space;
238    }
239
240    public PhysicsSpace getPhysicsSpace() {
241        return space;
242    }
243
244    @Override
245    public void write(JmeExporter ex) throws IOException {
246        super.write(ex);
247        OutputCapsule oc = ex.getCapsule(this);
248        oc.write(enabled, "enabled", true);
249        oc.write(motionState.isApplyPhysicsLocal(), "applyLocalPhysics", false);
250        oc.write(kinematicSpatial, "kinematicSpatial", true);
251        oc.write(spatial, "spatial", null);
252    }
253
254    @Override
255    public void read(JmeImporter im) throws IOException {
256        super.read(im);
257        InputCapsule ic = im.getCapsule(this);
258        enabled = ic.readBoolean("enabled", true);
259        kinematicSpatial = ic.readBoolean("kinematicSpatial", true);
260        spatial = (Spatial) ic.readSavable("spatial", null);
261        motionState.setApplyPhysicsLocal(ic.readBoolean("applyLocalPhysics", false));
262        setUserObject(spatial);
263    }
264}
265