FlyCam with Physics on Terrain

Hi, We have a water Scene, underneath the water is a terrain, the ocean floor. We have the ocean floor elevated so the terrain sticks up and some parts and looks like mountains.

We would like it so our camera can go underwater, but detects the ocean floor and thus the mountains, it should not clip them.

We have followed HelloTerrainCollision, the biggest obstacle is the fact we would like our “player” to fly, basically we just want to put the CapsuleCollisionShape over our camera:

[java] CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
player = new CharacterControl(capsuleShape, 0.05f);
player.setPhysicsLocation(cam.getLocation()); [/java]

This essentially works great! However our biggest issue is that our camera moves way too fast!

Our simpleUpdate method must be working on overdrive…

[java]
Vector3f camDir = cam.getDirection().clone();
Vector3f camLeft = cam.getLeft().clone();
if (isPressed()) {
if (left) {
walkDirection.addLocal(camLeft);
}
if (right) {
walkDirection.addLocal(camLeft.negate());
}
if (up) {
walkDirection.addLocal(camDir);
}
if (down) {
walkDirection.addLocal(camDir.negate());
}
player.setWalkDirection(walkDirection);
cam.setLocation(player.getPhysicsLocation());
} else { //if no button is pressed, camera should stay still and not fall
down = false;
up = false;
left = false;
right = false;
player.setWalkDirection(Vector3f.ZERO);
} [/java]

isPressed() simply checks if left,right,up,or down are true. If one of them is, that means the camera is moving. Those 4 directions are added to actionListener as seen in HelloTerrainCollision tutorial. Each of them turns to true depending on the input from the user.

It works, the collision works but our camera moves way too fast. Any ideas? Thank you.

T&D

make sure to do [java]walkDirection.set (Vector3f.ZERO);[/java] at the start of each frame (i noticed it wasn’t in the code you posted)

Ok I believe the problem lies with the WASD mappings… Here try the code, crazy fly cam right?

[java]
package mygame;

