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 jme3test.bullet;
33
34import com.jme3.app.SimpleApplication;
35import com.jme3.bounding.BoundingBox;
36import com.jme3.bullet.BulletAppState;
37import com.jme3.bullet.PhysicsSpace;
38import com.jme3.bullet.collision.PhysicsCollisionObject;
39import com.jme3.bullet.collision.shapes.BoxCollisionShape;
40import com.jme3.bullet.collision.shapes.CollisionShape;
41import com.jme3.bullet.control.RigidBodyControl;
42import com.jme3.bullet.util.CollisionShapeFactory;
43import com.jme3.font.BitmapText;
44import com.jme3.input.ChaseCamera;
45import com.jme3.input.KeyInput;
46import com.jme3.input.controls.ActionListener;
47import com.jme3.input.controls.AnalogListener;
48import com.jme3.input.controls.KeyTrigger;
49import com.jme3.light.DirectionalLight;
50import com.jme3.light.PointLight;
51import com.jme3.material.Material;
52import com.jme3.math.*;
53import com.jme3.renderer.Camera;
54import com.jme3.renderer.queue.RenderQueue.ShadowMode;
55import com.jme3.scene.Geometry;
56import com.jme3.scene.Spatial;
57import com.jme3.shadow.PssmShadowRenderer;
58import com.jme3.shadow.PssmShadowRenderer.CompareMode;
59import com.jme3.shadow.PssmShadowRenderer.FilterMode;
60import com.jme3.terrain.geomipmap.TerrainLodControl;
61import com.jme3.terrain.geomipmap.TerrainQuad;
62import com.jme3.terrain.heightmap.AbstractHeightMap;
63import com.jme3.terrain.heightmap.ImageBasedHeightMap;
64import com.jme3.texture.Texture;
65import com.jme3.texture.Texture.WrapMode;
66import com.jme3.util.SkyFactory;
67import java.util.ArrayList;
68import java.util.List;
69
70public class TestHoveringTank extends SimpleApplication implements AnalogListener,
71        ActionListener {
72
73    private BulletAppState bulletAppState;
74    private PhysicsHoverControl hoverControl;
75    private Spatial spaceCraft;
76    TerrainQuad terrain;
77    Material matRock;
78    boolean wireframe = false;
79    protected BitmapText hintText;
80    PointLight pl;
81    Geometry lightMdl;
82    Geometry collisionMarker;
83
84    public static void main(String[] args) {
85        TestHoveringTank app = new TestHoveringTank();
86        app.start();
87    }
88
89    private PhysicsSpace getPhysicsSpace() {
90        return bulletAppState.getPhysicsSpace();
91    }
92
93    private void setupKeys() {
94        inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A));
95        inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D));
96        inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W));
97        inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S));
98        inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE));
99        inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
100        inputManager.addListener(this, "Lefts");
101        inputManager.addListener(this, "Rights");
102        inputManager.addListener(this, "Ups");
103        inputManager.addListener(this, "Downs");
104        inputManager.addListener(this, "Space");
105        inputManager.addListener(this, "Reset");
106    }
107
108    @Override
109    public void simpleInitApp() {
110        bulletAppState = new BulletAppState();
111        bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
112        stateManager.attach(bulletAppState);
113//        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
114        bulletAppState.getPhysicsSpace().setAccuracy(1f/30f);
115        rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
116
117        PssmShadowRenderer pssmr = new PssmShadowRenderer(assetManager, 2048, 3);
118        pssmr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal());
119        pssmr.setLambda(0.55f);
120        pssmr.setShadowIntensity(0.6f);
121        pssmr.setCompareMode(CompareMode.Hardware);
122        pssmr.setFilterMode(FilterMode.Bilinear);
123        viewPort.addProcessor(pssmr);
124
125        setupKeys();
126        createTerrain();
127        buildPlayer();
128
129        DirectionalLight dl = new DirectionalLight();
130        dl.setColor(new ColorRGBA(1.0f, 0.94f, 0.8f, 1f).multLocal(1.3f));
131        dl.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal());
132        rootNode.addLight(dl);
133
134        Vector3f lightDir2 = new Vector3f(0.70518064f, 0.5902297f, -0.39287305f);
135        DirectionalLight dl2 = new DirectionalLight();
136        dl2.setColor(new ColorRGBA(0.7f, 0.85f, 1.0f, 1f));
137        dl2.setDirection(lightDir2);
138        rootNode.addLight(dl2);
139    }
140
141    private void buildPlayer() {
142        spaceCraft = assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml");
143        CollisionShape colShape = CollisionShapeFactory.createDynamicMeshShape(spaceCraft);
144        spaceCraft.setShadowMode(ShadowMode.CastAndReceive);
145        spaceCraft.setLocalTranslation(new Vector3f(-140, 14, -23));
146        spaceCraft.setLocalRotation(new Quaternion(new float[]{0, 0.01f, 0}));
147
148        hoverControl = new PhysicsHoverControl(colShape, 500);
149        hoverControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02);
150
151        spaceCraft.addControl(hoverControl);
152
153
154        rootNode.attachChild(spaceCraft);
155        getPhysicsSpace().add(hoverControl);
156
157        ChaseCamera chaseCam = new ChaseCamera(cam, inputManager);
158        spaceCraft.addControl(chaseCam);
159
160        flyCam.setEnabled(false);
161    }
162
163    public void makeMissile() {
164        Vector3f pos = spaceCraft.getWorldTranslation().clone();
165        Quaternion rot = spaceCraft.getWorldRotation();
166        Vector3f dir = rot.getRotationColumn(2);
167
168        Spatial missile = assetManager.loadModel("Models/SpaceCraft/Rocket.mesh.xml");
169        missile.scale(0.5f);
170        missile.rotate(0, FastMath.PI, 0);
171        missile.updateGeometricState();
172
173        BoundingBox box = (BoundingBox) missile.getWorldBound();
174        final Vector3f extent = box.getExtent(null);
175
176        BoxCollisionShape boxShape = new BoxCollisionShape(extent);
177
178        missile.setName("Missile");
179        missile.rotate(rot);
180        missile.setLocalTranslation(pos.addLocal(0, extent.y * 4.5f, 0));
181        missile.setLocalRotation(hoverControl.getPhysicsRotation());
182        missile.setShadowMode(ShadowMode.Cast);
183        RigidBodyControl control = new BombControl(assetManager, boxShape, 20);
184        control.setLinearVelocity(dir.mult(100));
185        control.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_03);
186        missile.addControl(control);
187
188
189        rootNode.attachChild(missile);
190        getPhysicsSpace().add(missile);
191    }
192
193    public void onAnalog(String binding, float value, float tpf) {
194    }
195
196    public void onAction(String binding, boolean value, float tpf) {
197        if (binding.equals("Lefts")) {
198            hoverControl.steer(value ? 50f : 0);
199        } else if (binding.equals("Rights")) {
200            hoverControl.steer(value ? -50f : 0);
201        } else if (binding.equals("Ups")) {
202            hoverControl.accelerate(value ? 100f : 0);
203        } else if (binding.equals("Downs")) {
204            hoverControl.accelerate(value ? -100f : 0);
205        } else if (binding.equals("Reset")) {
206            if (value) {
207                System.out.println("Reset");
208                hoverControl.setPhysicsLocation(new Vector3f(-140, 14, -23));
209                hoverControl.setPhysicsRotation(new Matrix3f());
210                hoverControl.clearForces();
211            } else {
212            }
213        } else if (binding.equals("Space") && value) {
214            makeMissile();
215        }
216    }
217
218    public void updateCamera() {
219        rootNode.updateGeometricState();
220
221        Vector3f pos = spaceCraft.getWorldTranslation().clone();
222        Quaternion rot = spaceCraft.getWorldRotation();
223        Vector3f dir = rot.getRotationColumn(2);
224
225        // make it XZ only
226        Vector3f camPos = new Vector3f(dir);
227        camPos.setY(0);
228        camPos.normalizeLocal();
229
230        // negate and multiply by distance from object
231        camPos.negateLocal();
232        camPos.multLocal(15);
233
234        // add Y distance
235        camPos.setY(2);
236        camPos.addLocal(pos);
237        cam.setLocation(camPos);
238
239        Vector3f lookAt = new Vector3f(dir);
240        lookAt.multLocal(7); // look at dist
241        lookAt.addLocal(pos);
242        cam.lookAt(lookAt, Vector3f.UNIT_Y);
243    }
244
245    @Override
246    public void simpleUpdate(float tpf) {
247    }
248
249    private void createTerrain() {
250        matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
251        matRock.setBoolean("useTriPlanarMapping", false);
252        matRock.setBoolean("WardIso", true);
253        matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
254        Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
255        Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
256        grass.setWrap(WrapMode.Repeat);
257        matRock.setTexture("DiffuseMap", grass);
258        matRock.setFloat("DiffuseMap_0_scale", 64);
259        Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
260        dirt.setWrap(WrapMode.Repeat);
261        matRock.setTexture("DiffuseMap_1", dirt);
262        matRock.setFloat("DiffuseMap_1_scale", 16);
263        Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
264        rock.setWrap(WrapMode.Repeat);
265        matRock.setTexture("DiffuseMap_2", rock);
266        matRock.setFloat("DiffuseMap_2_scale", 128);
267        Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
268        normalMap0.setWrap(WrapMode.Repeat);
269        Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png");
270        normalMap1.setWrap(WrapMode.Repeat);
271        Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png");
272        normalMap2.setWrap(WrapMode.Repeat);
273        matRock.setTexture("NormalMap", normalMap0);
274        matRock.setTexture("NormalMap_1", normalMap2);
275        matRock.setTexture("NormalMap_2", normalMap2);
276
277        AbstractHeightMap heightmap = null;
278        try {
279            heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f);
280            heightmap.load();
281        } catch (Exception e) {
282            e.printStackTrace();
283        }
284        terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
285        List<Camera> cameras = new ArrayList<Camera>();
286        cameras.add(getCamera());
287        TerrainLodControl control = new TerrainLodControl(terrain, cameras);
288        terrain.addControl(control);
289        terrain.setMaterial(matRock);
290        terrain.setLocalScale(new Vector3f(2, 2, 2));
291        terrain.setLocked(false); // unlock it so we can edit the height
292
293        terrain.setShadowMode(ShadowMode.CastAndReceive);
294        terrain.addControl(new RigidBodyControl(0));
295        rootNode.attachChild(terrain);
296        getPhysicsSpace().addAll(terrain);
297
298    }
299}
300