Hi All,
I'm incrementally diving into JME, I modify flagrush to be FPS -
- removed chase cam
- add camNode (for cam) and here starts the problems
3.I have add input to be FirstPersonHandler -> then I was unable to move more then just first button pressed and cam look stucked
4.I have changed input to NodeHandler - > much better(act like real FPS), but I'm NOT attached to the terrain
- I have input.update in the update method (and other updates - cam,tb,scene)
- I tryied to :
- attach player model to camNode and attach camNode to scene (root)
- attache camNode to player and attach player to scene
- almost all other possible combination
Still no luck, my final status is - I can move like FPS - WASD + mouse, but not attached to the terrain - can magically dive into it.
Thanks in advance (I;m sure it is something small due developer blindness for its own code).
P.S I delete some comments for space
package com.captiveimagination.jmenet.flagrush;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import jmetest.flagrushtut.lesson8.Lesson8;
import jmetest.flagrushtut.lesson9.Flag;
import jmetest.flagrushtut.lesson9.ForceFieldFence;
import jmetest.flagrushtut.lesson9.Vehicle;
import jmetest.renderer.TestSkybox;
import jmetest.terrain.TestTerrain;
import com.jme.app.BaseGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.input.NodeHandler;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.RenderPass;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.CameraNode;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.terrain.TerrainBlock;
/**
* Lesson 9
* @author Mark Powell
*/
public class FlagRush extends BaseGame {
private static final Logger logger = Logger.getLogger(FlagRush.class
.getName());
// the terrain we will drive over.
private TerrainBlock tb;
// fence that will keep us in.
private ForceFieldFence fence;
//Sky box (we update it each frame)
private Skybox skybox;
//the new player object
//private Vehicle player;
private Vehicle player;
//the flag to grab
private Flag flag;
//private ChaseCamera chaser;
protected InputHandler input;
//the timer
protected Timer timer;
// Our camera object for viewing the scene
private Camera cam;
//The chase camera, this will follow our player as he zooms around the level
private ChaseCamera chaser;
// the root node of the scene graph
private Node scene;
//used to hold on the scene and not flying around
private CameraNode camNode;
// display attributes for the window. We will keep these values
// to allow the user to change them
private int width, height, depth, freq;
private boolean fullscreen;
//store the normal of the terrain
private Vector3f normal = new Vector3f();
//height above ground level
private float agl;
private static ShadowedRenderPass shadowPass = new ShadowedRenderPass();
private BasicPassManager passManager;
protected void update(float interpolation) {
// Update the GameTaskQueue
GameTaskQueueManager.getManager().getQueue(GameTaskQueue.UPDATE).execute();
// update the time to get the framerate
timer.update();
interpolation = timer.getTimePerFrame();
//update the keyboard input (move the player around)
input.update(interpolation);
//update the chase camera to handle the player moving around.
//chaser.update(interpolation);
//update the fence to animate the force field texture
fence.update(interpolation);
//update the flag to make it flap in the wind
flag.update(interpolation);
//we want to keep the skybox around our eyes, so move it with
//the camera
skybox.setLocalTranslation(cam.getLocation());
skybox.updateGeometricState(0, true);
// if escape was pressed, we exit
if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
finished = true;
}
//We don't want the chase camera to go below the world, so always keep
//it 2 units above the level.
if(cam.getLocation().y < (tb.getHeight(cam.getLocation())+2)) {
cam.getLocation().y = tb.getHeight(cam.getLocation()) + 2;
cam.update();
}
//make sure that if the player left the level we don't crash. When we add collisions,
//the fence will do its job and keep the player inside.
float characterMinHeight = tb.getHeight(player
.getLocalTranslation())+agl;
if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
player.getLocalTranslation().y = characterMinHeight;
}
//get the normal of the terrain at our current location. We then apply it to the up vector
//of the player.
tb.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.
scene.updateGeometricState(interpolation, true);
}
/**
* draws the scene graph
*
* @see com.jme.app.BaseGame#render(float)
*/
protected void render(float interpolation) {
// Render the GameTaskQueue
GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).execute();
// Clear the screen
display.getRenderer().clearBuffers();
display.getRenderer().draw(scene);
/** Have the PassManager render. */
passManager.renderPasses(display.getRenderer());
}
/**
* initializes the display and camera.
*
* @see com.jme.app.BaseGame#initSystem()
*/
protected void initSystem() {
// store the properties information
width = settings.getWidth();
height = settings.getHeight();
depth = settings.getDepth();
freq = settings.getFrequency();
fullscreen = settings.isFullscreen();
try {
display = DisplaySystem.getDisplaySystem(settings.getRenderer());
display.setMinStencilBits(8);
display.createWindow(width, height, depth, freq, fullscreen);
cam = display.getRenderer().createCamera(width, height);
} catch (JmeException e) {
logger.log(Level.SEVERE, "Could not create displaySystem", e);
System.exit(1);
}
// set the background to black
display.getRenderer().setBackgroundColor(ColorRGBA.black);
// initialize the camera
cam.setFrustum(1.0f, 1000.0f, -0.55f, 0.55f, 0.4125f, -0.4125f);
Vector3f loc = new Vector3f(4.0f, 0.0f, 0.0f);
Vector3f left = new Vector3f(0.0f, -1.0f, 0.0f);
Vector3f up = new Vector3f(0.0f, 0.0f, 1.0f);
Vector3f dir = new Vector3f(-1.0f, 0f, 0.0f);
cam.setFrame(loc, left, up, dir);
/** Get a high resolution timer for FPS updates. */
timer = Timer.getTimer();
display.getRenderer().setCamera(cam);
KeyBindingManager.getKeyBindingManager().set("exit",
KeyInput.KEY_ESCAPE);
}
/**
* initializes the scene
*
* @see com.jme.app.BaseGame#initGame()
*/
protected void initGame() {
display.setTitle("Flag Rush");
scene = new Node("Scene graph node");
/** Create a ZBuffer to display pixels closest to the camera above farther ones. */
ZBufferState buf = display.getRenderer().createZBufferState();
buf.setEnabled(true);
buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
scene.setRenderState(buf);
CullState cs = display.getRenderer().createCullState();
cs.setCullFace(CullState.Face.Back);
scene.setRenderState(cs);
//Add terrain to the scene
buildTerrain();
//Add a flag randomly to the terrain
buildFlag();
//Light the world
buildLighting();
//add the force field fence
buildEnvironment();
//Add the skybox
buildSkyBox();
//Build the player
buildPlayer();
//build the chase cam
//buildChaseCamera();
//build the player input
buildInput();
//set up passes
buildPassManager();
cam.update();
// update the scene graph for rendering
scene.updateGeometricState(0.0f, true);
scene.updateRenderState();
}
private void buildPassManager() {
passManager = new BasicPassManager();
// Add skybox first to make sure it is in the background
RenderPass rPass = new RenderPass();
rPass.add(skybox);
passManager.add(rPass);
shadowPass.add(scene);
shadowPass.addOccluder(player);
// shadowPass.addOccluder(flag);
shadowPass.setRenderShadows(true);
shadowPass.setLightingMethod(ShadowedRenderPass.LightingMethod.Modulative);
passManager.add(shadowPass);
}
private void buildFlag() {
//create the flag and place it
flag = new Flag(tb);
scene.attachChild(flag);
flag.placeFlag();
}
private void buildPlayer() {
Spatial model = null;
try {
URL bikeFile = Lesson8.class.getClassLoader().getResource("jmetest/data/model/bike.jme");
BinaryImporter importer = new BinaryImporter();
model = (Spatial)importer.load(bikeFile.openStream());
model.setModelBound(new BoundingBox());
model.updateModelBound();
//scale it to be MUCH smaller than it is originally
model.setLocalScale(.0025f);
} catch (IOException e) {
logger
.throwing(this.getClass().toString(), "buildPlayer()",
e);
}
//set the vehicles attributes (these numbers can be thought
//of as Unit/Second).
player = new Vehicle("Player Node", model);
player.setAcceleration(15);
player.setBraking(15);
player.setTurnSpeed(2.5f);
player.setWeight(25);
player.setMaxSpeed(25);
player.setMinSpeed(15);
player = new Vehicle(" Player ", model);
player.setLocalTranslation(new Vector3f(0,0, 0));
camNode = new CameraNode( "Camera Node", cam);
camNode.setLocalTranslation(new Vector3f(100, 0, 100));
camNode.attachChild(player);
//camNode.setModelBound( new BoundingBox() );
//camNode.updateModelBound();
//camNode.updateGeometricState(0, true);
//camNode.attachChild(player);
scene.attachChild(camNode);
player.updateGeometricState(0, true);
agl = ((BoundingBox)player.getWorldBound()).yExtent;
player.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
//camNode.updateGeometricState(0,true);
}
private void buildEnvironment() {
//This is the main node of our fence
fence = new ForceFieldFence("fence");
fence.setLocalScale(5);
//now let's move the fence to to the height of the terrain and in a little bit.
fence.setLocalTranslation(new Vector3f(25, tb.getHeight(25,25)+10, 25));
scene.attachChild(fence);
}
/**
* creates a light for the terrain.
*/
private void buildLighting() {
/** Set up a basic, default light. */
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);
scene.setRenderState(lightState);
}
/**
* build the height map and terrain block.
*/
private void buildTerrain() {
// MidPointHeightMap heightMap = new MidPointHeightMap(64, 1f);
int[] heightMap = null;
ImageIcon imageIcon = null;
try {
ObjectInputStream ois = new ObjectInputStream(getClass().getClassLoader().getResourceAsStream("resource/heightmap.data"));
heightMap = (int[])ois.readObject();
imageIcon = (ImageIcon)ois.readObject();
ois.close();
} catch(Exception exc) {
throw new RuntimeException(exc);
}
float[] tempMap = new float[heightMap.length];
for(int i = 0; i < heightMap.length; i++){
tempMap[i] = heightMap[i];
}
// Scale the data
Vector3f terrainScale = new Vector3f(4, 0.0575f, 4);
// create a terrainblock
tb = new TerrainBlock("Terrain", 64, terrainScale, tempMap,
new Vector3f(0, 0, 0));
tb.setModelBound(new BoundingBox());
tb.updateModelBound();
// assign the texture to the terrain
TextureState ts = display.getRenderer().createTextureState();
Texture t1 = TextureManager.loadTexture(imageIcon.getImage(),
Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
ts.setTexture(t1, 0);
//load a detail texture and set the combine modes for the two terrain textures.
Texture t2 = TextureManager.loadTexture(
TestTerrain.class.getClassLoader().getResource(
"jmetest/data/texture/Detail.jpg"),
Texture.MinificationFilter.Trilinear,
Texture.MagnificationFilter.Bilinear);
ts.setTexture(t2, 1);
t2.setWrap(Texture.WrapMode.Repeat);
t1.setApply(Texture.ApplyMode.Combine);
t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate);
t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor);
t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);
t1.setCombineScaleRGB(Texture.CombinerScale.One);
t2.setApply(Texture.ApplyMode.Combine);
t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned);
t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
t2.setCombineSrc1RGB(Texture.CombinerSource.Previous);
t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);
t2.setCombineScaleRGB(Texture.CombinerScale.One);
tb.setRenderState(ts);
//set the detail parameters.
tb.setDetailTexture(1, 16);
tb.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
scene.attachChild(tb);
}
private void buildSkyBox() {
skybox = new Skybox("skybox", 10, 10, 10);
Texture north = TextureManager.loadTexture(
TestSkybox.class.getClassLoader().getResource(
"jmetest/data/texture/north.jpg"),
Texture.MinificationFilter.BilinearNearestMipMap,
Texture.MagnificationFilter.Bilinear);
Texture south = TextureManager.loadTexture(
TestSkybox.class.getClassLoader().getResource(
"jmetest/data/texture/south.jpg"),
Texture.MinificationFilter.BilinearNoMipMaps,
Texture.MagnificationFilter.Bilinear);
Texture east = TextureManager.loadTexture(
TestSkybox.class.getClassLoader().getResource(
"jmetest/data/texture/east.jpg"),
Texture.MinificationFilter.BilinearNoMipMaps,
Texture.MagnificationFilter.Bilinear);
Texture west = TextureManager.loadTexture(
TestSkybox.class.getClassLoader().getResource(
"jmetest/data/texture/west.jpg"),
Texture.MinificationFilter.BilinearNoMipMaps,
Texture.MagnificationFilter.Bilinear);
Texture up = TextureManager.loadTexture(
TestSkybox.class.getClassLoader().getResource(
"jmetest/data/texture/top.jpg"),
Texture.MinificationFilter.BilinearNoMipMaps,
Texture.MagnificationFilter.Bilinear);
Texture down = TextureManager.loadTexture(
TestSkybox.class.getClassLoader().getResource(
"jmetest/data/texture/bottom.jpg"),
Texture.MinificationFilter.BilinearNoMipMaps,
Texture.MagnificationFilter.Bilinear);
skybox.setTexture(Skybox.Face.North, north);
skybox.setTexture(Skybox.Face.West, west);
skybox.setTexture(Skybox.Face.South, south);
skybox.setTexture(Skybox.Face.East, east);
skybox.setTexture(Skybox.Face.Up, up);
skybox.setTexture(Skybox.Face.Down, down);
skybox.preloadTextures();
skybox.updateRenderState();
// scene.attachChild(skybox);
}
private void buildInput() {
//input = new CustomFirstPersonHandler(cam, 50, 20, player);
input = new NodeHandler( camNode, 15f, 1 );
}
protected void reinit() {
display.recreateWindow(width, height, depth, freq, fullscreen);
}
protected void quit() {
super.quit();
System.exit(0);
}
protected void cleanup() {
KeyInput.destroyIfInitalized();
MouseInput.destroyIfInitalized();
}
}