Home | History | Annotate | Download | only in water
      1 /*
      2  * Copyright (c) 2009-2012 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.water;
     33 
     34 import com.jme3.asset.AssetManager;
     35 import com.jme3.export.InputCapsule;
     36 import com.jme3.export.JmeExporter;
     37 import com.jme3.export.JmeImporter;
     38 import com.jme3.export.OutputCapsule;
     39 import com.jme3.light.DirectionalLight;
     40 import com.jme3.light.Light;
     41 import com.jme3.material.Material;
     42 import com.jme3.math.*;
     43 import com.jme3.post.Filter;
     44 import com.jme3.post.Filter.Pass;
     45 import com.jme3.renderer.Camera;
     46 import com.jme3.renderer.RenderManager;
     47 import com.jme3.renderer.ViewPort;
     48 import com.jme3.scene.Node;
     49 import com.jme3.scene.Spatial;
     50 import com.jme3.texture.Image.Format;
     51 import com.jme3.texture.Texture.WrapMode;
     52 import com.jme3.texture.Texture2D;
     53 import com.jme3.util.TempVars;
     54 import java.io.IOException;
     55 
     56 /**
     57  * The WaterFilter is a 2D post process that simulate water.
     58  * It renders water above and under water.
     59  * See this blog post for more info <a href="http://jmonkeyengine.org/2011/01/15/new-advanced-water-effect-for-jmonkeyengine-3/">http://jmonkeyengine.org/2011/01/15/new-advanced-water-effect-for-jmonkeyengine-3/</a>
     60  *
     61  *
     62  * @author Rmy Bouquet aka Nehon
     63  */
     64 public class WaterFilter extends Filter {
     65 
     66     private Pass reflectionPass;
     67     protected Spatial reflectionScene;
     68     protected ViewPort reflectionView;
     69     private Texture2D normalTexture;
     70     private Texture2D foamTexture;
     71     private Texture2D causticsTexture;
     72     private Texture2D heightTexture;
     73     private Plane plane;
     74     private Camera reflectionCam;
     75     protected Ray ray = new Ray();
     76     private Vector3f targetLocation = new Vector3f();
     77     private ReflectionProcessor reflectionProcessor;
     78     private Matrix4f biasMatrix = new Matrix4f(0.5f, 0.0f, 0.0f, 0.5f,
     79             0.0f, 0.5f, 0.0f, 0.5f,
     80             0.0f, 0.0f, 0.0f, 0.5f,
     81             0.0f, 0.0f, 0.0f, 1.0f);
     82     private Matrix4f textureProjMatrix = new Matrix4f();
     83     private boolean underWater;
     84     private RenderManager renderManager;
     85     private ViewPort viewPort;
     86     private float time = 0;
     87     //properties
     88     private float speed = 1;
     89     private Vector3f lightDirection = new Vector3f(0, -1, 0);
     90     private ColorRGBA lightColor = ColorRGBA.White;
     91     private float waterHeight = 0.0f;
     92     private ColorRGBA waterColor = new ColorRGBA(0.0078f, 0.3176f, 0.5f, 1.0f);
     93     private ColorRGBA deepWaterColor = new ColorRGBA(0.0039f, 0.00196f, 0.145f, 1.0f);
     94     private Vector3f colorExtinction = new Vector3f(5.0f, 20.0f, 30.0f);
     95     private float waterTransparency = 0.1f;
     96     private float maxAmplitude = 1.5f;
     97     private float shoreHardness = 0.1f;
     98     private boolean useFoam = true;
     99     private float foamIntensity = 0.5f;
    100     private float foamHardness = 1.0f;
    101     private Vector3f foamExistence = new Vector3f(0.45f, 4.35f, 1.5f);
    102     private float waveScale = 0.005f;
    103     private float sunScale = 3.0f;
    104     private float shininess = 0.7f;
    105     private Vector2f windDirection = new Vector2f(0.0f, -1.0f);
    106     private int reflectionMapSize = 512;
    107     private boolean useRipples = true;
    108     private float normalScale = 3.0f;
    109     private boolean useHQShoreline = true;
    110     private boolean useSpecular = true;
    111     private boolean useRefraction = true;
    112     private float refractionStrength = 0.0f;
    113     private float refractionConstant = 0.5f;
    114     private float reflectionDisplace = 30;
    115     private float underWaterFogDistance = 120;
    116     private boolean useCaustics = true;
    117     private float causticsIntensity = 0.5f;
    118 
    119     /**
    120      * Create a Water Filter
    121      */
    122     public WaterFilter() {
    123         super("WaterFilter");
    124     }
    125 
    126     public WaterFilter(Node reflectionScene, Vector3f lightDirection) {
    127         super("WaterFilter");
    128         this.reflectionScene = reflectionScene;
    129         this.lightDirection = lightDirection;
    130     }
    131 
    132     @Override
    133     protected boolean isRequiresDepthTexture() {
    134         return true;
    135     }
    136 
    137     @Override
    138     protected void preFrame(float tpf) {
    139         time = time + (tpf * speed);
    140         material.setFloat("Time", time);
    141         Camera sceneCam = viewPort.getCamera();
    142         biasMatrix.mult(sceneCam.getViewProjectionMatrix(), textureProjMatrix);
    143         material.setMatrix4("TextureProjMatrix", textureProjMatrix);
    144         material.setVector3("CameraPosition", sceneCam.getLocation());
    145         material.setMatrix4("ViewProjectionMatrixInverse", sceneCam.getViewProjectionMatrix().invert());
    146 
    147         material.setFloat("WaterHeight", waterHeight);
    148 
    149         //update reflection cam
    150         ray.setOrigin(sceneCam.getLocation());
    151         ray.setDirection(sceneCam.getDirection());
    152         plane = new Plane(Vector3f.UNIT_Y, new Vector3f(0, waterHeight, 0).dot(Vector3f.UNIT_Y));
    153         reflectionProcessor.setReflectionClipPlane(plane);
    154         boolean inv = false;
    155         if (!ray.intersectsWherePlane(plane, targetLocation)) {
    156             ray.setDirection(ray.getDirection().negateLocal());
    157             ray.intersectsWherePlane(plane, targetLocation);
    158             inv = true;
    159         }
    160         Vector3f loc = plane.reflect(sceneCam.getLocation(), new Vector3f());
    161         reflectionCam.setLocation(loc);
    162         reflectionCam.setFrustum(sceneCam.getFrustumNear(),
    163                 sceneCam.getFrustumFar(),
    164                 sceneCam.getFrustumLeft(),
    165                 sceneCam.getFrustumRight(),
    166                 sceneCam.getFrustumTop(),
    167                 sceneCam.getFrustumBottom());
    168         TempVars vars = TempVars.get();
    169 
    170 
    171         vars.vect1.set(sceneCam.getLocation()).addLocal(sceneCam.getUp());
    172         float planeDistance = plane.pseudoDistance(vars.vect1);
    173         vars.vect2.set(plane.getNormal()).multLocal(planeDistance * 2.0f);
    174         vars.vect3.set(vars.vect1.subtractLocal(vars.vect2)).subtractLocal(loc).normalizeLocal().negateLocal();
    175 
    176         reflectionCam.lookAt(targetLocation, vars.vect3);
    177         vars.release();
    178 
    179         if (inv) {
    180             reflectionCam.setAxes(reflectionCam.getLeft().negateLocal(), reflectionCam.getUp(), reflectionCam.getDirection().negateLocal());
    181         }
    182 
    183         //if we're under water no need to compute reflection
    184         if (sceneCam.getLocation().y >= waterHeight) {
    185             boolean rtb = true;
    186             if (!renderManager.isHandleTranslucentBucket()) {
    187                 renderManager.setHandleTranslucentBucket(true);
    188                 rtb = false;
    189             }
    190             renderManager.renderViewPort(reflectionView, tpf);
    191             if (!rtb) {
    192                 renderManager.setHandleTranslucentBucket(false);
    193             }
    194             renderManager.setCamera(sceneCam, false);
    195             renderManager.getRenderer().setFrameBuffer(viewPort.getOutputFrameBuffer());
    196 
    197 
    198             underWater = false;
    199         } else {
    200             underWater = true;
    201         }
    202     }
    203 
    204     @Override
    205     protected Material getMaterial() {
    206         return material;
    207     }
    208 
    209     private DirectionalLight findLight(Node node) {
    210         for (Light light : node.getWorldLightList()) {
    211             if (light instanceof DirectionalLight) {
    212                 return (DirectionalLight) light;
    213             }
    214         }
    215         for (Spatial child : node.getChildren()) {
    216             if (child instanceof Node) {
    217                 return findLight((Node) child);
    218             }
    219         }
    220 
    221         return null;
    222     }
    223 
    224     @Override
    225     protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
    226 
    227         if (reflectionScene == null) {
    228             reflectionScene = vp.getScenes().get(0);
    229             DirectionalLight l = findLight((Node) reflectionScene);
    230             if (l != null) {
    231                 lightDirection = l.getDirection();
    232             }
    233 
    234         }
    235 
    236         this.renderManager = renderManager;
    237         this.viewPort = vp;
    238         reflectionPass = new Pass();
    239         reflectionPass.init(renderManager.getRenderer(), reflectionMapSize, reflectionMapSize, Format.RGBA8, Format.Depth);
    240         reflectionCam = new Camera(reflectionMapSize, reflectionMapSize);
    241         reflectionView = new ViewPort("reflectionView", reflectionCam);
    242         reflectionView.setClearFlags(true, true, true);
    243         reflectionView.attachScene(reflectionScene);
    244         reflectionView.setOutputFrameBuffer(reflectionPass.getRenderFrameBuffer());
    245         plane = new Plane(Vector3f.UNIT_Y, new Vector3f(0, waterHeight, 0).dot(Vector3f.UNIT_Y));
    246         reflectionProcessor = new ReflectionProcessor(reflectionCam, reflectionPass.getRenderFrameBuffer(), plane);
    247         reflectionView.addProcessor(reflectionProcessor);
    248 
    249         normalTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/water_normalmap.dds");
    250         if (foamTexture == null) {
    251             foamTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/foam.jpg");
    252         }
    253         if (causticsTexture == null) {
    254             causticsTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/caustics.jpg");
    255         }
    256         heightTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/heightmap.jpg");
    257 
    258         normalTexture.setWrap(WrapMode.Repeat);
    259         foamTexture.setWrap(WrapMode.Repeat);
    260         causticsTexture.setWrap(WrapMode.Repeat);
    261         heightTexture.setWrap(WrapMode.Repeat);
    262 
    263         material = new Material(manager, "Common/MatDefs/Water/Water.j3md");
    264         material.setTexture("HeightMap", heightTexture);
    265         material.setTexture("CausticsMap", causticsTexture);
    266         material.setTexture("FoamMap", foamTexture);
    267         material.setTexture("NormalMap", normalTexture);
    268         material.setTexture("ReflectionMap", reflectionPass.getRenderedTexture());
    269 
    270         material.setFloat("WaterTransparency", waterTransparency);
    271         material.setFloat("NormalScale", normalScale);
    272         material.setFloat("R0", refractionConstant);
    273         material.setFloat("MaxAmplitude", maxAmplitude);
    274         material.setVector3("LightDir", lightDirection);
    275         material.setColor("LightColor", lightColor);
    276         material.setFloat("ShoreHardness", shoreHardness);
    277         material.setFloat("RefractionStrength", refractionStrength);
    278         material.setFloat("WaveScale", waveScale);
    279         material.setVector3("FoamExistence", foamExistence);
    280         material.setFloat("SunScale", sunScale);
    281         material.setVector3("ColorExtinction", colorExtinction);
    282         material.setFloat("Shininess", shininess);
    283         material.setColor("WaterColor", waterColor);
    284         material.setColor("DeepWaterColor", deepWaterColor);
    285         material.setVector2("WindDirection", windDirection);
    286         material.setFloat("FoamHardness", foamHardness);
    287         material.setBoolean("UseRipples", useRipples);
    288         material.setBoolean("UseHQShoreline", useHQShoreline);
    289         material.setBoolean("UseSpecular", useSpecular);
    290         material.setBoolean("UseFoam", useFoam);
    291         material.setBoolean("UseCaustics", useCaustics);
    292         material.setBoolean("UseRefraction", useRefraction);
    293         material.setFloat("ReflectionDisplace", reflectionDisplace);
    294         material.setFloat("FoamIntensity", foamIntensity);
    295         material.setFloat("UnderWaterFogDistance", underWaterFogDistance);
    296         material.setFloat("CausticsIntensity", causticsIntensity);
    297 
    298 
    299     }
    300 
    301     @Override
    302     public void write(JmeExporter ex) throws IOException {
    303         super.write(ex);
    304         OutputCapsule oc = ex.getCapsule(this);
    305 
    306         oc.write(speed, "speed", 1f);
    307         oc.write(lightDirection, "lightDirection", new Vector3f(0, -1, 0));
    308         oc.write(lightColor, "lightColor", ColorRGBA.White);
    309         oc.write(waterHeight, "waterHeight", 0.0f);
    310         oc.write(waterColor, "waterColor", new ColorRGBA(0.0078f, 0.3176f, 0.5f, 1.0f));
    311         oc.write(deepWaterColor, "deepWaterColor", new ColorRGBA(0.0039f, 0.00196f, 0.145f, 1.0f));
    312 
    313         oc.write(colorExtinction, "colorExtinction", new Vector3f(5.0f, 20.0f, 30.0f));
    314         oc.write(waterTransparency, "waterTransparency", 0.1f);
    315         oc.write(maxAmplitude, "maxAmplitude", 1.5f);
    316         oc.write(shoreHardness, "shoreHardness", 0.1f);
    317         oc.write(useFoam, "useFoam", true);
    318 
    319         oc.write(foamIntensity, "foamIntensity", 0.5f);
    320         oc.write(foamHardness, "foamHardness", 1.0f);
    321 
    322         oc.write(foamExistence, "foamExistence", new Vector3f(0.45f, 4.35f, 1.5f));
    323         oc.write(waveScale, "waveScale", 0.005f);
    324 
    325         oc.write(sunScale, "sunScale", 3.0f);
    326         oc.write(shininess, "shininess", 0.7f);
    327         oc.write(windDirection, "windDirection", new Vector2f(0.0f, -1.0f));
    328         oc.write(reflectionMapSize, "reflectionMapSize", 512);
    329         oc.write(useRipples, "useRipples", true);
    330 
    331         oc.write(normalScale, "normalScale", 3.0f);
    332         oc.write(useHQShoreline, "useHQShoreline", true);
    333 
    334         oc.write(useSpecular, "useSpecular", true);
    335 
    336         oc.write(useRefraction, "useRefraction", true);
    337         oc.write(refractionStrength, "refractionStrength", 0.0f);
    338         oc.write(refractionConstant, "refractionConstant", 0.5f);
    339         oc.write(reflectionDisplace, "reflectionDisplace", 30f);
    340         oc.write(underWaterFogDistance, "underWaterFogDistance", 120f);
    341         oc.write(causticsIntensity, "causticsIntensity", 0.5f);
    342 
    343         oc.write(useCaustics, "useCaustics", true);
    344     }
    345 
    346     @Override
    347     public void read(JmeImporter im) throws IOException {
    348         super.read(im);
    349         InputCapsule ic = im.getCapsule(this);
    350         speed = ic.readFloat("speed", 1f);
    351         lightDirection = (Vector3f) ic.readSavable("lightDirection", new Vector3f(0, -1, 0));
    352         lightColor = (ColorRGBA) ic.readSavable("lightColor", ColorRGBA.White);
    353         waterHeight = ic.readFloat("waterHeight", 0.0f);
    354         waterColor = (ColorRGBA) ic.readSavable("waterColor", new ColorRGBA(0.0078f, 0.3176f, 0.5f, 1.0f));
    355         deepWaterColor = (ColorRGBA) ic.readSavable("deepWaterColor", new ColorRGBA(0.0039f, 0.00196f, 0.145f, 1.0f));
    356 
    357         colorExtinction = (Vector3f) ic.readSavable("colorExtinction", new Vector3f(5.0f, 20.0f, 30.0f));
    358         waterTransparency = ic.readFloat("waterTransparency", 0.1f);
    359         maxAmplitude = ic.readFloat("maxAmplitude", 1.5f);
    360         shoreHardness = ic.readFloat("shoreHardness", 0.1f);
    361         useFoam = ic.readBoolean("useFoam", true);
    362 
    363         foamIntensity = ic.readFloat("foamIntensity", 0.5f);
    364         foamHardness = ic.readFloat("foamHardness", 1.0f);
    365 
    366         foamExistence = (Vector3f) ic.readSavable("foamExistence", new Vector3f(0.45f, 4.35f, 1.5f));
    367         waveScale = ic.readFloat("waveScale", 0.005f);
    368 
    369         sunScale = ic.readFloat("sunScale", 3.0f);
    370         shininess = ic.readFloat("shininess", 0.7f);
    371         windDirection = (Vector2f) ic.readSavable("windDirection", new Vector2f(0.0f, -1.0f));
    372         reflectionMapSize = ic.readInt("reflectionMapSize", 512);
    373         useRipples = ic.readBoolean("useRipples", true);
    374 
    375         normalScale = ic.readFloat("normalScale", 3.0f);
    376         useHQShoreline = ic.readBoolean("useHQShoreline", true);
    377 
    378         useSpecular = ic.readBoolean("useSpecular", true);
    379 
    380         useRefraction = ic.readBoolean("useRefraction", true);
    381         refractionStrength = ic.readFloat("refractionStrength", 0.0f);
    382         refractionConstant = ic.readFloat("refractionConstant", 0.5f);
    383         reflectionDisplace = ic.readFloat("reflectionDisplace", 30f);
    384         underWaterFogDistance = ic.readFloat("underWaterFogDistance", 120f);
    385         causticsIntensity = ic.readFloat("causticsIntensity", 0.5f);
    386 
    387         useCaustics = ic.readBoolean("useCaustics", true);
    388 
    389     }
    390 
    391     /**
    392      * gets the height of the water plane
    393      * @return
    394      */
    395     public float getWaterHeight() {
    396         return waterHeight;
    397     }
    398 
    399     /**
    400      * Sets the height of the water plane
    401      * default is 0.0
    402      * @param waterHeight
    403      */
    404     public void setWaterHeight(float waterHeight) {
    405         this.waterHeight = waterHeight;
    406     }
    407 
    408     /**
    409      * sets the scene to render in the reflection map
    410      * @param reflectionScene
    411      */
    412     public void setReflectionScene(Spatial reflectionScene) {
    413         this.reflectionScene = reflectionScene;
    414     }
    415 
    416     /**
    417      * returns the waterTransparency value
    418      * @return
    419      */
    420     public float getWaterTransparency() {
    421         return waterTransparency;
    422     }
    423 
    424     /**
    425      * Sets how fast will colours fade out. You can also think about this
    426      * values as how clear water is. Therefore use smaller values (eg. 0.05)
    427      * to have crystal clear water and bigger to achieve "muddy" water.
    428      * default is 0.1f
    429      * @param waterTransparency
    430      */
    431     public void setWaterTransparency(float waterTransparency) {
    432         this.waterTransparency = waterTransparency;
    433         if (material != null) {
    434             material.setFloat("WaterTransparency", waterTransparency);
    435         }
    436     }
    437 
    438     /**
    439      * Returns the normal scales applied to the normal map
    440      * @return
    441      */
    442     public float getNormalScale() {
    443         return normalScale;
    444     }
    445 
    446     /**
    447      * Sets the normal scaling factors to apply to the normal map.
    448      * the higher the value the more small ripples will be visible on the waves.
    449      * default is 1.0
    450      * @param normalScale
    451      */
    452     public void setNormalScale(float normalScale) {
    453         this.normalScale = normalScale;
    454         if (material != null) {
    455             material.setFloat("NormalScale", normalScale);
    456         }
    457     }
    458 
    459     /**
    460      * returns the refractoin constant
    461      * @return
    462      */
    463     public float getRefractionConstant() {
    464         return refractionConstant;
    465     }
    466 
    467     /**
    468      * This is a constant related to the index of refraction (IOR) used to compute the fresnel term.
    469      * F = R0 + (1-R0)( 1 - N.V)^5
    470      * where F is the fresnel term, R0 the constant, N the normal vector and V tne view vector.
    471      * It usually depend on the material you are lookinh through (here water).
    472      * Default value is 0.3f
    473      * In practice, the lowest the value and the less the reflection can be seen on water
    474      * @param refractionConstant
    475      */
    476     public void setRefractionConstant(float refractionConstant) {
    477         this.refractionConstant = refractionConstant;
    478         if (material != null) {
    479             material.setFloat("R0", refractionConstant);
    480         }
    481     }
    482 
    483     /**
    484      * return the maximum wave amplitude
    485      * @return
    486      */
    487     public float getMaxAmplitude() {
    488         return maxAmplitude;
    489     }
    490 
    491     /**
    492      * Sets the maximum waves amplitude
    493      * default is 1.0
    494      * @param maxAmplitude
    495      */
    496     public void setMaxAmplitude(float maxAmplitude) {
    497         this.maxAmplitude = maxAmplitude;
    498         if (material != null) {
    499             material.setFloat("MaxAmplitude", maxAmplitude);
    500         }
    501     }
    502 
    503     /**
    504      * gets the light direction
    505      * @return
    506      */
    507     public Vector3f getLightDirection() {
    508         return lightDirection;
    509     }
    510 
    511     /**
    512      * Sets the light direction
    513      * @param lightDirection
    514      */
    515     public void setLightDirection(Vector3f lightDirection) {
    516         this.lightDirection = lightDirection;
    517         if (material != null) {
    518             material.setVector3("LightDir", lightDirection);
    519         }
    520     }
    521 
    522     /**
    523      * returns the light color
    524      * @return
    525      */
    526     public ColorRGBA getLightColor() {
    527         return lightColor;
    528     }
    529 
    530     /**
    531      * Sets the light color to use
    532      * default is white
    533      * @param lightColor
    534      */
    535     public void setLightColor(ColorRGBA lightColor) {
    536         this.lightColor = lightColor;
    537         if (material != null) {
    538             material.setColor("LightColor", lightColor);
    539         }
    540     }
    541 
    542     /**
    543      * Return the shoreHardeness
    544      * @return
    545      */
    546     public float getShoreHardness() {
    547         return shoreHardness;
    548     }
    549 
    550     /**
    551      * The smaller this value is, the softer the transition between
    552      * shore and water. If you want hard edges use very big value.
    553      * Default is 0.1f.
    554      * @param shoreHardness
    555      */
    556     public void setShoreHardness(float shoreHardness) {
    557         this.shoreHardness = shoreHardness;
    558         if (material != null) {
    559             material.setFloat("ShoreHardness", shoreHardness);
    560         }
    561     }
    562 
    563     /**
    564      * returns the foam hardness
    565      * @return
    566      */
    567     public float getFoamHardness() {
    568         return foamHardness;
    569     }
    570 
    571     /**
    572      * Sets the foam hardness : How much the foam will blend with the shore to avoid hard edged water plane.
    573      * Default is 1.0
    574      * @param foamHardness
    575      */
    576     public void setFoamHardness(float foamHardness) {
    577         this.foamHardness = foamHardness;
    578         if (material != null) {
    579             material.setFloat("FoamHardness", foamHardness);
    580         }
    581     }
    582 
    583     /**
    584      * returns the refractionStrenght
    585      * @return
    586      */
    587     public float getRefractionStrength() {
    588         return refractionStrength;
    589     }
    590 
    591     /**
    592      * This value modifies current fresnel term. If you want to weaken
    593      * reflections use bigger value. If you want to empasize them use
    594      * value smaller then 0. Default is 0.0f.
    595      * @param refractionStrength
    596      */
    597     public void setRefractionStrength(float refractionStrength) {
    598         this.refractionStrength = refractionStrength;
    599         if (material != null) {
    600             material.setFloat("RefractionStrength", refractionStrength);
    601         }
    602     }
    603 
    604     /**
    605      * returns the scale factor of the waves height map
    606      * @return
    607      */
    608     public float getWaveScale() {
    609         return waveScale;
    610     }
    611 
    612     /**
    613      * Sets the scale factor of the waves height map
    614      * the smaller the value the bigger the waves
    615      * default is 0.005f
    616      * @param waveScale
    617      */
    618     public void setWaveScale(float waveScale) {
    619         this.waveScale = waveScale;
    620         if (material != null) {
    621             material.setFloat("WaveScale", waveScale);
    622         }
    623     }
    624 
    625     /**
    626      * returns the foam existance vector
    627      * @return
    628      */
    629     public Vector3f getFoamExistence() {
    630         return foamExistence;
    631     }
    632 
    633     /**
    634      * Describes at what depth foam starts to fade out and
    635      * at what it is completely invisible. The third value is at
    636      * what height foam for waves appear (+ waterHeight).
    637      * default is (0.45, 4.35, 1.0);
    638      * @param foamExistence
    639      */
    640     public void setFoamExistence(Vector3f foamExistence) {
    641         this.foamExistence = foamExistence;
    642         if (material != null) {
    643             material.setVector3("FoamExistence", foamExistence);
    644         }
    645     }
    646 
    647     /**
    648      * gets the scale of the sun
    649      * @return
    650      */
    651     public float getSunScale() {
    652         return sunScale;
    653     }
    654 
    655     /**
    656      * Sets the scale of the sun for specular effect
    657      * @param sunScale
    658      */
    659     public void setSunScale(float sunScale) {
    660         this.sunScale = sunScale;
    661         if (material != null) {
    662             material.setFloat("SunScale", sunScale);
    663         }
    664     }
    665 
    666     /**
    667      * Returns the color exctinction vector of the water
    668      * @return
    669      */
    670     public Vector3f getColorExtinction() {
    671         return colorExtinction;
    672     }
    673 
    674     /**
    675      * Return at what depth the refraction color extinct
    676      * the first value is for red
    677      * the second is for green
    678      * the third is for blue
    679      * Play with thos parameters to "trouble" the water
    680      * default is (5.0, 20.0, 30.0f);
    681      * @param colorExtinction
    682      */
    683     public void setColorExtinction(Vector3f colorExtinction) {
    684         this.colorExtinction = colorExtinction;
    685         if (material != null) {
    686             material.setVector3("ColorExtinction", colorExtinction);
    687         }
    688     }
    689 
    690     /**
    691      * Sets the foam texture
    692      * @param foamTexture
    693      */
    694     public void setFoamTexture(Texture2D foamTexture) {
    695         this.foamTexture = foamTexture;
    696         foamTexture.setWrap(WrapMode.Repeat);
    697         if (material != null) {
    698             material.setTexture("FoamMap", foamTexture);
    699         }
    700     }
    701 
    702     /**
    703      * Sets the height texture
    704      * @param heightTexture
    705      */
    706     public void setHeightTexture(Texture2D heightTexture) {
    707         this.heightTexture = heightTexture;
    708         heightTexture.setWrap(WrapMode.Repeat);
    709     }
    710 
    711     /**
    712      * Sets the normal Texture
    713      * @param normalTexture
    714      */
    715     public void setNormalTexture(Texture2D normalTexture) {
    716         this.normalTexture = normalTexture;
    717         normalTexture.setWrap(WrapMode.Repeat);
    718     }
    719 
    720     /**
    721      * return the shininess factor of the water
    722      * @return
    723      */
    724     public float getShininess() {
    725         return shininess;
    726     }
    727 
    728     /**
    729      * Sets the shinines factor of the water
    730      * default is 0.7f
    731      * @param shininess
    732      */
    733     public void setShininess(float shininess) {
    734         this.shininess = shininess;
    735         if (material != null) {
    736             material.setFloat("Shininess", shininess);
    737         }
    738     }
    739 
    740     /**
    741      * retruns the speed of the waves
    742      * @return
    743      */
    744     public float getSpeed() {
    745         return speed;
    746     }
    747 
    748     /**
    749      * Set the speed of the waves (0.0 is still) default is 1.0
    750      * @param speed
    751      */
    752     public void setSpeed(float speed) {
    753         this.speed = speed;
    754     }
    755 
    756     /**
    757      * returns the color of the water
    758      *
    759      * @return
    760      */
    761     public ColorRGBA getWaterColor() {
    762         return waterColor;
    763     }
    764 
    765     /**
    766      * Sets the color of the water
    767      * see setDeepWaterColor for deep water color
    768      * default is (0.0078f, 0.5176f, 0.5f,1.0f) (greenish blue)
    769      * @param waterColor
    770      */
    771     public void setWaterColor(ColorRGBA waterColor) {
    772         this.waterColor = waterColor;
    773         if (material != null) {
    774             material.setColor("WaterColor", waterColor);
    775         }
    776     }
    777 
    778     /**
    779      * returns the deep water color
    780      * @return
    781      */
    782     public ColorRGBA getDeepWaterColor() {
    783         return deepWaterColor;
    784     }
    785 
    786     /**
    787      * sets the deep water color
    788      * see setWaterColor for general color
    789      * default is (0.0039f, 0.00196f, 0.145f,1.0f) (very dark blue)
    790      * @param deepWaterColor
    791      */
    792     public void setDeepWaterColor(ColorRGBA deepWaterColor) {
    793         this.deepWaterColor = deepWaterColor;
    794         if (material != null) {
    795             material.setColor("DeepWaterColor", deepWaterColor);
    796         }
    797     }
    798 
    799     /**
    800      * returns the wind direction
    801      * @return
    802      */
    803     public Vector2f getWindDirection() {
    804         return windDirection;
    805     }
    806 
    807     /**
    808      * sets the wind direction
    809      * the direction where the waves move
    810      * default is (0.0f, -1.0f)
    811      * @param windDirection
    812      */
    813     public void setWindDirection(Vector2f windDirection) {
    814         this.windDirection = windDirection;
    815         if (material != null) {
    816             material.setVector2("WindDirection", windDirection);
    817         }
    818     }
    819 
    820     /**
    821      * returns the size of the reflection map
    822      * @return
    823      */
    824     public int getReflectionMapSize() {
    825         return reflectionMapSize;
    826     }
    827 
    828     /**
    829      * Sets the size of the reflection map
    830      * default is 512, the higher, the better quality, but the slower the effect.
    831      * @param reflectionMapSize
    832      */
    833     public void setReflectionMapSize(int reflectionMapSize) {
    834         this.reflectionMapSize = reflectionMapSize;
    835     }
    836 
    837     /**
    838      * returns true if the water uses foam
    839      * @return
    840      */
    841     public boolean isUseFoam() {
    842         return useFoam;
    843     }
    844 
    845     /**
    846      * set to true to use foam with water
    847      * default true
    848      * @param useFoam
    849      */
    850     public void setUseFoam(boolean useFoam) {
    851         this.useFoam = useFoam;
    852         if (material != null) {
    853             material.setBoolean("UseFoam", useFoam);
    854         }
    855 
    856     }
    857 
    858     /**
    859      * sets the texture to use to render caustics on the ground underwater
    860      * @param causticsTexture
    861      */
    862     public void setCausticsTexture(Texture2D causticsTexture) {
    863         this.causticsTexture = causticsTexture;
    864         if (material != null) {
    865             material.setTexture("causticsMap", causticsTexture);
    866         }
    867     }
    868 
    869     /**
    870      * returns true if caustics are rendered
    871      * @return
    872      */
    873     public boolean isUseCaustics() {
    874         return useCaustics;
    875     }
    876 
    877     /**
    878      * set to true if you want caustics to be rendered on the ground underwater, false otherwise
    879      * @param useCaustics
    880      */
    881     public void setUseCaustics(boolean useCaustics) {
    882         this.useCaustics = useCaustics;
    883         if (material != null) {
    884             material.setBoolean("UseCaustics", useCaustics);
    885         }
    886     }
    887 
    888     /**
    889      * return true
    890      * @return
    891      */
    892     public boolean isUseHQShoreline() {
    893         return useHQShoreline;
    894     }
    895 
    896     public void setUseHQShoreline(boolean useHQShoreline) {
    897         this.useHQShoreline = useHQShoreline;
    898         if (material != null) {
    899             material.setBoolean("UseHQShoreline", useHQShoreline);
    900         }
    901 
    902     }
    903 
    904     /**
    905      * returns true if the water use the refraction
    906      * @return
    907      */
    908     public boolean isUseRefraction() {
    909         return useRefraction;
    910     }
    911 
    912     /**
    913      * set to true to use refraction (default is true)
    914      * @param useRefraction
    915      */
    916     public void setUseRefraction(boolean useRefraction) {
    917         this.useRefraction = useRefraction;
    918         if (material != null) {
    919             material.setBoolean("UseRefraction", useRefraction);
    920         }
    921 
    922     }
    923 
    924     /**
    925      * returns true if the ater use ripples
    926      * @return
    927      */
    928     public boolean isUseRipples() {
    929         return useRipples;
    930     }
    931 
    932     /**
    933      *
    934      * Set to true tu use ripples
    935      * @param useRipples
    936      */
    937     public void setUseRipples(boolean useRipples) {
    938         this.useRipples = useRipples;
    939         if (material != null) {
    940             material.setBoolean("UseRipples", useRipples);
    941         }
    942 
    943     }
    944 
    945     /**
    946      * returns true if the water use specular
    947      * @return
    948      */
    949     public boolean isUseSpecular() {
    950         return useSpecular;
    951     }
    952 
    953     /**
    954      * Set to true to use specular lightings on the water
    955      * @param useSpecular
    956      */
    957     public void setUseSpecular(boolean useSpecular) {
    958         this.useSpecular = useSpecular;
    959         if (material != null) {
    960             material.setBoolean("UseSpecular", useSpecular);
    961         }
    962     }
    963 
    964     /**
    965      * returns the foam intensity
    966      * @return
    967      */
    968     public float getFoamIntensity() {
    969         return foamIntensity;
    970     }
    971 
    972     /**
    973      * sets the foam intensity default is 0.5f
    974      * @param foamIntensity
    975      */
    976     public void setFoamIntensity(float foamIntensity) {
    977         this.foamIntensity = foamIntensity;
    978         if (material != null) {
    979             material.setFloat("FoamIntensity", foamIntensity);
    980 
    981         }
    982     }
    983 
    984     /**
    985      * returns the reflection displace
    986      * see {@link setReflectionDisplace(float reflectionDisplace)}
    987      * @return
    988      */
    989     public float getReflectionDisplace() {
    990         return reflectionDisplace;
    991     }
    992 
    993     /**
    994      * Sets the reflection displace. define how troubled will look the reflection in the water. default is 30
    995      * @param reflectionDisplace
    996      */
    997     public void setReflectionDisplace(float reflectionDisplace) {
    998         this.reflectionDisplace = reflectionDisplace;
    999         if (material != null) {
   1000             material.setFloat("m_ReflectionDisplace", reflectionDisplace);
   1001         }
   1002     }
   1003 
   1004     /**
   1005      * returns true if the camera is under the water level
   1006      * @return
   1007      */
   1008     public boolean isUnderWater() {
   1009         return underWater;
   1010     }
   1011 
   1012     /**
   1013      * returns the distance of the fog when under water
   1014      * @return
   1015      */
   1016     public float getUnderWaterFogDistance() {
   1017         return underWaterFogDistance;
   1018     }
   1019 
   1020     /**
   1021      * sets the distance of the fog when under water.
   1022      * default is 120 (120 world units) use a high value to raise the view range under water
   1023      * @param underWaterFogDistance
   1024      */
   1025     public void setUnderWaterFogDistance(float underWaterFogDistance) {
   1026         this.underWaterFogDistance = underWaterFogDistance;
   1027         if (material != null) {
   1028             material.setFloat("UnderWaterFogDistance", underWaterFogDistance);
   1029         }
   1030     }
   1031 
   1032     /**
   1033      * get the intensity of caustics under water
   1034      * @return
   1035      */
   1036     public float getCausticsIntensity() {
   1037         return causticsIntensity;
   1038     }
   1039 
   1040     /**
   1041      * sets the intensity of caustics under water. goes from 0 to 1, default is 0.5f
   1042      * @param causticsIntensity
   1043      */
   1044     public void setCausticsIntensity(float causticsIntensity) {
   1045         this.causticsIntensity = causticsIntensity;
   1046         if (material != null) {
   1047             material.setFloat("CausticsIntensity", causticsIntensity);
   1048         }
   1049     }
   1050 }
   1051