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.CollisionShape;
9import com.jme3.bullet.objects.PhysicsVehicle;
10import com.jme3.bullet.objects.VehicleWheel;
11import com.jme3.export.InputCapsule;
12import com.jme3.export.JmeExporter;
13import com.jme3.export.JmeImporter;
14import com.jme3.export.OutputCapsule;
15import com.jme3.math.Quaternion;
16import com.jme3.math.Vector3f;
17import com.jme3.renderer.RenderManager;
18import com.jme3.renderer.ViewPort;
19import com.jme3.scene.Geometry;
20import com.jme3.scene.Node;
21import com.jme3.scene.Spatial;
22import com.jme3.scene.control.Control;
23import com.jme3.scene.debug.Arrow;
24import java.io.IOException;
25import java.util.Iterator;
26
27/**
28 *
29 * @author normenhansen
30 */
31public class VehicleControl extends PhysicsVehicle implements PhysicsControl {
32
33    protected Spatial spatial;
34    protected boolean enabled = true;
35    protected PhysicsSpace space = null;
36    protected boolean added = false;
37
38    public VehicleControl() {
39    }
40
41    /**
42     * Creates a new PhysicsNode with the supplied collision shape
43     * @param shape
44     */
45    public VehicleControl(CollisionShape shape) {
46        super(shape);
47    }
48
49    public VehicleControl(CollisionShape shape, float mass) {
50        super(shape, mass);
51    }
52
53    public boolean isApplyPhysicsLocal() {
54        return motionState.isApplyPhysicsLocal();
55    }
56
57    /**
58     * When set to true, the physics coordinates will be applied to the local
59     * translation of the Spatial
60     * @param applyPhysicsLocal
61     */
62    public void setApplyPhysicsLocal(boolean applyPhysicsLocal) {
63        motionState.setApplyPhysicsLocal(applyPhysicsLocal);
64        for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
65            VehicleWheel vehicleWheel = it.next();
66            vehicleWheel.setApplyLocal(applyPhysicsLocal);
67        }
68    }
69
70    private Vector3f getSpatialTranslation(){
71        if(motionState.isApplyPhysicsLocal()){
72            return spatial.getLocalTranslation();
73        }
74        return spatial.getWorldTranslation();
75    }
76
77    private Quaternion getSpatialRotation(){
78        if(motionState.isApplyPhysicsLocal()){
79            return spatial.getLocalRotation();
80        }
81        return spatial.getWorldRotation();
82    }
83
84    public Control cloneForSpatial(Spatial spatial) {
85        VehicleControl control = new VehicleControl(collisionShape, mass);
86        control.setAngularFactor(getAngularFactor());
87        control.setAngularSleepingThreshold(getAngularSleepingThreshold());
88        control.setAngularVelocity(getAngularVelocity());
89        control.setCcdMotionThreshold(getCcdMotionThreshold());
90        control.setCcdSweptSphereRadius(getCcdSweptSphereRadius());
91        control.setCollideWithGroups(getCollideWithGroups());
92        control.setCollisionGroup(getCollisionGroup());
93        control.setDamping(getLinearDamping(), getAngularDamping());
94        control.setFriction(getFriction());
95        control.setGravity(getGravity());
96        control.setKinematic(isKinematic());
97        control.setLinearSleepingThreshold(getLinearSleepingThreshold());
98        control.setLinearVelocity(getLinearVelocity());
99        control.setPhysicsLocation(getPhysicsLocation());
100        control.setPhysicsRotation(getPhysicsRotationMatrix());
101        control.setRestitution(getRestitution());
102
103        control.setFrictionSlip(getFrictionSlip());
104        control.setMaxSuspensionTravelCm(getMaxSuspensionTravelCm());
105        control.setSuspensionStiffness(getSuspensionStiffness());
106        control.setSuspensionCompression(tuning.suspensionCompression);
107        control.setSuspensionDamping(tuning.suspensionDamping);
108        control.setMaxSuspensionForce(getMaxSuspensionForce());
109
110        for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
111            VehicleWheel wheel = it.next();
112            VehicleWheel newWheel = control.addWheel(wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), wheel.isFrontWheel());
113            newWheel.setFrictionSlip(wheel.getFrictionSlip());
114            newWheel.setMaxSuspensionTravelCm(wheel.getMaxSuspensionTravelCm());
115            newWheel.setSuspensionStiffness(wheel.getSuspensionStiffness());
116            newWheel.setWheelsDampingCompression(wheel.getWheelsDampingCompression());
117            newWheel.setWheelsDampingRelaxation(wheel.getWheelsDampingRelaxation());
118            newWheel.setMaxSuspensionForce(wheel.getMaxSuspensionForce());
119
120            //TODO: bad way finding children!
121            if (spatial instanceof Node) {
122                Node node = (Node) spatial;
123                Spatial wheelSpat = node.getChild(wheel.getWheelSpatial().getName());
124                if (wheelSpat != null) {
125                    newWheel.setWheelSpatial(wheelSpat);
126                }
127            }
128        }
129        control.setApplyPhysicsLocal(isApplyPhysicsLocal());
130
131        control.setSpatial(spatial);
132        return control;
133    }
134
135    public void setSpatial(Spatial spatial) {
136        if (getUserObject() == null || getUserObject() == this.spatial) {
137            setUserObject(spatial);
138        }
139        this.spatial = spatial;
140        if (spatial == null) {
141            if (getUserObject() == spatial) {
142                setUserObject(null);
143            }
144            this.spatial = null;
145            this.collisionShape = null;
146            return;
147        }
148        setPhysicsLocation(getSpatialTranslation());
149        setPhysicsRotation(getSpatialRotation());
150    }
151
152    public void setEnabled(boolean enabled) {
153        this.enabled = enabled;
154        if (space != null) {
155            if (enabled && !added) {
156                if(spatial!=null){
157                    setPhysicsLocation(getSpatialTranslation());
158                    setPhysicsRotation(getSpatialRotation());
159                }
160                space.addCollisionObject(this);
161                added = true;
162            } else if (!enabled && added) {
163                space.removeCollisionObject(this);
164                added = false;
165            }
166        }
167    }
168
169    public boolean isEnabled() {
170        return enabled;
171    }
172
173    public void update(float tpf) {
174        if (enabled && spatial != null) {
175            if (getMotionState().applyTransform(spatial)) {
176                spatial.getWorldTransform();
177                applyWheelTransforms();
178            }
179        } else if (enabled) {
180            applyWheelTransforms();
181        }
182    }
183
184    @Override
185    protected Spatial getDebugShape() {
186        return super.getDebugShape();
187    }
188
189    public void render(RenderManager rm, ViewPort vp) {
190        if (enabled && space != null && space.getDebugManager() != null) {
191            if (debugShape == null) {
192                attachDebugShape(space.getDebugManager());
193            }
194            Node debugNode = (Node) debugShape;
195            debugShape.setLocalTranslation(spatial.getWorldTranslation());
196            debugShape.setLocalRotation(spatial.getWorldRotation());
197            int i = 0;
198            for (Iterator<VehicleWheel> it = wheels.iterator(); it.hasNext();) {
199                VehicleWheel physicsVehicleWheel = it.next();
200                Vector3f location = physicsVehicleWheel.getLocation().clone();
201                Vector3f direction = physicsVehicleWheel.getDirection().clone();
202                Vector3f axle = physicsVehicleWheel.getAxle().clone();
203                float restLength = physicsVehicleWheel.getRestLength();
204                float radius = physicsVehicleWheel.getRadius();
205
206                Geometry locGeom = (Geometry) debugNode.getChild("WheelLocationDebugShape" + i);
207                Geometry dirGeom = (Geometry) debugNode.getChild("WheelDirectionDebugShape" + i);
208                Geometry axleGeom = (Geometry) debugNode.getChild("WheelAxleDebugShape" + i);
209                Geometry wheelGeom = (Geometry) debugNode.getChild("WheelRadiusDebugShape" + i);
210
211                Arrow locArrow = (Arrow) locGeom.getMesh();
212                locArrow.setArrowExtent(location);
213                Arrow axleArrow = (Arrow) axleGeom.getMesh();
214                axleArrow.setArrowExtent(axle.normalizeLocal().multLocal(0.3f));
215                Arrow wheelArrow = (Arrow) wheelGeom.getMesh();
216                wheelArrow.setArrowExtent(direction.normalizeLocal().multLocal(radius));
217                Arrow dirArrow = (Arrow) dirGeom.getMesh();
218                dirArrow.setArrowExtent(direction.normalizeLocal().multLocal(restLength));
219
220                dirGeom.setLocalTranslation(location);
221                axleGeom.setLocalTranslation(location.addLocal(direction));
222                wheelGeom.setLocalTranslation(location);
223                i++;
224            }
225            debugShape.updateLogicalState(0);
226            debugShape.updateGeometricState();
227            rm.renderScene(debugShape, vp);
228        }
229    }
230
231    public void setPhysicsSpace(PhysicsSpace space) {
232        createVehicle(space);
233        if (space == null) {
234            if (this.space != null) {
235                this.space.removeCollisionObject(this);
236                added = false;
237            }
238        } else {
239            if(this.space==space) return;
240            space.addCollisionObject(this);
241            added = true;
242        }
243        this.space = space;
244    }
245
246    public PhysicsSpace getPhysicsSpace() {
247        return space;
248    }
249
250    @Override
251    public void write(JmeExporter ex) throws IOException {
252        super.write(ex);
253        OutputCapsule oc = ex.getCapsule(this);
254        oc.write(enabled, "enabled", true);
255        oc.write(motionState.isApplyPhysicsLocal(), "applyLocalPhysics", false);
256        oc.write(spatial, "spatial", null);
257    }
258
259    @Override
260    public void read(JmeImporter im) throws IOException {
261        super.read(im);
262        InputCapsule ic = im.getCapsule(this);
263        enabled = ic.readBoolean("enabled", true);
264        spatial = (Spatial) ic.readSavable("spatial", null);
265        motionState.setApplyPhysicsLocal(ic.readBoolean("applyLocalPhysics", false));
266        setUserObject(spatial);
267    }
268}
269