I am doing some testing with the scene explorer so I created a Scene and edited a terrain for it, just with some mountains as @Normen explainned in jME SDK Usecase Demo 1 video. I added a RigidBodyControl to the scene.
The problem is, if I use this scene, the character keeps bouncing upside down. But if I use a created Terrain, all works fine.
Does anybody can give me some insight? I don’t know what I am doing wrong with the physics on my Scene.
Here is my code:
[java]
/*
- To change this template, choose Tools | Templates
- and open the template in the editor.
*/
package mygame;
/*
- Copyright © 2009-2012 jMonkeyEngine All rights reserved. <p/>
- 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. <p/> * 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. <p/> * 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. <p/> 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.
*/
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.input.controls.TouchListener;
import com.jme3.input.controls.TouchTrigger;
import com.jme3.input.event.TouchEvent;
import static com.jme3.input.event.TouchEvent.Type.FLING;
import static com.jme3.input.event.TouchEvent.Type.LONGPRESSED;
import static com.jme3.input.event.TouchEvent.Type.MOVE;
import static com.jme3.input.event.TouchEvent.Type.TAP;
import static com.jme3.input.event.TouchEvent.Type.UP;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.CameraNode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.SceneGraphVisitor;
import com.jme3.scene.SceneGraphVisitorAdapter;
import com.jme3.scene.Spatial;
import com.jme3.system.AppSettings;
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.Iterator;
import java.util.List;
/**
-
A walking physical character followed by a 3rd person camera. (No animation.)
-
@author normenhansen, zathras
*/
public class Main extends SimpleApplication
implements ActionListener, AnimEventListener, TouchListener {private BulletAppState bulletAppState;
private BetterCharacterControl physicsCharacter;
private Node characterNode;
private Node model;
private CameraNode camNode;
boolean rotate = false;
private Vector3f walkDirection = new Vector3f(0, 0, 0);
private Vector3f viewDirection = new Vector3f(0, 0, 1);
boolean leftStrafe = false, rightStrafe = false, forward = false, backward = false,
leftRotate = false, rightRotate = false;
private boolean left = false, right = false, up = false, down = false;
private Vector3f normalGravity = new Vector3f(0.0f,-9.8f,0.0f);
private ChaseCamera chaseCamera;
private boolean lockView = false;
private boolean jumpwalk = true;
private List<AnimChannel> animChannels;
private List<AnimControl> animControls;
//terrain
//loaded terrain
private Spatial terrainSpatial;
//created terrain
private TerrainQuad terrainQuad;
//terrain common
private Node terrain;
private RigidBodyControl terrainPhysicsNode;
boolean loadedTerrain = false;
//Materials
Material matRock;
Material matBullet;public static void main(String[] args) {
Main app = new Main();
AppSettings settings = new AppSettings(true);
settings.setRenderer(AppSettings.LWJGL_OPENGL2);
settings.setAudioRenderer(AppSettings.LWJGL_OPENAL);
app.setSettings(settings);
app.start();
}@Override
public void simpleInitApp() {
//setup keyboard mapping
setupKeys();// activate physics bulletAppState = new BulletAppState(); stateManager.attach(bulletAppState); bulletAppState.setDebugEnabled(true); // init a physics test scene loadedTerrain = false; if (loadedTerrain) { loadTerrain(); } else { createTerrain(); } createCharacter(); flyCam.setEnabled(false); chaseCamera = new ChaseCamera(cam, model, inputManager); chaseCamera.setSmoothMotion(false); chaseCamera.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE)); createLight(); //stateManager.attach(new VideoRecorderAppState()); inputManager.addMapping("Touch", new TouchTrigger(0)); inputManager.addListener(this, new String[]{"Touch"});
}
private void createTerrain() {
// load sky
Spatial sky = SkyFactory.createSky(assetManager, “Textures/ColorRamp/cloudy.png”, true);
rootNode.attachChild(sky);
//rootNode.attachChild(SkyFactory.createSky(assetManager, “Textures/Sky/Bright/BrightSky.dds”, false));
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) { } terrainQuad = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap()); terrainQuad.setMaterial(matRock); terrainQuad.setLocalScale(new Vector3f(2, 2, 2)); terrainQuad.setLocalTranslation(-100, -40, 150); terrain = new Node(); terrain.attachChild(terrainQuad); terrainPhysicsNode = new RigidBodyControl(CollisionShapeFactory.createMeshShape(terrain), 0); terrain.addControl(terrainPhysicsNode); getPhysicsSpace().add(terrain); rootNode.attachChild(terrain);
}
public void loadTerrain() {
terrainSpatial = assetManager.loadModel(“Scenes/gilbScene.j3o”);
terrainSpatial.depthFirstTraversal(new SceneGraphVisitor() {@Override public void visit(Spatial spatial) { RigidBodyControl rigidBodyControl = spatial.getControl(RigidBodyControl.class); if(rigidBodyControl != null) { terrainPhysicsNode = rigidBodyControl; } } }); terrain = new Node(); terrain.attachChild(terrainSpatial); terrain.addControl(terrainPhysicsNode); getPhysicsSpace().add(terrainPhysicsNode); rootNode.attachChild(terrain);
}
public void createCharacter() {
// Create a node for the character model
characterNode = new Node(“character node”);
if (!loadedTerrain) {
characterNode.setLocalTranslation(new Vector3f(56.0f, 1.0f, 169.0f));
} else {
characterNode.setLocalTranslation(new Vector3f(0.0f, 1.0f, 0.0f));
}// Add a character control to the node so we can add other things and // control the model rotation physicsCharacter = new BetterCharacterControl(0.5f, 2.5f, 80f); characterNode.addControl(physicsCharacter); // Load model, attach to character node model = (Node) assetManager.loadModel("Models/Jaime/Jaime.j3o"); model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); model.setLocalScale(2.0f); characterNode.attachChild(model); setAnimControls(model); setAnimation("Idle", 0.3f); // Add character node to the rootNode getPhysicsSpace().add(physicsCharacter); getPhysicsSpace().addAll(characterNode); physicsCharacter.setGravity(normalGravity); rootNode.attachChild(characterNode);
}
@Override
public void simpleUpdate(float tpf) {
checkGravity();
// Get current forward and left vectors of model by using its rotation
// to rotate the unit vectors
Vector3f modelForwardDir = characterNode.getWorldRotation().mult(Vector3f.UNIT_Z);
Vector3f modelLeftDir = characterNode.getWorldRotation().mult(Vector3f.UNIT_X);// WalkDirection is global! // You *can* make your character fly with this. walkDirection.set(0, 0, 0); if (leftStrafe || left || down) { walkDirection.addLocal(modelLeftDir.mult(1.0f)); } else if (rightStrafe || right) { walkDirection.addLocal(modelLeftDir.negate().multLocal(1.0f)); } if (forward || up) { walkDirection.addLocal(modelForwardDir.mult(1.0f)); } else if (backward) { walkDirection.addLocal(modelForwardDir.negate().multLocal(1.0f)); } physicsCharacter.setWalkDirection(walkDirection); if (walkDirection.z != 0f || walkDirection.x != 0f || walkDirection.y != 0f) { setAnimation("Walk", 1.0f); } else { setAnimation("Idle", 0.3f); } // ViewDirection is local to characters physics system! // The final world rotation depends on the gravity and on the state of // setApplyPhysicsLocal() if (leftRotate) { Quaternion rotateL = new Quaternion().fromAngleAxis(FastMath.PI * tpf, Vector3f.UNIT_Y); rotateL.multLocal(viewDirection); } else if (rightRotate) { Quaternion rotateR = new Quaternion().fromAngleAxis(-FastMath.PI * tpf, Vector3f.UNIT_Y); rotateR.multLocal(viewDirection); } physicsCharacter.setViewDirection(viewDirection); int x = (int) model.getWorldTranslation().x; int y = (int) model.getWorldTranslation().y; int z = (int) model.getWorldTranslation().z; String position = "(" + x + ", " + y + ", " + z + ")"; fpsText.setSize(12.0f); fpsText.setText("Position = ".concat(position));
}
private PhysicsSpace getPhysicsSpace() {
return bulletAppState.getPhysicsSpace();
}@Override
public void onAction(String binding, boolean value, float tpf) {
switch (binding) {
case “Strafe Left”:
if (value) {
leftStrafe = true;
} else {
leftStrafe = false;
}
break;
case “Strafe Right”:
if (value) {
rightStrafe = true;
} else {
rightStrafe = false;
}
break;
case “Rotate Left”:
if (value) {
leftRotate = true;
} else {
leftRotate = false;
}
break;
case “Rotate Right”:
if (value) {
rightRotate = true;
} else {
rightRotate = false;
}
break;
case “walk Forward”:
if (value) {
forward = true;
} else {
forward = false;
}
break;
case “walk Backward”:
if (value) {
backward = true;
} else {
backward = false;
}
break;
case “Jump”:
physicsCharacter.jump();
break;
case “Duck”:
if (value) {
physicsCharacter.setDucked(true);
} else {
physicsCharacter.setDucked(false);
}
break;
case “Lock View”:
if (value && lockView) {
lockView = false;
} else if (value && !lockView) {
lockView = true;
}
//flyCam.setEnabled(!lockView);
//camNode.setEnabled(lockView);
break;
case “jumpWalk”:
if (!value) {
jumpwalk = !jumpwalk;
}
break;
}
}private void setupKeys() {
inputManager.addMapping(“Strafe Left”,
new KeyTrigger(KeyInput.KEY_U),
new KeyTrigger(KeyInput.KEY_Z));
inputManager.addMapping(“Strafe Right”,
new KeyTrigger(KeyInput.KEY_O),
new KeyTrigger(KeyInput.KEY_X));inputManager.addMapping("Rotate Left", new KeyTrigger(KeyInput.KEY_J), new KeyTrigger(KeyInput.KEY_LEFT), new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); inputManager.addMapping("Rotate Right", new KeyTrigger(KeyInput.KEY_L), new KeyTrigger(KeyInput.KEY_RIGHT), new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); inputManager.addMapping("walk Forward", new KeyTrigger(KeyInput.KEY_I), new KeyTrigger(KeyInput.KEY_UP), new MouseButtonTrigger(MouseInput.BUTTON_MIDDLE)); inputManager.addMapping("walk Backward", new KeyTrigger(KeyInput.KEY_K), new KeyTrigger(KeyInput.KEY_DOWN)); inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_F), new KeyTrigger(KeyInput.KEY_SPACE)); inputManager.addMapping("Duck", new KeyTrigger(KeyInput.KEY_G), new KeyTrigger(KeyInput.KEY_LSHIFT), new KeyTrigger(KeyInput.KEY_RSHIFT)); inputManager.addMapping("jumpWalk", new KeyTrigger(KeyInput.KEY_J)); inputManager.addMapping("Lock View", new KeyTrigger(KeyInput.KEY_RETURN)); inputManager.addListener(this, "Strafe Left", "Strafe Right"); inputManager.addListener(this, "Rotate Left", "Rotate Right"); inputManager.addListener(this, "walk Forward", "walk Backward"); inputManager.addListener(this, "Jump", "Duck", "Lock View"); inputManager.addListener(this, "jumpWalk");
}
@Override
public void simpleRender(RenderManager rm) {
}private void createLight() {
//if (!loadedTerrain) {
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight directionalLight = new DirectionalLight();
directionalLight.setDirection(direction);
directionalLight.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(directionalLight);
AmbientLight ambientLight = new AmbientLight();
rootNode.addLight(ambientLight);
//}
}@Override
public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
}@Override
public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
}private void setAnimControls(Spatial spatial) {
if (spatial == null) {
if (animControls != null) {
for (Iterator<AnimControl> it = animControls.iterator(); it.hasNext(){
AnimControl animControl = it.next();
animControl.clearChannels();
}
}
animControls = null;
animChannels = null;
return;
}SceneGraphVisitorAdapter visitor = new SceneGraphVisitorAdapter() { @Override public void visit(Geometry geom) { super.visit(geom); checkForAnimControl(geom); } @Override public void visit(Node geom) { super.visit(geom); checkForAnimControl(geom); } private void checkForAnimControl(Spatial geom) { AnimControl animControl = geom.getControl(AnimControl.class); if (animControl == null) { return; } if (animControls == null) { animControls = new ArrayList<>(); } if (animChannels == null) { animChannels = new ArrayList<>(); } animControls.add(animControl); animChannels.add(animControl.createChannel()); } }; spatial.depthFirstTraversal(visitor);
}
public void setAnimation(String animation, float speed) {
for (AnimChannel animChannel : animChannels) {
try {
if (animChannel.getAnimationName().equals(animation)) {
return;
}
} catch (Exception e) {
}
animChannel.setAnim(animation);
animChannel.setSpeed(speed);
}
}private void checkGravity() {
Vector3f dist = terrain.getWorldTranslation().subtract(characterNode.getWorldTranslation());
if (dist.length() < 24) {
physicsCharacter.setGravity(dist.normalizeLocal().multLocal(9.81f));
} else {
physicsCharacter.setGravity(normalGravity);
}
}@Override
public void onTouch(String name, TouchEvent evt, float tpf) {
float x;
float y;
float pressure;
switch (evt.getType()) {
case MOVE:
x = evt.getX();
y = evt.getY();pressure = evt.getPressure(); up = false; left = false; right = false; if(x>400) right = true; else left = true; break; case TAP: x = evt.getX(); y = evt.getY(); up = false; left = false; right = false; if(x>400) right = true; else left = true; break; case LONGPRESSED: // move forward up = true; break; case UP: up = false; break; case FLING: break; default: break; } evt.setConsumed();
}
}
[/java]