Oh well, after some hours of reading the forum and trying to solve this,
I just can't
I'm testing the physics with a cube, that should go around the terrain, but for any reason
the cube looks like a bird feather LOL. and for worst a ghost feather that goes in the terrain !!
Here is the video of the ghosty cube !! http://www.youtube.com/watch?v=yJiIkmvG1NA
Follow the code that I'm using, most of it is merged code from the samples.
thx !!
Client.java
import com.jmex.editors.swing.settings.GameSettingsPanel;
import com.jmex.game.StandardGame;
import com.jmex.game.state.GameStateManager;
import com.jmex.physics.util.states.PhysicsGameState;
public class Client {
public StandardGame game;
public PhysicsGameState physicsState;
/**
* @param args
*/
public static void main(String[] args) {
try {
new Client();
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public Client() throws Exception {
game = new StandardGame("SIOTI");
// Show settings screen
if (GameSettingsPanel.prompt(game.getSettings())) {
// Start StandardGame, it will block until it has initialized
// successfully, then return
game.start();
physicsState = new SiotiPhysicsState("game state");
GameStateManager.getInstance().attachChild(physicsState);
physicsState.setActive(true);
}
}
}
SiotiPhysicsState.java
import java.util.HashMap;
import org.odejava.Odejava;
import com.jme.bounding.BoundingBox;
import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.ThirdPersonHandler;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.input.util.SyntheticButton;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.Node;
import com.jme.scene.state.CullState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.util.Timer;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.PhysicsNode;
import com.jmex.physics.PhysicsSpace;
import com.jmex.physics.PhysicsUpdateCallback;
import com.jmex.physics.contact.ContactInfo;
import com.jmex.physics.material.Material;
import com.jmex.physics.util.states.PhysicsGameState;
import com.jmex.terrain.TerrainPage;
import com.sioti.client.CameraManager;
import com.sioti.client.LightManager;
import com.sioti.client.Player;
import com.sioti.client.TerrainManager;
public class SiotiPhysicsState extends PhysicsGameState {
protected ChaseCamera chaser;
private Vector3f normal = new Vector3f();
private Camera camera;
protected Node player;
protected TerrainPage terrain;
protected DynamicPhysicsNode playerNode;
private BasicPassManager passManager;
protected PhysicsNode terrainNode;
boolean playerOnFloor = true;
private DisplaySystem display;
public InputHandler input;
private Timer timer;
private static ShadowedRenderPass shadowPass = new ShadowedRenderPass();
public SiotiPhysicsState(String name) {
super(name);
display = DisplaySystem.getDisplaySystem();
camera = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
//timer = Timer.getTimer();
initGame();
}
private void initGame() {
initZBufferState();
initLight();
initCulling();
terrain = TerrainManager.buildSampleTerrain(display);
terrain.setModelBound(new BoundingBox());
terrain.updateModelBound();
player = new Player("the player", display);
// evita que o personagem fique embaixo do chao...
// iniciando no ar ...
// TODO identificar como colocar exatamente sobre o chao
player.getLocalTranslation().y = terrain.getHeight(player
.getLocalTranslation().x, player.getLocalTranslation().z) +10;
player.setModelBound(new BoundingBox());
player.updateModelBound();
initInput();
initShadows();
initPhysics();
chaser = CameraManager.buildChaseCamera(camera, player, 80, 200);
getRootNode().updateGeometricState(0.0F, true);
getRootNode().updateRenderState();
makeKeyBindings();
}
private void initPhysics() {
// make supernodes
playerNode = getPhysicsSpace().createDynamicNode();
terrainNode = getPhysicsSpace().createStaticNode();
// attach
playerNode.attachChild(player);
terrainNode.attachChild(terrain);
// physics stuff
terrainNode.generatePhysicsGeometry();
playerNode.generatePhysicsGeometry();
// move the center of mass down to let the box land on its 'feet'
// playerNode.setMass(1400);
// playerNode.computeMass();
// playerNode.setAffectedByGravity(true);
playerNode.setCenterOfMass(new Vector3f(0f, -0.5f, 0f));
Odejava.setMaxContactGeomsPerNearcallback( 50 );
// playerNode.
// this box keeps the default material
// to move the player around we create a special material for it
// and apply surface motion on it
final Material playerMaterial = new Material("player material");
playerNode.setMaterial(playerMaterial);
// the actual motion is applied in the MoveAction (see below)
// attach the player & terrain
getRootNode().attachChild(playerNode);
getRootNode().attachChild(terrainNode);
}
private void initZBufferState() {
ZBufferState buf = display.getRenderer().createZBufferState();
buf.setEnabled(true);
buf.setFunction(ZBufferState.CF_LEQUAL);
getRootNode().setRenderState(buf);
}
private void initLight() {
LightManager.addDefaultLight(getRootNode(), display);
LightManager.addFog(getRootNode(), display);
}
private void initCulling() {
CullState cs = display.getRenderer().createCullState();
cs.setCullMode(CullState.CS_BACK);
cs.setEnabled(true);
getRootNode().setRenderState(cs);
}
private void initInput() {
HashMap handlerProps = new HashMap();
handlerProps.put(ThirdPersonHandler.PROP_DOGRADUAL, "true");
handlerProps.put(ThirdPersonHandler.PROP_TURNSPEED, ""
+ (1.0f * FastMath.PI));
handlerProps.put(ThirdPersonHandler.PROP_LOCKBACKWARDS, "false");
handlerProps.put(ThirdPersonHandler.PROP_CAMERAALIGNEDMOVE, "true");
input = new ThirdPersonHandler(player, camera, handlerProps);
input.setActionSpeed(10f);
KeyBindingManager.getKeyBindingManager().set("exit",
KeyInput.KEY_ESCAPE);
}
private void initShadows() {
passManager = new BasicPassManager();
shadowPass.add(getRootNode());
shadowPass.addOccluder(player);
shadowPass.setRenderShadows(true);
shadowPass.setEnabled(true);
shadowPass.setLightingMethod(ShadowedRenderPass.MODULATIVE);
passManager.add(shadowPass);
}
public void render(float arg0) {
display.getRenderer().clearBuffers();
display.getRenderer().draw(getRootNode());
passManager.renderPasses(display.getRenderer());
}
public void update(float f) {
chaser.update(f);
input.update(f);
if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit",
false)) {
System.exit(0);
}
// We don't want the chase camera to go below the world, so always keep
// it 2 units above the level.
if (camera.getLocation().y < (terrain.getHeight(camera.getLocation()) + 2)) {
camera.getLocation().y = terrain.getHeight(camera.getLocation()) + 2;
camera.update();
}
// inclina o personagem de acordo com o terreno
//
// get the normal of the terrain at our current location. We then apply
// it to the up vector
// of the player.
terrain.getSurfaceNormal(player.getLocalTranslation(), normal);
if (normal != null) {
player.rotateUpTo(normal);
}
// Because we are changing the scene (moving the skybox and player) we
// need to update
// the graph.
getRootNode().updateGeometricState(f, true);
}
private void makeKeyBindings() {
// we map the MoveAction to the keys DELETE and PAGE DOWN for
// forward and backward
input.addAction(new MoveAction(new Vector3f(0f, 0f, 2f)),
InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_UP,
InputHandler.AXIS_NONE, false);
input.addAction(new MoveAction(new Vector3f(0f, 0f, -2f)),
InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_DOWN,
InputHandler.AXIS_NONE, false);
input.addAction(new RotateAction(new Vector3f(0, 1, 0)),
InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_LEFT,
InputHandler.AXIS_NONE, true);
input.addAction(new RotateAction(new Vector3f(0, -1, 0)),
InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_RIGHT,
InputHandler.AXIS_NONE, true);
input.addAction(new MoveAction(new Vector3f(2f, 0f, 0f)),
InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_S,
InputHandler.AXIS_NONE, false);
input.addAction(new MoveAction(new Vector3f(2f, 0f, 0f)),
InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_D,
InputHandler.AXIS_NONE, false);
// now the player should be able to jump
// we do this by applying a single force vector when the HOME key is
// pressed
// this should happen only while the player is touching the floor
// (we determine that below)
input.addAction(new InputAction() {
public void performAction(InputActionEvent evt) {
if (playerOnFloor && evt.getTriggerPressed()) {
playerNode.addForce(new Vector3f(0, 500, 0));
}
}
}, InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_SPACE,
InputHandler.AXIS_NONE, false);
// ok finally detect when the player is touching the ground:
// a simple way to do this is making a boolean variable
// (playerOnFloor) which is
// set to true on collision with the floor and to false before each
// physics computation
// collision events analoguous to Lesson8
SyntheticButton playerCollisionEventHandler = playerNode
.getCollisionEventHandler();
input.addAction(new InputAction() {
public void performAction(InputActionEvent evt) {
ContactInfo contactInfo = (ContactInfo) evt.getTriggerData();
if (contactInfo.getNode1() == terrainNode
|| contactInfo.getNode2() == terrainNode) {
playerOnFloor = true;
}
}
}, playerCollisionEventHandler.getDeviceName(),
playerCollisionEventHandler.getIndex(), InputHandler.AXIS_NONE,
true);
// when physics supports
// release events
// and a very simple callback to set the variable to false before
// each step
getPhysicsSpace().addToUpdateCallbacks(new PhysicsUpdateCallback() {
public void beforeStep(PhysicsSpace space, float time) {
playerOnFloor = false;
}
public void afterStep(PhysicsSpace space, float time) {
}
});
}
/**
* Action called on key input for applying movement of the player.
*/
private class MoveAction extends InputAction {
/**
* store direction this action instance will move.
*/
private Vector3f direction;
/**
* @param direction
* direction this action instance will move
*/
public MoveAction(Vector3f direction) {
this.direction = direction;
}
public void performAction(InputActionEvent evt) {
if (evt.getTriggerPressed()) {
// key goes down - apply motion
playerNode.getMaterial().setSurfaceMotion(direction);
} else {
// key goes up - stand still
playerNode.getMaterial().setSurfaceMotion(Vector3f.ZERO);
// note: for a game we usually won't want zero motion on key
// release but be able to combine
// keys in some way
}
}
}
private class RotateAction extends InputAction {
public Vector3f direction;
public RotateAction(Vector3f direction) {
this.direction = direction;
}
public void performAction(InputActionEvent evt) {
// key goes down - apply motion
playerNode.addTorque(direction);
}
}
}
CameraManager.java
package com.sioti.client;
import java.util.HashMap;
import com.jme.bounding.BoundingBox;
import com.jme.input.ChaseCamera;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.scene.Node;
public class CameraManager {
public static ChaseCamera buildChaseCamera(Camera camera, Node target,
float minDistance, float maxDistance) {
Vector3f targetOffset = new Vector3f();
targetOffset.y = ((BoundingBox) target.getWorldBound()).yExtent * 1.5f;
HashMap props = new HashMap();
props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "20");
props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "10");
props.put(ThirdPersonMouseLook.PROP_MAXASCENT, "" + 45
* FastMath.DEG_TO_RAD);
props.put(ThirdPersonMouseLook.PROP_MOUSEBUTTON_FOR_LOOKING,
new Integer(2));
props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(5, 0,
30 * FastMath.DEG_TO_RAD));
props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);
ChaseCamera chaser = new ChaseCamera(camera, target, props);
chaser.setMaxDistance(200);
chaser.setMinDistance(80);
return chaser;
}
}
LightManager
package com.sioti.client;
import com.jme.light.DirectionalLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.state.FogState;
import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;
public class LightManager {
public static void addDefaultLight(Node node, DisplaySystem display) {
DirectionalLight light = new DirectionalLight();
light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, .5f));
light.setDirection(new Vector3f(1, -1, 0));
light.setShadowCaster(true);
light.setEnabled(true);
/** Attach the light to a lightState and the lightState to rootNode. */
LightState lightState = display.getRenderer().createLightState();
lightState.setEnabled(true);
lightState.setGlobalAmbient(new ColorRGBA(.2f, .2f, .2f, 1f));
lightState.attach(light);
node.setRenderState(lightState);
}
public static void addFog(Node node, DisplaySystem display) {
FogState fs = display.getRenderer().createFogState();
fs.setDensity(0.5f);
fs.setEnabled(true);
fs.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 0.5f));
fs.setEnd(1000);
fs.setStart(500);
fs.setDensityFunction(FogState.DF_LINEAR);
fs.setApplyFunction(FogState.AF_PER_VERTEX);
node.setRenderState(fs);
}
}
Player
package com.sioti.client;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.model.converters.Md2ToJme;
import com.sioti.util.Log;
public class Player extends Node {
private static final long serialVersionUID = 1L;
public Player(String name, DisplaySystem display) {
super(name);
Box box = new Box("box", new Vector3f(0, 3, 0), 2f, 2f, 2f);
//Sphere box = new Sphere("ball", 25, 25, 4f);
box.setSolidColor(ColorRGBA.red);
box.setModelBound(new BoundingBox());
box.updateModelBound();
attachChild(box);
// attachChild(loadModel(display));
updateWorldBound();
}
private Node loadModel(DisplaySystem display) {
Md2ToJme converter = new Md2ToJme();
ByteArrayOutputStream BO = new ByteArrayOutputStream();
URL textu = Player.class.getClassLoader().getResource(
"media/models/alita/alita2.jpg");
URL freak = Player.class.getClassLoader().getResource(
"media/models/alita/tris.md2");
Node freakmd2 = null;
try {
long time = System.currentTimeMillis();
converter.convert(freak.openStream(), BO);
Log.debug("Time to convert from md2 to .jme:"
+ (System.currentTimeMillis() - time));
} catch (IOException e) {
Log.debug("damn exceptions:" + e.getMessage());
}
try {
long time = System.currentTimeMillis();
freakmd2 = (Node) BinaryImporter.getInstance().load(
new ByteArrayInputStream(BO.toByteArray()));
Log.debug("Time to convert from .jme to SceneGraph:"
+ (System.currentTimeMillis() - time));
} catch (IOException e) {
Log.warning("damn exceptions:" + e.getMessage());
}
TextureState ts = display.getRenderer().createTextureState();
ts.setEnabled(true);
ts.setTexture(TextureManager.loadTexture(textu,
Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR));
freakmd2.setRenderState(ts);
freakmd2.setLocalScale(.3f);
return freakmd2;
}
}
TerrainManager
package com.sioti.client;
import javax.swing.ImageIcon;
import com.jme.image.Texture;
import com.jme.math.Vector3f;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.FaultFractalHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;
public class TerrainManager {
public static TerrainPage buildSampleTerrain(DisplaySystem display) {
FaultFractalHeightMap heightMap = new FaultFractalHeightMap(257, 32, 0,
255, 0.75f);
Vector3f terrainScale = new Vector3f(10, 1, 10);
heightMap.setHeightScale(0.001f);
TerrainPage page = new TerrainPage("Terrain", 33, heightMap.getSize(),
terrainScale, heightMap.getHeightMap(), false);
page.setDetailTexture(1, 16);
ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
heightMap);
pt.addTexture(new ImageIcon(TerrainManager.class.getClassLoader()
.getResource("media/texture/grassb.png")), -128, 0, 128);
pt.addTexture(new ImageIcon(TerrainManager.class.getClassLoader()
.getResource("media/texture/dirt.jpg")), 0, 128, 255);
pt.addTexture(new ImageIcon(TerrainManager.class.getClassLoader()
.getResource("media/texture/highest.jpg")), 128, 255, 384);
pt.createTexture(512);
TextureState ts = display.getRenderer().createTextureState();
ts.setEnabled(true);
Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, true);
ts.setTexture(t1, 0);
Texture t2 = TextureManager.loadTexture(TerrainManager.class
.getClassLoader().getResource("media/texture/detail.jpg"),
Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
ts.setTexture(t2, 1);
t2.setWrap(Texture.WM_WRAP_S_WRAP_T);
t1.setApply(Texture.AM_COMBINE);
t1.setCombineFuncRGB(Texture.ACF_MODULATE);
t1.setCombineSrc0RGB(Texture.ACS_TEXTURE);
t1.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
t1.setCombineSrc1RGB(Texture.ACS_PRIMARY_COLOR);
t1.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
t1.setCombineScaleRGB(1.0f);
t2.setApply(Texture.AM_COMBINE);
t2.setCombineFuncRGB(Texture.ACF_ADD_SIGNED);
t2.setCombineSrc0RGB(Texture.ACS_TEXTURE);
t2.setCombineOp0RGB(Texture.ACO_SRC_COLOR);
t2.setCombineSrc1RGB(Texture.ACS_PREVIOUS);
t2.setCombineOp1RGB(Texture.ACO_SRC_COLOR);
t2.setCombineScaleRGB(1.0f);
page.setRenderState(ts);
return page;
}
}