/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package jme3test.bullet; import com.jme3.app.SimpleApplication; import com.jme3.bounding.BoundingBox; import com.jme3.bullet.BulletAppState; import com.jme3.bullet.PhysicsSpace; import com.jme3.bullet.collision.PhysicsCollisionObject; import com.jme3.bullet.collision.shapes.BoxCollisionShape; import com.jme3.bullet.collision.shapes.CollisionShape; import com.jme3.bullet.control.RigidBodyControl; import com.jme3.bullet.util.CollisionShapeFactory; import com.jme3.font.BitmapText; import com.jme3.input.ChaseCamera; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.AnalogListener; import com.jme3.input.controls.KeyTrigger; import com.jme3.light.DirectionalLight; import com.jme3.light.PointLight; import com.jme3.material.Material; import com.jme3.math.*; import com.jme3.renderer.Camera; import com.jme3.renderer.queue.RenderQueue.ShadowMode; import com.jme3.scene.Geometry; import com.jme3.scene.Spatial; import com.jme3.shadow.PssmShadowRenderer; import com.jme3.shadow.PssmShadowRenderer.CompareMode; import com.jme3.shadow.PssmShadowRenderer.FilterMode; import com.jme3.system.AppSettings; import com.jme3.terrain.geomipmap.TerrainLodControl; import com.jme3.terrain.geomipmap.TerrainQuad; import com.jme3.terrain.heightmap.AbstractHeightMap; import com.jme3.terrain.heightmap.ImageBasedHeightMap; import com.jme3.texture.Texture; import com.jme3.texture.Texture.WrapMode; import com.jme3.util.SkyFactory; import java.util.ArrayList; import java.util.List; public class TestHoveringTank extends SimpleApplication implements AnalogListener, ActionListener { private BulletAppState bulletAppState; private PhysicsHoverControl hoverControl; private Spatial spaceCraft; TerrainQuad terrain; Material matRock; boolean wireframe = false; protected BitmapText hintText; PointLight pl; Geometry lightMdl; Geometry collisionMarker; public static void main(String[] args) { TestHoveringTank app = new TestHoveringTank(); app.start(); } private PhysicsSpace getPhysicsSpace() { return bulletAppState.getPhysicsSpace(); } private void setupKeys() { inputManager.addMapping("Lefts", new KeyTrigger(KeyInput.KEY_A)); inputManager.addMapping("Rights", new KeyTrigger(KeyInput.KEY_D)); inputManager.addMapping("Ups", new KeyTrigger(KeyInput.KEY_W)); inputManager.addMapping("Downs", new KeyTrigger(KeyInput.KEY_S)); inputManager.addMapping("Space", new KeyTrigger(KeyInput.KEY_SPACE)); inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN)); inputManager.addListener(this, "Lefts"); inputManager.addListener(this, "Rights"); inputManager.addListener(this, "Ups"); inputManager.addListener(this, "Downs"); inputManager.addListener(this, "Space"); inputManager.addListener(this, "Reset"); } @Override public void simpleInitApp() { bulletAppState = new BulletAppState(); bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL); stateManager.attach(bulletAppState); // bulletAppState.getPhysicsSpace().enableDebug(assetManager); bulletAppState.getPhysicsSpace().setAccuracy(1f/30f); rootNode.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false)); PssmShadowRenderer pssmr = new PssmShadowRenderer(assetManager, 2048, 3); pssmr.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal()); pssmr.setLambda(0.55f); pssmr.setShadowIntensity(0.6f); pssmr.setCompareMode(CompareMode.Hardware); pssmr.setFilterMode(FilterMode.Bilinear); viewPort.addProcessor(pssmr); setupKeys(); createTerrain(); buildPlayer(); DirectionalLight dl = new DirectionalLight(); dl.setColor(new ColorRGBA(1.0f, 0.94f, 0.8f, 1f).multLocal(1.3f)); dl.setDirection(new Vector3f(-0.5f, -0.3f, -0.3f).normalizeLocal()); rootNode.addLight(dl); Vector3f lightDir2 = new Vector3f(0.70518064f, 0.5902297f, -0.39287305f); DirectionalLight dl2 = new DirectionalLight(); dl2.setColor(new ColorRGBA(0.7f, 0.85f, 1.0f, 1f)); dl2.setDirection(lightDir2); rootNode.addLight(dl2); } private void buildPlayer() { spaceCraft = assetManager.loadModel("Models/HoverTank/Tank2.mesh.xml"); CollisionShape colShape = CollisionShapeFactory.createDynamicMeshShape(spaceCraft); spaceCraft.setShadowMode(ShadowMode.CastAndReceive); spaceCraft.setLocalTranslation(new Vector3f(-140, 50, -23)); spaceCraft.setLocalRotation(new Quaternion(new float[]{0, 0.01f, 0})); hoverControl = new PhysicsHoverControl(colShape, 500); spaceCraft.addControl(hoverControl); rootNode.attachChild(spaceCraft); getPhysicsSpace().add(hoverControl); hoverControl.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_02); ChaseCamera chaseCam = new ChaseCamera(cam, inputManager); spaceCraft.addControl(chaseCam); flyCam.setEnabled(false); } public void makeMissile() { Vector3f pos = spaceCraft.getWorldTranslation().clone(); Quaternion rot = spaceCraft.getWorldRotation(); Vector3f dir = rot.getRotationColumn(2); Spatial missile = assetManager.loadModel("Models/SpaceCraft/Rocket.mesh.xml"); missile.scale(0.5f); missile.rotate(0, FastMath.PI, 0); missile.updateGeometricState(); BoundingBox box = (BoundingBox) missile.getWorldBound(); final Vector3f extent = box.getExtent(null); BoxCollisionShape boxShape = new BoxCollisionShape(extent); missile.setName("Missile"); missile.rotate(rot); missile.setLocalTranslation(pos.addLocal(0, extent.y * 4.5f, 0)); missile.setLocalRotation(hoverControl.getPhysicsRotation()); missile.setShadowMode(ShadowMode.Cast); RigidBodyControl control = new BombControl(assetManager, boxShape, 20); control.setLinearVelocity(dir.mult(100)); control.setCollisionGroup(PhysicsCollisionObject.COLLISION_GROUP_03); missile.addControl(control); rootNode.attachChild(missile); getPhysicsSpace().add(missile); } public void onAnalog(String binding, float value, float tpf) { } public void onAction(String binding, boolean value, float tpf) { if (binding.equals("Lefts")) { hoverControl.steer(value ? 50f : 0); } else if (binding.equals("Rights")) { hoverControl.steer(value ? -50f : 0); } else if (binding.equals("Ups")) { hoverControl.accelerate(value ? 100f : 0); } else if (binding.equals("Downs")) { hoverControl.accelerate(value ? -100f : 0); } else if (binding.equals("Reset")) { if (value) { System.out.println("Reset"); hoverControl.setPhysicsLocation(new Vector3f(-140, 14, -23)); hoverControl.setPhysicsRotation(new Matrix3f()); hoverControl.clearForces(); } else { } } else if (binding.equals("Space") && value) { makeMissile(); } } public void updateCamera() { rootNode.updateGeometricState(); Vector3f pos = spaceCraft.getWorldTranslation().clone(); Quaternion rot = spaceCraft.getWorldRotation(); Vector3f dir = rot.getRotationColumn(2); // make it XZ only Vector3f camPos = new Vector3f(dir); camPos.setY(0); camPos.normalizeLocal(); // negate and multiply by distance from object camPos.negateLocal(); camPos.multLocal(15); // add Y distance camPos.setY(2); camPos.addLocal(pos); cam.setLocation(camPos); Vector3f lookAt = new Vector3f(dir); lookAt.multLocal(7); // look at dist lookAt.addLocal(pos); cam.lookAt(lookAt, Vector3f.UNIT_Y); } @Override public void simpleUpdate(float tpf) { } private void createTerrain() { matRock = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md"); matRock.setBoolean("useTriPlanarMapping", false); matRock.setBoolean("WardIso", true); matRock.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png")); Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png"); Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg"); grass.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap", grass); matRock.setFloat("DiffuseMap_0_scale", 64); Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg"); dirt.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_1", dirt); matRock.setFloat("DiffuseMap_1_scale", 16); Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg"); rock.setWrap(WrapMode.Repeat); matRock.setTexture("DiffuseMap_2", rock); matRock.setFloat("DiffuseMap_2_scale", 128); Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/splat/grass_normal.jpg"); normalMap0.setWrap(WrapMode.Repeat); Texture normalMap1 = assetManager.loadTexture("Textures/Terrain/splat/dirt_normal.png"); normalMap1.setWrap(WrapMode.Repeat); Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/splat/road_normal.png"); normalMap2.setWrap(WrapMode.Repeat); matRock.setTexture("NormalMap", normalMap0); matRock.setTexture("NormalMap_1", normalMap2); matRock.setTexture("NormalMap_2", normalMap2); AbstractHeightMap heightmap = null; try { heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), 0.25f); heightmap.load(); } catch (Exception e) { e.printStackTrace(); } terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap()); List<Camera> cameras = new ArrayList<Camera>(); cameras.add(getCamera()); TerrainLodControl control = new TerrainLodControl(terrain, cameras); terrain.addControl(control); terrain.setMaterial(matRock); terrain.setLocalScale(new Vector3f(2, 2, 2)); terrain.setLocked(false); // unlock it so we can edit the height terrain.setShadowMode(ShadowMode.CastAndReceive); terrain.addControl(new RigidBodyControl(0)); rootNode.attachChild(terrain); getPhysicsSpace().addAll(terrain); } }