How to really swim

This is my version of the “swimming” example on the site. It is actually a falling and walking on the sea bed example. I found a water.isUnderWater(); for the camera, but that doesn’t sound promising either as a character may have his/her head outside the water. How can I tell if a character is underwater and give him swimming physics and controls?

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.renderer.RenderManager;
import com.jme3.texture.Texture;
import com.jme3.input.controls.;
import com.jme3.light.
;
import com.jme3.post.FilterPostProcessor;
import com.jme3.scene.control.CameraControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.;
import com.jme3.water.WaterFilter;
import com.jme3.scene.
;
import com.jme3.math.;
import com.jme3.util.
;
import com.jme3.texture.Texture.;
import com.jme3.scene.shape.
;
import com.jme3.bullet.;
import com.jme3.bullet.control.
;
import com.jme3.input.*;

/**

  • test

  • @author normenhansen
    */
    public class WaterSimple extends SimpleApplication implements ActionListener {
    private boolean rotateLeft = false;
    private boolean rotateRight = false;
    private boolean forward = false;
    private boolean backward = false;
    private BetterCharacterControl landControl;
    private BetterCharacterControl waterControl;
    private Node player;
    private Vector3f walkDirection = new Vector3f(0,0,0);
    private Vector3f viewDirection = new Vector3f(0,0,1);
    private float speed = 20;
    private Vector3f lightDir = new Vector3f(-2.9f, -1.2f, -5.8f);
    private WaterFilter water;
    private TerrainQuad terrain;
    private float time = 0.0f;
    private float waterHeight = 0.0f;
    private float initialWaterHeight = 0.8f;
    private Node reflectedScene;
    private BulletAppState landAppState;

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

    @Override
    public void simpleInitApp() {
    setDisplayFps(false);
    setDisplayStatView(false);

     reflectedScene=new Node("Reflected Scene");
     rootNode.attachChild(reflectedScene);
     FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
     viewPort.addProcessor(fpp);
     // add water
     water = new WaterFilter(reflectedScene, lightDir);
     water.setLightColor(ColorRGBA.White);
     water.setWindDirection(Vector2f.UNIT_XY);
     water.setLightDirection(lightDir);
     water.setSunScale(3);
     water.setWaveScale(0.005f);
     water.setMaxAmplitude(5);
     water.setWaterTransparency(.1f);
     water.setWaterColor(new ColorRGBA(0.1f, 0.6f, 0.9f, 1.0f));
     water.setDeepWaterColor(new ColorRGBA(0.0f, 0.0f, 0.1f, 1.0f));
     water.setWaterHeight(initialWaterHeight);
     fpp.addFilter(water);
     initScene();
     
     
     settings.setFrameRate(60);
     viewPort.setBackgroundColor(ColorRGBA.Cyan);
     
     landAppState = new BulletAppState();
     stateManager.attach(landAppState);
     RigidBodyControl scenePhy = new RigidBodyControl(0f);
     reflectedScene.addControl(scenePhy);
     landAppState.getPhysicsSpace().add(reflectedScene);
     rootNode.attachChild( reflectedScene );
     landAppState.getPhysicsSpace().setGravity( new Vector3f(0,-9.81f,0) );
     walkDirection.set(0, 0, 0);
     
     player = (Node) assetManager.loadModel("Models/RattleSnakey/RattleSnakey.mesh.j3o");
     player.setLocalTranslation( new Vector3f(50,6,0) );
     
     landControl = new BetterCharacterControl(1.5f , 4, 30f);
     landControl = new BetterCharacterControl(1.5f , 4, 30f);
     landControl.setJumpForce( new Vector3f(0,300,0) );
     landControl.setGravity( new Vector3f(0,-10,0) );
     landControl.setGravity( new Vector3f(0,-10,0) );
     player.addControl(landControl);
     player.addControl(landControl);
     landAppState.getPhysicsSpace().add(landControl);
     
     inputManager.addMapping("Forward", new KeyTrigger(KeyInput.KEY_W) );
     inputManager.addMapping("Back", new KeyTrigger(KeyInput.KEY_S) );
     inputManager.addMapping("Rotate Left", new KeyTrigger(KeyInput.KEY_A) );
     inputManager.addMapping("Rotate Right", new KeyTrigger(KeyInput.KEY_D) );
     inputManager.addMapping("Jump", new KeyTrigger(KeyInput.KEY_SPACE) );
     inputManager.addListener(this, "Rotate Left", "Rotate Right" );
     inputManager.addListener(this, "Forward", "Back", "Jump" );
     reflectedScene.attachChild(player);
     
     // Third person camera
     flyCam.setEnabled(false);
     ChaseCamera camNode = new ChaseCamera(cam, player, inputManager);
     camNode.setDragToRotate(true);
    

    }

    @Override
    public void simpleUpdate(float tpf) {
    // Get current forward and left vectors of the player:
    Vector3f modelForwardDir = player.getWorldRotation().mult(Vector3f.UNIT_Z);
    Vector3f modelLeftDir = player.getWorldRotation().mult(Vector3f.UNIT_X);
    // Depending on which nav keys are pressed, determine the change in direction

     walkDirection.set(0, 0, 0);
     if (forward) {
         walkDirection.addLocal(modelForwardDir.mult(speed));
     } else if (backward) {
         walkDirection.addLocal(modelForwardDir.mult(speed).negate());
     }
     landControl.setWalkDirection(walkDirection);
     // Depending on which nav keys are pressed, determine the change in rotation
     if (rotateLeft) {
         Quaternion rotateL = new Quaternion().fromAngleAxis(FastMath.PI * tpf, Vector3f.UNIT_Y);
         rotateL.multLocal(viewDirection);
     } else if (rotateRight) {
         Quaternion rotateR = new Quaternion().fromAngleAxis(-FastMath.PI * tpf, Vector3f.UNIT_Y);
         rotateR.multLocal(viewDirection);
     }
     landControl.setViewDirection(viewDirection);
     // simulate tides by varying the height of the water plane
     time += tpf;
     waterHeight = (float) Math.cos(((time * 0.6f) % FastMath.TWO_PI)) * 1.5f;
     water.setWaterHeight(initialWaterHeight + waterHeight);
    

    }

    private TerrainQuad createTerrain() {

     Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/heightmap.png");
     Material terrainMat = new Material(assetManager, "Common/MatDefs/Terrain/TerrainLighting.j3md");
     terrainMat.setBoolean("useTriPlanarMapping", false);
     terrainMat.setBoolean("WardIso", true);
     terrainMat.setTexture("AlphaMap", assetManager.loadTexture("Textures/Terrain/alphamap.png"));
     
     Texture grass = assetManager.loadTexture("Textures/Terrain/grass.jpg");
     grass.setWrap(WrapMode.Repeat);
     terrainMat.setTexture("DiffuseMap_1", grass);
     terrainMat.setFloat("DiffuseMap_1_scale", 64);
     Texture normalMap1 = assetManager.loadTexture( "Textures/Terrain/grass_normal.jpg");
     normalMap1.setWrap(WrapMode.Repeat);
     terrainMat.setTexture("NormalMap_1", normalMap1);
     Texture rock = assetManager.loadTexture("Textures/Terrain/rock.png");
     rock.setWrap(WrapMode.Repeat);
     terrainMat.setTexture("DiffuseMap", rock);
     terrainMat.setFloat("DiffuseMap_0_scale", 64);
     Texture normalMap0 = assetManager.loadTexture("Textures/Terrain/rock_normal.png");
     normalMap0.setWrap(WrapMode.Repeat);
     terrainMat.setTexture("NormalMap", normalMap0);
     
     Texture road = assetManager.loadTexture("Textures/Terrain/road.png");
     road.setWrap(WrapMode.Repeat);
     terrainMat.setTexture("DiffuseMap_2", road);
     terrainMat.setFloat("DiffuseMap_2_scale", 64);
     Texture normalMap2 = assetManager.loadTexture("Textures/Terrain/road_normal.png");
     normalMap2.setWrap(WrapMode.Repeat);
     terrainMat.setTexture("NormalMap_2", normalMap2);
     
     AbstractHeightMap heightmap = null;
     try {
       heightmap = new ImageBasedHeightMap(heightMapImage.getImage(), .5f);
       heightmap.load();
     } catch (Exception e) {
       e.printStackTrace();
     }
     heightmap.smooth(0.9f,3);
     
     terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
     terrain.setMaterial(terrainMat);
     terrain.scale(4, 4, 4);
     terrain.move(0,-110,200);
     
     return terrain;
    

    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }

    private void initScene() {
    // reflectedScene node groups everything that reflects in water,
    // including terrain, skyË› light, but not the water itself.
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection(lightDir);
    sun.setColor(ColorRGBA.White.clone().multLocal(1.7f));
    reflectedScene.addLight(sun);

     AmbientLight ambientLight = new AmbientLight();
     reflectedScene.addLight(ambientLight);
     Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false);
     reflectedScene.attachChild(sky);
     reflectedScene.attachChild(createTerrain());
    

    }

    public void onAction(String binding, boolean isPressed, float tpf) {
    if (binding.equals(“Rotate Left”)) {
    rotateLeft = isPressed;
    } else if (binding.equals(“Rotate Right”)) {
    rotateRight = isPressed;
    } else
    if (binding.equals(“Forward”)) {
    forward = isPressed;
    } else if (binding.equals(“Back”)) {
    backward = isPressed;
    } else
    if (binding.equals(“Jump”)) {
    landControl.jump();
    }
    }

}

1 Like

I’ll just make my own implementation of the jMe3 WaterFilter class for 3rd person. (Obviously the is underwater method is made for 1st person)

1 Like

The water, unless you specify something else starts at y=0

1 Like

You can easily get the water’s height. From there you can compare that value to , for example, the character’s head y-coordinate. If it’s below the current water height, you implement code for the "underwater"mechanics.

1 Like

I’ll just make my own implementation of the jMe3 WaterFilter class for 3rd person. (Obviously the is underwater method is made for 1st person)

OR, you can just add a setCamera() method to the WaterFilter class, and that way you can add your main camera to be used in the isUnderwater() condition.

1 Like

Where can i find the “swimming example on the site”, because the code provided isnt complete yet.
I’m trying to produce some swimming code myself, and i would love to see if it had already been done.

1 Like

Swimming, i imagine, would just be a state in which gravational forces are lowered and jumping allowed whilst not touching the floor. One only has to check the height of the sea vs the players Y coordinate to switch between control states. The way the sea filter works means this check will always work.

A bit of tweaking to account for eye height vs feet usually suffices.

1 Like

If your interested, I did just that here: "Matrix cannot be inverted" error whilst using .setWalkDirection (Also code for swimming) - #8 by JDC

1 Like