unfair said:
Yeah something similar to that. What I'd suggest you do is grab the cam location at the beginning of your update function, and after every line print out the current location of the camera. I suspect you'll see that one line changes the location majorly - it's probably something keeping a local reference, or somewhere you forgot to use .clone when copying a Vector3f.
Great advice, can't believe I didn't think of it myself. I can NOT figure out what's going on here.. I did what you said in two locations. One in the main update loop, and one in the updateChaser() method, which I use to keep my code tidy. In the main update loop, the position never changed. In the updateChaser loop, it went berzerk. I have NO clue how to fix this, and I've looked at every resource I know of.
Don't feel obligated to look through this entire code, but if you have time to take a quick glance, any hope of fixing this bug is appreciated.
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package fyrestonev01;
import com.jme.app.BaseGame;
import com.jme.bounding.BoundingBox;
import com.jme.input.ChaseCamera;
import com.jme.input.FirstPersonHandler;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.input.ThirdPersonHandler;
import com.jme.input.joystick.JoystickInput;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Plane;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.RenderPass;
import com.jme.scene.Node;
import com.jme.scene.PassNode;
import com.jme.scene.Skybox;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.CullState;
import com.jme.scene.state.FogState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.Timer;
import com.jmex.effects.water.WaterRenderPass;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.HashMap;
/**
*
* @author Tyler
*/
public class FyrestoneClient extends BaseGame
{
//Camera
private Camera cam;
private ChaseCamera chaser;
//End Camera
//Character
private Node charNode;
private Box character;
//End character
//Terrain
private Node terrainNode;
private Skybox skybox;
private LightState lightState;
private DirectionalLight light;
private FogState fog;
private TileSpace[] loadedTiles = new TileSpace[9];
private PassNode[] tiles = new PassNode[9];
//End Terrain
//Statistics
private Timer timer;
private float tpf;
//End statistics
//Pass Management
private BasicPassManager pManager;
private RenderPass terrainPass;
private WaterRenderPass waterPass;
private Quad waterQuad;
//End Pass Management
//Input
private InputHandler input;
//End input
//Display Settings
private int width, height, depth, freq;
private boolean fullscreen;
//End display settings
public static void main(String args[])
{
FyrestoneClient app = new FyrestoneClient();
app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
app.start();
}
@Override
protected void update(float interpolation)
{
timer.update();
tpf = timer.getTimePerFrame();
updateChaser();
input.update(tpf);
GameTaskQueueManager.getManager().getQueue(GameTaskQueue.UPDATE).execute();
checkInput();
Vector3f myLoc = cam.getLocation();
skybox.setLocalTranslation(myLoc);
updateTiles();
Vector3f transVec = new Vector3f(cam.getLocation().x, waterPass.getWaterHeight(), cam.getLocation().z);
setTextureCoords(0, transVec.y, -transVec.z, .07f);
setVertexCoords(transVec.x, transVec.y, transVec.z);
terrainNode.updateGeometricState(tpf, true);
pManager.updatePasses(tpf);
}
@Override
protected void render(float interpolation)
{
display.getRenderer().clearStatistics();
display.getRenderer().clearBuffers();
GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).execute();
pManager.renderPasses(display.getRenderer());
}
@Override
protected void initSystem()
{
try {createWindow();}
catch(JmeException e) {e.printStackTrace(); System.exit(1);}
cam = display.getRenderer().createCamera(width, height);
setupCamera();
display.getRenderer().setCamera(cam);
timer = Timer.getTimer();
}
@Override
protected void initGame()
{
createKeyBindings();
terrainNode = new Node("Terrain");
charNode = new Node("Character");
createCharacter();
setupChaser();
input = new ThirdPersonHandler(charNode, cam);
input.setActionSpeed(100f);
pManager = new BasicPassManager();
TileSpaceManager.startup();
skybox = EnvironmentManager.makeSkybox();
fog = EnvironmentManager.makeFog();
terrainNode.attachChild(skybox);
terrainNode.setRenderState(lightState);
terrainNode.setRenderState(fog);
setupZBufferState();
setupBackfaceCulling();
makeWater();
terrainNode.updateGeometricState(tpf, true);
terrainNode.updateRenderState();
terrainPass = new RenderPass();
terrainPass.add(terrainNode);
pManager.add(terrainPass);
pManager.updatePasses(tpf);
}
@Override
protected void reinit()
{
display.recreateWindow(width, height, depth, freq, fullscreen);
}
@Override
protected void cleanup()
{
display.getRenderer().cleanup();
KeyInput.destroyIfInitalized();
MouseInput.destroyIfInitalized();
JoystickInput.destroyIfInitalized();
}
private void setupCamera()
{
cam.setFrustumPerspective(45.0f, (float)display.getWidth()/(float)display.getHeight(), 1, 1000);
Vector3f loc = new Vector3f(100f, 0f, 100f);
Vector3f left = new Vector3f(-1f, 0f, 0f);
Vector3f up = new Vector3f(0f, 1f, 0f);
Vector3f dir = new Vector3f(0f, 0f, -1f);
cam.setFrame(loc, left, up, dir);
cam.update();
}
private void createWindow()
{
width = properties.getWidth();
height = properties.getHeight();
depth = properties.getDepth();
freq = properties.getFreq();
fullscreen = properties.getFullscreen();
display = DisplaySystem.getDisplaySystem(properties.getRenderer());
display.createWindow(width, height, depth, freq, fullscreen);
display.getRenderer().setBackgroundColor(ColorRGBA.blue);
display.setTitle("Fyrestone: Developer's Edition 0.00");
display.getRenderer().enableStatistics(true);
}
private void createKeyBindings()
{
KeyBindingManager.getKeyBindingManager().set("exit", KeyInput.KEY_ESCAPE);
}
private void checkInput()
{
if(KeyBindingManager.getKeyBindingManager().isValidCommand("exit", false))
{
System.out.println("Pushed escape!");
finished = true;
}
}
private void makeWater()
{
waterPass = new WaterRenderPass(cam, 6, false, true);
waterPass.setWaterPlane(new Plane(new Vector3f(0f, 1f, 0f), 1f));
waterPass.setClipBias(-1f);
waterPass.setReflectionThrottle(0f);
waterPass.setRefractionThrottle(0f);
waterQuad = new Quad("myWater", 1, 1);
FloatBuffer normBuf = waterQuad.getNormalBuffer(0);
normBuf.clear();
normBuf.put(0).put(1).put(0);
normBuf.put(0).put(1).put(0);
normBuf.put(0).put(1).put(0);
normBuf.put(0).put(1).put(0);
waterPass.setWaterEffectOnSpatial(waterQuad);
terrainNode.attachChild(waterQuad);
waterPass.setReflectedScene(terrainNode);
waterPass.setReflectedScene(skybox);
waterPass.setSkybox(skybox);
pManager.add(waterPass);
}
private void setupZBufferState()
{
ZBufferState buf = display.getRenderer().createZBufferState();
buf.setEnabled(true);
buf.setFunction(ZBufferState.CF_LEQUAL);
terrainNode.setRenderState(buf);
}
private void setupBackfaceCulling()
{
CullState cs = display.getRenderer().createCullState();
cs.setEnabled(true);
cs.setCullMode(CullState.CS_BACK);
terrainNode.setRenderState(cs);
}
private void updateTiles()
{
Vector3f myLoc = cam.getLocation();
int tile_x = (int)myLoc.x / (TileSpaceManager.TERRAINSCALE * TileSpaceManager.MAPSIZE);
int tile_y = (int)myLoc.z / (TileSpaceManager.TERRAINSCALE * TileSpaceManager.MAPSIZE);
ArrayList<TileSpace> mustStayLoaded = TileSpaceManager.getTileSpace(tile_x, tile_y).getNeighbors();
mustStayLoaded.add(TileSpaceManager.getTileSpace(tile_x, tile_y));
for(int i=0; i<loadedTiles.length; i++)
{
if(loadedTiles[i] != null)
{
boolean kill = true;
for(TileSpace ts : mustStayLoaded)
{
if(loadedTiles[i].x == ts.x && loadedTiles[i].y == ts.y)
kill = false;
}
if(kill)
{
System.out.println("************************* Killing page at " + loadedTiles[i].x + " " + loadedTiles[i].y + " *************************");
loadedTiles[i] = null;
tiles[i].removeFromParent();
tiles[i] = null;
}
}
}
for(TileSpace ts : mustStayLoaded)
{
boolean present = false;
for(int i=0; i<loadedTiles.length; i++)
{
if(loadedTiles[i] != null)
{
if(loadedTiles[i].x == ts.x && loadedTiles[i].y == ts.y)
present = true;
}
}
if(!present)
{
int x = getFirstNullPage();
loadedTiles[x] = TileSpaceManager.getTileSpace(ts.x, ts.y);
tiles[x] = TileSpaceManager.makePassNode(TileSpaceManager.makeTerrainPage(ts.x, ts.y), ts.x, ts.y);
terrainNode.attachChild(tiles[x]);
terrainNode.updateRenderState();
}
}
}
private int getFirstNullPage()
{
for(int i=0; i<loadedTiles.length; i++)
{
if(loadedTiles[i] == null)
return i;
}
return 0;
}
private void setVertexCoords(float x, float y, float z)
{
FloatBuffer vertBuf = waterQuad.getVertexBuffer(0);
vertBuf.clear();
vertBuf.put(x - EnvironmentManager.FOGEND).put(y).put(z - EnvironmentManager.FOGEND);
vertBuf.put(x - EnvironmentManager.FOGEND).put(y).put(z + EnvironmentManager.FOGEND);
vertBuf.put(x + EnvironmentManager.FOGEND).put(y).put(z + EnvironmentManager.FOGEND);
vertBuf.put(x + EnvironmentManager.FOGEND).put(y).put(z - EnvironmentManager.FOGEND);
}
private void setTextureCoords(int buffer, float x, float y, float textureScale)
{
x *= textureScale * 0.5f;
y *= textureScale * 0.5f;
textureScale = EnvironmentManager.FOGEND * textureScale;
FloatBuffer texBuf;
texBuf = waterQuad.getTextureBuffer(0, buffer);
texBuf.clear();
texBuf.put(x).put(textureScale + y);
texBuf.put(x).put(y);
texBuf.put(textureScale + x).put(y);
texBuf.put(textureScale + x).put(textureScale + y);
}
private void createCharacter()
{
character = new Box("character", new Vector3f(), 1f, 1f, 1f);
character.setModelBound(new BoundingBox());
character.updateModelBound();
charNode.attachChild(character);
terrainNode.attachChild(charNode);
charNode.updateWorldBound();
charNode.setLocalTranslation(new Vector3f(1000f,150f,1000f));
}
private void setupChaser()
{
Vector3f targetOffset = new Vector3f();
targetOffset.y = ((BoundingBox)charNode.getWorldBound()).yExtent * 1.5f;
HashMap props = new HashMap();
props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "6");
props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "3");
props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);
props.put(ThirdPersonMouseLook.PROP_MAXASCENT, ""+45 * FastMath.DEG_TO_RAD);
props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(5, 0, 30 * FastMath.DEG_TO_RAD));
props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);
chaser = new ChaseCamera(cam, charNode, props);
chaser.setMaxDistance(8);
chaser.setMinDistance(2);
}
private void updateChaser()
{
chaser.update(tpf);
Vector3f myLoc = cam.getLocation();
int tile_x = (int)myLoc.x / (TileSpaceManager.TERRAINSCALE * TileSpaceManager.MAPSIZE);
int tile_y = (int)myLoc.z / (TileSpaceManager.TERRAINSCALE * TileSpaceManager.MAPSIZE);
if(cam.getLocation().y < (TileSpaceManager.makeTerrainPage(tile_x, tile_y).getHeight(cam.getLocation())))
{
cam.getLocation().y = TileSpaceManager.makeTerrainPage(tile_x, tile_y).getHeight(cam.getLocation());
cam.update();
}
myLoc = charNode.getLocalTranslation();
float characterMinHeight = TileSpaceManager.makeTerrainPage(tile_x, tile_y).getHeight(charNode.getLocalTranslation())+((BoundingBox)charNode.getWorldBound()).yExtent;
if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight))
{
charNode.getLocalTranslation().y = characterMinHeight;
}
}
}
There is the entire code, and here are the parts I think that matter:
private void updateChaser()
{
chaser.update(tpf);
Vector3f myLoc = cam.getLocation();
int tile_x = (int)myLoc.x / (TileSpaceManager.TERRAINSCALE * TileSpaceManager.MAPSIZE);
int tile_y = (int)myLoc.z / (TileSpaceManager.TERRAINSCALE * TileSpaceManager.MAPSIZE);
if(cam.getLocation().y < (TileSpaceManager.makeTerrainPage(tile_x, tile_y).getHeight(cam.getLocation())))
{
cam.getLocation().y = TileSpaceManager.makeTerrainPage(tile_x, tile_y).getHeight(cam.getLocation());
cam.update();
}
myLoc = charNode.getLocalTranslation();
float characterMinHeight = TileSpaceManager.makeTerrainPage(tile_x, tile_y).getHeight(charNode.getLocalTranslation())+((BoundingBox)charNode.getWorldBound()).yExtent;
if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight))
{
charNode.getLocalTranslation().y = characterMinHeight;
}
}
private void setupChaser()
{
Vector3f targetOffset = new Vector3f();
targetOffset.y = ((BoundingBox)charNode.getWorldBound()).yExtent * 1.5f;
HashMap props = new HashMap();
props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "6");
props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "3");
props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);
props.put(ThirdPersonMouseLook.PROP_MAXASCENT, ""+45 * FastMath.DEG_TO_RAD);
props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(5, 0, 30 * FastMath.DEG_TO_RAD));
props.put(ChaseCamera.PROP_TARGETOFFSET, targetOffset);
chaser = new ChaseCamera(cam, charNode, props);
chaser.setMaxDistance(8);
chaser.setMinDistance(2);
}
protected void update(float interpolation)
{
timer.update();
tpf = timer.getTimePerFrame();
updateChaser();
input.update(tpf);
GameTaskQueueManager.getManager().getQueue(GameTaskQueue.UPDATE).execute();
checkInput();
Vector3f myLoc = cam.getLocation();
skybox.setLocalTranslation(myLoc);
updateTiles();
Vector3f transVec = new Vector3f(cam.getLocation().x, waterPass.getWaterHeight(), cam.getLocation().z);
setTextureCoords(0, transVec.y, -transVec.z, .07f);
setVertexCoords(transVec.x, transVec.y, transVec.z);
terrainNode.updateGeometricState(tpf, true);
pManager.updatePasses(tpf);
}