/**

  • test

  • @author normenhansen
    */
    public class TestPostWater extends SimpleApplication {

    private Vector3f lightDir = new Vector3f(-4.9236743f, -1.27054665f, 5.896916f);
    private WaterFilter water;
    TerrainQuad terrain;
    Material matRock;
    AudioNode waves;
    LowPassFilter underWaterAudioFilter = new LowPassFilter(0.5f, 0.1f);
    LowPassFilter underWaterReverbFilter = new LowPassFilter(0.5f, 0.1f);
    LowPassFilter aboveWaterAudioFilter = new LowPassFilter(1, 1);
    DirectionalLight sun = new DirectionalLight();
    DirectionalLight sun2 = new DirectionalLight();
    DirectionalLight sun3 = new DirectionalLight();
    Node cruise1 = new Node(“Cruise”);
    Node sail1 = new Node(“Sail”);
    Node cargo1 = new Node(“Cargo”);
    Node battle1 = new Node(“Battle”);
    boolean cruiser = false, sailer = false, cargos = false, battler = false;
    private int NUMBEROFSHIPS = 4;
    protected Spatial cruise;
    protected Spatial battle;
    protected Spatial sailboat;
    protected Spatial cargo;
    Boolean isRunning = true;
    int select = 0; //for boat selection
    private RigidBodyControl landscape;
    private BulletAppState bulletAppState;
    private CharacterControl player;
    private boolean left = false, right = false, up = false, down = false;
    private Vector3f walkDirection = new Vector3f();

    public static void main(String[] args) {
    TestPostWater app = new TestPostWater();
    app.start();
    }

    @Override
    public void simpleInitApp() {

     bulletAppState = new BulletAppState();
     stateManager.attach(bulletAppState);
    
     initKeys();
    
     setDisplayFps(false);
     setDisplayStatView(false);
    
     Node mainScene = new Node("Main Scene");
     rootNode.attachChild(mainScene);
    

// loadShips();

    sun.setDirection(lightDir);
    sun.setColor(ColorRGBA.White.clone().multLocal(1.7f));

    sun2.setColor(ColorRGBA.White.mult(1f));
    sun2.setDirection(new Vector3f(.5f, .5f, .5f).normalizeLocal());
    cruise.addLight(sun2);
    battle.addLight(sun2);
    sailboat.addLight(sun2);
    cargo.addLight(sun2);

    sun3.setColor(ColorRGBA.White.mult(30f));
    sun3.setDirection(new Vector3f(.5f, .5f, .5f).normalizeLocal());

    createTerrain(mainScene);
    createLights();

    mainScene.addLight(sun);

    flyCam.setMoveSpeed(30);
    cam.setLocation(new Vector3f(-700, 100, 300));//these comments were switched
    cam.setRotation(new Quaternion().fromAngleAxis(0.5f, Vector3f.UNIT_Z));
    // cam.setLocation(new Vector3f(-327.21957f, 61.6459f, 126.884346f));
    // cam.setRotation(new Quaternion(0.052168474f, 0.9443102f, -0.18395276f, 0.2678024f));


    cam.setRotation(new Quaternion().fromAngles(new float[]{FastMath.PI * 0.06f, FastMath.PI * 0.65f, 0}));


    Spatial sky = SkyFactory.createSky(assetManager, "Scenes/Beach/FullskiesSunset0068.dds", false);
    sky.setLocalScale(350);

    mainScene.attachChild(sky);
    cam.setFrustumFar(4000);
    //cam.setFrustumNear(100);

    createPlayer();
    createWater();
    //    loadCrosshairs();

    bulletAppState.getPhysicsSpace().add(terrain);
    bulletAppState.getPhysicsSpace().add(player);
}

private void createPlayer() {

    CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
    player = new CharacterControl(capsuleShape, 0.05f);

// player.setGravity(30);
// player.setJumpSpeed(150);
// player.setPhysicsLocation(new Vector3f(-10, 10, 10));
player.setPhysicsLocation(cam.getLocation());
}
private void createWater() {

    water = new WaterFilter(rootNode, lightDir);

    FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
    fpp.addFilter(water);

    BloomFilter bloom = new BloomFilter();
    bloom.setExposurePower(55);
    bloom.setBloomIntensity(1.0f);
    fpp.addFilter(bloom);
    LightScatteringFilter lsf = new LightScatteringFilter(lightDir.mult(-300));
    lsf.setLightDensity(1.0f);
    fpp.addFilter(lsf);
    DepthOfFieldFilter dof = new DepthOfFieldFilter();
    dof.setFocusDistance(0);
    dof.setFocusRange(100);
    fpp.addFilter(dof);

    water.setWaveScale(0.003f);
    water.setMaxAmplitude(2f);
    water.setFoamExistence(new Vector3f(1f, 4, 0.5f));
    water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam2.jpg"));
    //water.setNormalScale(0.5f);

    //water.setRefractionConstant(0.25f);
    water.setRefractionStrength(0.2f);
    //water.setFoamHardness(0.6f);

    water.setWaterHeight(initialWaterHeight);
    uw = cam.getLocation().y < waterHeight;

    waves = new AudioNode(assetManager, "Sound/Environment/Ocean Waves.ogg", false);
    waves.setLooping(true);
    waves.setReverbEnabled(true);

    if (uw) {
        waves.setDryFilter(new LowPassFilter(0.5f, 0.1f));
    } else {
        waves.setDryFilter(aboveWaterAudioFilter);
    }
    audioRenderer.playSource(waves);
    viewPort.addProcessor(fpp);

    inputManager.addListener(new ActionListener() {
        public void onAction(String name, boolean isPressed, float tpf) {
            if (isPressed) {
                if (name.equals("foam1")) {
                    water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam.jpg"));
                }
                if (name.equals("foam2")) {
                    water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam2.jpg"));
                }
                if (name.equals("foam3")) {
                    water.setFoamTexture((Texture2D) assetManager.loadTexture("Common/MatDefs/Water/Textures/foam3.jpg"));
                }

                if (name.equals("upRM")) {
                    water.setReflectionMapSize(Math.min(water.getReflectionMapSize() * 2, 4096));
                    System.out.println("Reflection map size : " + water.getReflectionMapSize());
                }
                if (name.equals("downRM")) {
                    water.setReflectionMapSize(Math.max(water.getReflectionMapSize() / 2, 32));
                    System.out.println("Reflection map size : " + water.getReflectionMapSize());
                }
            }
        }
    }, "foam1", "foam2", "foam3", "upRM", "downRM");
    inputManager.addMapping("foam1", new KeyTrigger(KeyInput.KEY_1));
    inputManager.addMapping("foam2", new KeyTrigger(KeyInput.KEY_2));
    inputManager.addMapping("foam3", new KeyTrigger(KeyInput.KEY_3));
    inputManager.addMapping("upRM", new KeyTrigger(KeyInput.KEY_X));
    inputManager.addMapping("downRM", new KeyTrigger(KeyInput.KEY_Y));
}

private void createTerrain(Node rootNode) {
    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) {
        e.printStackTrace();
    }
    terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
    List cameras = new ArrayList();
    cameras.add(getCamera());
    terrain.setMaterial(matRock);
    rootNode.attachChild(terrain);

    // these next 3 lines were edited so underwater terrain rises above water to create mountains
    terrain.setLocalScale(new Vector3f(5, 10, 5));
    terrain.setLocalTranslation(new Vector3f(0, 1, 0));
    terrain.setLocked(true); // unlock it so we can edit the height
    terrain.setShadowMode(ShadowMode.Receive);

    TerrainLodControl control = new TerrainLodControl(terrain, cameras);
    terrain.addControl(control);
    //Add physics to Underwater Terrain
    CollisionShape terrainShape =
            CollisionShapeFactory.createMeshShape((Node) terrain);
    landscape = new RigidBodyControl(terrainShape, 0);
    terrain.addControl(landscape);


}
//This part is to emulate tides, slightly varrying the height of the water plane
private float time = 0.0f;
private float waterHeight = 0.0f;
private float initialWaterHeight = 90f;//0.8f;
private boolean uw = false;

