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 */
32
33package jme3test.helloworld;
34
35import com.jme3.app.SimpleApplication;
36import com.jme3.asset.plugins.ZipLocator;
37import com.jme3.bullet.BulletAppState;
38import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
39import com.jme3.bullet.collision.shapes.CollisionShape;
40import com.jme3.bullet.control.CharacterControl;
41import com.jme3.bullet.control.RigidBodyControl;
42import com.jme3.bullet.util.CollisionShapeFactory;
43import com.jme3.input.KeyInput;
44import com.jme3.input.controls.ActionListener;
45import com.jme3.input.controls.KeyTrigger;
46import com.jme3.light.AmbientLight;
47import com.jme3.light.DirectionalLight;
48import com.jme3.math.ColorRGBA;
49import com.jme3.math.Vector3f;
50import com.jme3.scene.Node;
51import com.jme3.scene.Spatial;
52
53/**
54 * Example 9 - How to make walls and floors solid.
55 * This collision code uses Physics and a custom Action Listener.
56 * @author normen, with edits by Zathras
57 */
58public class HelloCollision extends SimpleApplication
59        implements ActionListener {
60
61  private Spatial sceneModel;
62  private BulletAppState bulletAppState;
63  private RigidBodyControl landscape;
64  private CharacterControl player;
65  private Vector3f walkDirection = new Vector3f();
66  private boolean left = false, right = false, up = false, down = false;
67
68  public static void main(String[] args) {
69    HelloCollision app = new HelloCollision();
70    app.start();
71  }
72
73  public void simpleInitApp() {
74    /** Set up Physics */
75    bulletAppState = new BulletAppState();
76    stateManager.attach(bulletAppState);
77    //bulletAppState.getPhysicsSpace().enableDebug(assetManager);
78
79    // We re-use the flyby camera for rotation, while positioning is handled by physics
80    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));
81    flyCam.setMoveSpeed(100);
82    setUpKeys();
83    setUpLight();
84
85    // We load the scene from the zip file and adjust its size.
86    assetManager.registerLocator("town.zip", ZipLocator.class.getName());
87    sceneModel = assetManager.loadModel("main.scene");
88    sceneModel.setLocalScale(2f);
89
90    // We set up collision detection for the scene by creating a
91    // compound collision shape and a static RigidBodyControl with mass zero.
92    CollisionShape sceneShape =
93            CollisionShapeFactory.createMeshShape((Node) sceneModel);
94    landscape = new RigidBodyControl(sceneShape, 0);
95    sceneModel.addControl(landscape);
96
97    // We set up collision detection for the player by creating
98    // a capsule collision shape and a CharacterControl.
99    // The CharacterControl offers extra settings for
100    // size, stepheight, jumping, falling, and gravity.
101    // We also put the player in its starting position.
102    CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
103    player = new CharacterControl(capsuleShape, 0.05f);
104    player.setJumpSpeed(20);
105    player.setFallSpeed(30);
106    player.setGravity(30);
107    player.setPhysicsLocation(new Vector3f(0, 10, 0));
108
109    // We attach the scene and the player to the rootnode and the physics space,
110    // to make them appear in the game world.
111    rootNode.attachChild(sceneModel);
112    bulletAppState.getPhysicsSpace().add(landscape);
113    bulletAppState.getPhysicsSpace().add(player);
114  }
115
116  private void setUpLight() {
117    // We add light so we see the scene
118    AmbientLight al = new AmbientLight();
119    al.setColor(ColorRGBA.White.mult(1.3f));
120    rootNode.addLight(al);
121
122    DirectionalLight dl = new DirectionalLight();
123    dl.setColor(ColorRGBA.White);
124    dl.setDirection(new Vector3f(2.8f, -2.8f, -2.8f).normalizeLocal());
125    rootNode.addLight(dl);
126  }
127
128  /** We over-write some navigational key mappings here, so we can
129   * add physics-controlled walking and jumping: */
130  private void setUpKeys() {
131    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
132    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
133    inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
134    inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
135    inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));
136    inputManager.addListener(this, "Left");
137    inputManager.addListener(this, "Right");
138    inputManager.addListener(this, "Up");
139    inputManager.addListener(this, "Down");
140    inputManager.addListener(this, "Jump");
141  }
142
143  /** These are our custom actions triggered by key presses.
144   * We do not walk yet, we just keep track of the direction the user pressed. */
145  public void onAction(String binding, boolean value, float tpf) {
146    if (binding.equals("Left")) {
147      if (value) { left = true; } else { left = false; }
148    } else if (binding.equals("Right")) {
149      if (value) { right = true; } else { right = false; }
150    } else if (binding.equals("Up")) {
151      if (value) { up = true; } else { up = false; }
152    } else if (binding.equals("Down")) {
153      if (value) { down = true; } else { down = false; }
154    } else if (binding.equals("Jump")) {
155      player.jump();
156    }
157  }
158
159  /**
160   * This is the main event loop--walking happens here.
161   * We check in which direction the player is walking by interpreting
162   * the camera direction forward (camDir) and to the side (camLeft).
163   * The setWalkDirection() command is what lets a physics-controlled player walk.
164   * We also make sure here that the camera moves with player.
165   */
166  @Override
167  public void simpleUpdate(float tpf) {
168    Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
169    Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
170    walkDirection.set(0, 0, 0);
171    if (left)  { walkDirection.addLocal(camLeft); }
172    if (right) { walkDirection.addLocal(camLeft.negate()); }
173    if (up)    { walkDirection.addLocal(camDir); }
174    if (down)  { walkDirection.addLocal(camDir.negate()); }
175    player.setWalkDirection(walkDirection);
176    cam.setLocation(player.getPhysicsLocation());
177  }
178}
179