@Override
public void simpleUpdate(float tpf) {
    super.simpleUpdate(tpf);
    //           box.updateGeometricState();
    time += tpf;
    waterHeight = (float) Math.cos(((time * 0.6f) % FastMath.TWO_PI)) * 1.5f;
    water.setWaterHeight(initialWaterHeight + waterHeight);
    if (water.isUnderWater() && !uw) {

        waves.setDryFilter(new LowPassFilter(0.5f, 0.1f));
        uw = true;
    }
    if (!water.isUnderWater() && uw) {
        uw = false;
        //waves.setReverbEnabled(false);
        waves.setDryFilter(new LowPassFilter(1, 1f));
        //waves.setDryFilter(new LowPassFilter(1,1f));

    }

    Vector3f camDir = cam.getDirection().clone();
    Vector3f camLeft = cam.getLeft().clone();

// Vector3f camDir = cam.getDirection().clone().multLocal(0.6f);
// Vector3f camLeft = cam.getLeft().clone().multLocal(0.4f);
// walkDirection.set(0, 0, 0);
System.out.println(isPressed());
if (isPressed()) {
if (left) {
walkDirection.addLocal(camLeft);
}
if (right) {
walkDirection.addLocal(camLeft.negate());
}
if (up) {
walkDirection.addLocal(camDir);
}
if (down) {
walkDirection.addLocal(camDir.negate());
}
player.setWalkDirection(walkDirection);
cam.setLocation(player.getPhysicsLocation());
} else {
down = false;
up = false;
left = false;
right = false;
player.setWalkDirection(Vector3f.ZERO);
}
walkDirection.set (Vector3f.ZERO);

}

private boolean isPressed() {

    if (left || right || up || down) {
        return true;
    } else {
        return false;
    }
}

private void initKeys() {

    inputManager.addMapping("Select", new KeyTrigger(KeyInput.KEY_P));
    inputManager.addMapping("Reposition", new KeyTrigger(KeyInput.KEY_R));


    inputManager.addMapping("Left", new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping("Right", new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping("Up", new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping("Down", new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE));

    inputManager.addListener(actionListener, new String[]{"Select", "Reposition", "Left",
                "Right", "Up", "Down", "Jump"});

}

float y = 0;   // y value is unique to each ship. Gives proper appearance that ship is floating on the water
int min = -500;//min and max values for coordinates of where you would like your ship to appear in the water
int max = 500;
boolean shipSelected = false;    //avoids NullPointerException in getSelectedShip() among other things...
private ActionListener actionListener = new ActionListener() {
    public void onAction(String name, boolean keyPressed, float tpf) {



        if (name.equals("Left")) {
            if (keyPressed) {
                left = true;
            } else {
                left = false;
            }
        } else if (name.equals("Right")) {
            if (keyPressed) {
                right = true;
            } else {
                right = false;
            }
        } else if (name.equals("Up")) {
            if (keyPressed) {
                up = true;
            } else {
                up = false;
            }
        } else if (name.equals("Down")) {
            if (keyPressed) {
                down = true;
            } else {
                down = false;
            }
        } else if (name.equals("Jump")) {
            if (keyPressed) {
                player.jump();
            }
        }
    }
};

} [/java]

I commented out //loadShips() because you will not have the ships.
Need to import test-data for the terrain and effects libraries for the water.
Thanks