diff --git a/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java b/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java index c880c24e7..a1240d12d 100644 --- a/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java +++ b/jme3-effects/src/main/java/com/jme3/water/WaterFilter.java @@ -31,7 +31,9 @@ */ package com.jme3.water; +import com.jme3.asset.AssetKey; import com.jme3.asset.AssetManager; +import com.jme3.asset.TextureKey; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; @@ -41,7 +43,6 @@ import com.jme3.light.Light; import com.jme3.material.Material; import com.jme3.math.*; import com.jme3.post.Filter; -import com.jme3.post.Filter.Pass; import com.jme3.renderer.Camera; import com.jme3.renderer.RenderManager; import com.jme3.renderer.Renderer; @@ -51,28 +52,35 @@ import com.jme3.scene.Spatial; import com.jme3.texture.Image.Format; import com.jme3.texture.Texture.WrapMode; import com.jme3.texture.Texture2D; -import com.jme3.util.TempVars; +import com.jme3.util.clone.Cloner; +import com.jme3.util.clone.JmeCloneable; + import java.io.IOException; /** * The WaterFilter is a 2D post process that simulate water. * It renders water above and under water. * See the jMonkeyEngine wiki for more info https://jmonkeyengine.github.io/wiki/jme3/advanced/post-processor_water.html. - * - * + * + * * @author Rémy Bouquet aka Nehon */ -public class WaterFilter extends Filter { +public class WaterFilter extends Filter implements JmeCloneable, Cloneable { + + public static final String DEFAULT_NORMAL_MAP = "Common/MatDefs/Water/Textures/water_normalmap.dds"; + public static final String DEFAULT_FOAM = "Common/MatDefs/Water/Textures/foam.jpg"; + public static final String DEFAULT_CAUSTICS = "Common/MatDefs/Water/Textures/caustics.jpg"; + public static final String DEFAULT_HEIGHT_MAP = "Common/MatDefs/Water/Textures/heightmap.jpg"; private Pass reflectionPass; protected Spatial reflectionScene; + protected Spatial rootScene; protected ViewPort reflectionView; private Texture2D normalTexture; private Texture2D foamTexture; private Texture2D causticsTexture; private Texture2D heightTexture; private Camera reflectionCam; - protected Ray ray = new Ray(); private Vector3f targetLocation = new Vector3f(); private ReflectionProcessor reflectionProcessor; private Matrix4f biasMatrix = new Matrix4f(0.5f, 0.0f, 0.0f, 0.5f, @@ -120,7 +128,9 @@ public class WaterFilter extends Filter { private Vector3f center; private float radius; private AreaShape shapeType = AreaShape.Circular; - + + private boolean needSaveReflectionScene; + public enum AreaShape{ Circular, Square @@ -201,16 +211,23 @@ public class WaterFilter extends Filter { return null; } + /** + * @return true if need to try to use direction light from a scene. + */ + protected boolean useDirectionLightFromScene() { + return true; + } + @Override protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) { + rootScene = vp.getScenes().get(0); if (reflectionScene == null) { - reflectionScene = vp.getScenes().get(0); - DirectionalLight l = findLight((Node) reflectionScene); - if (l != null) { - lightDirection = l.getDirection(); + reflectionScene = rootScene; + DirectionalLight directionalLight = findLight((Node) reflectionScene); + if (directionalLight != null && useDirectionLightFromScene()) { + lightDirection = directionalLight.getDirection(); } - } this.renderManager = renderManager; @@ -227,19 +244,25 @@ public class WaterFilter extends Filter { reflectionProcessor.setReflectionClipPlane(plane); reflectionView.addProcessor(reflectionProcessor); - normalTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/water_normalmap.dds"); + if (normalTexture == null) { + normalTexture = (Texture2D) manager.loadTexture(DEFAULT_NORMAL_MAP); + normalTexture.setWrap(WrapMode.Repeat); + } + if (foamTexture == null) { - foamTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/foam.jpg"); + foamTexture = (Texture2D) manager.loadTexture(DEFAULT_FOAM); + foamTexture.setWrap(WrapMode.Repeat); } + if (causticsTexture == null) { - causticsTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/caustics.jpg"); + causticsTexture = (Texture2D) manager.loadTexture(DEFAULT_CAUSTICS); + causticsTexture.setWrap(WrapMode.Repeat); } - heightTexture = (Texture2D) manager.loadTexture("Common/MatDefs/Water/Textures/heightmap.jpg"); - normalTexture.setWrap(WrapMode.Repeat); - foamTexture.setWrap(WrapMode.Repeat); - causticsTexture.setWrap(WrapMode.Repeat); - heightTexture.setWrap(WrapMode.Repeat); + if (heightTexture == null) { + heightTexture = (Texture2D) manager.loadTexture(DEFAULT_HEIGHT_MAP); + heightTexture.setWrap(WrapMode.Repeat); + } material = new Material(manager, "Common/MatDefs/Water/Water.j3md"); material.setTexture("HeightMap", heightTexture); @@ -278,10 +301,9 @@ public class WaterFilter extends Filter { if (center != null) { material.setVector3("Center", center); material.setFloat("Radius", radius * radius); - material.setBoolean("SquareArea", shapeType==AreaShape.Square); + material.setBoolean("SquareArea", shapeType == AreaShape.Square); } material.setFloat("WaterHeight", waterHeight); - } @Override @@ -292,8 +314,36 @@ public class WaterFilter extends Filter { @Override public void write(JmeExporter ex) throws IOException { super.write(ex); + OutputCapsule oc = ex.getCapsule(this); + final Spatial reflectionScene = getReflectionScene(); + final boolean needSaveReflectionScene = isNeedSaveReflectionScene(); + + final AssetKey causticsTextureKey = causticsTexture.getKey(); + final AssetKey heightTextureKey = heightTexture.getKey(); + final AssetKey normalTextureKey = normalTexture.getKey(); + final AssetKey foamTextureKey = foamTexture.getKey(); + + if (causticsTextureKey != null && !DEFAULT_CAUSTICS.equals(causticsTextureKey.getName())) { + oc.write(causticsTextureKey, "causticsTexture", null); + } + if (heightTextureKey != null && !DEFAULT_HEIGHT_MAP.equals(heightTextureKey.getName())) { + oc.write(heightTextureKey, "heightTexture", null); + } + if (normalTextureKey != null && !DEFAULT_NORMAL_MAP.equals(normalTextureKey.getName())) { + oc.write(normalTextureKey, "normalTexture", null); + } + if (foamTextureKey != null && !DEFAULT_FOAM.equals(foamTextureKey.getName())) { + oc.write(foamTextureKey, "foamTexture", null); + } + + oc.write(needSaveReflectionScene, "needSaveReflectionScene", false); + + if (needSaveReflectionScene) { + oc.write(reflectionScene, "reflectionScene", null); + } + oc.write(speed, "speed", 1f); oc.write(lightDirection, "lightDirection", new Vector3f(0, -1, 0)); oc.write(lightColor, "lightColor", ColorRGBA.White); @@ -382,6 +432,29 @@ public class WaterFilter extends Filter { useCaustics = ic.readBoolean("useCaustics", true); + final TextureKey causticsTextureKey = (TextureKey) ic.readSavable("causticsTexture", null); + final TextureKey heightTextureKey = (TextureKey) ic.readSavable("heightTexture", null); + final TextureKey normalTextureKey = (TextureKey) ic.readSavable("normalTexture", null); + final TextureKey foamTextureKey = (TextureKey) ic.readSavable("foamTexture", null); + + needSaveReflectionScene = ic.readBoolean("needSaveReflectionScene", false); + reflectionScene = (Spatial) ic.readSavable("reflectionScene", null); + + final AssetManager assetManager = im.getAssetManager(); + + if (causticsTextureKey != null) { + setCausticsTexture((Texture2D) assetManager.loadTexture(causticsTextureKey)); + } + if (heightTextureKey != null) { + setHeightTexture((Texture2D) assetManager.loadTexture(heightTextureKey)); + } + if (normalTextureKey != null) { + setNormalTexture((Texture2D) assetManager.loadTexture(normalTextureKey)); + } + if (foamTextureKey != null) { + setFoamTexture((Texture2D) assetManager.loadTexture(foamTextureKey)); + } + //positional attributes center = (Vector3f) ic.readSavable("center", null); radius = ic.readFloat("radius", 0f); @@ -410,15 +483,36 @@ public class WaterFilter extends Filter { } if (reflectionProcessor != null) { reflectionProcessor.setReflectionClipPlane(plane); - } + } } /** - * sets the scene to render in the reflection map - * @param reflectionScene + * Sets the scene to render in the reflection map. + * + * @param reflectionScene the refraction scene. */ - public void setReflectionScene(Spatial reflectionScene) { + public void setReflectionScene(final Spatial reflectionScene) { + + final Spatial currentScene = getReflectionScene(); + + if (reflectionView != null) { + reflectionView.detachScene(currentScene == null? rootScene : currentScene); + } + this.reflectionScene = reflectionScene; + + if (reflectionView != null) { + reflectionView.attachScene(reflectionScene == null? rootScene : reflectionScene); + } + } + + /** + * Gets the scene which is used to render in the reflection map. + * + * @return the refraction scene. + */ + public Spatial getReflectionScene() { + return reflectionScene; } /** @@ -696,8 +790,9 @@ public class WaterFilter extends Filter { } /** - * Sets the foam texture - * @param foamTexture + * Sets the foam texture. + * + * @param foamTexture the foam texture. */ public void setFoamTexture(Texture2D foamTexture) { this.foamTexture = foamTexture; @@ -707,8 +802,18 @@ public class WaterFilter extends Filter { } } + /** + * Gets the foam texture. + * + * @return the foam texture. + */ + public Texture2D getFoamTexture() { + return foamTexture; + } + /** * Sets the height texture + * * @param heightTexture */ public void setHeightTexture(Texture2D heightTexture) { @@ -720,8 +825,18 @@ public class WaterFilter extends Filter { } /** - * Sets the normal Texture - * @param normalTexture + * Gets the height texture. + * + * @return the height texture. + */ + public Texture2D getHeightTexture() { + return heightTexture; + } + + /** + * Sets the normal texture. + * + * @param normalTexture the normal texture. */ public void setNormalTexture(Texture2D normalTexture) { this.normalTexture = normalTexture; @@ -731,6 +846,15 @@ public class WaterFilter extends Filter { } } + /** + * Gets the normal texture. + * + * @return the normal texture. + */ + public Texture2D getNormalTexture() { + return normalTexture; + } + /** * return the shininess factor of the water * @return @@ -878,8 +1002,9 @@ public class WaterFilter extends Filter { } /** - * sets the texture to use to render caustics on the ground underwater - * @param causticsTexture + * Sets the texture to use to render caustics on the ground underwater. + * + * @param causticsTexture the caustics texture. */ public void setCausticsTexture(Texture2D causticsTexture) { this.causticsTexture = causticsTexture; @@ -888,6 +1013,15 @@ public class WaterFilter extends Filter { } } + /** + * Gets the texture which is used to render caustics on the ground underwater. + * + * @return the caustics texture. + */ + public Texture2D getCausticsTexture() { + return causticsTexture; + } + /** * Whether or not caustics are rendered * @return true if caustics are rendered @@ -1132,6 +1266,47 @@ public class WaterFilter extends Filter { material.setBoolean("SquareArea", shapeType==AreaShape.Square); } } - - -} + + @Override + public Object jmeClone() { + try { + return super.clone(); + } catch (final CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + @Override + public void cloneFields(final Cloner cloner, final Object original) { + this.normalTexture = cloner.clone(normalTexture); + this.foamTexture = cloner.clone(foamTexture); + this.causticsTexture = cloner.clone(causticsTexture); + this.heightTexture = cloner.clone(heightTexture); + this.targetLocation = cloner.clone(targetLocation); + this.biasMatrix = cloner.clone(biasMatrix); + this.textureProjMatrix = cloner.clone(textureProjMatrix); + this.lightDirection = cloner.clone(lightDirection); + this.lightColor = cloner.clone(lightColor); + this.waterColor = cloner.clone(waterColor); + this.deepWaterColor = cloner.clone(deepWaterColor); + this.colorExtinction = cloner.clone(colorExtinction); + this.foamExistence = cloner.clone(foamExistence); + this.windDirection = cloner.clone(windDirection); + } + + /** + * Sets the flag. + * + * @param needSaveReflectionScene true if need to save reflection scene. + */ + public void setNeedSaveReflectionScene(final boolean needSaveReflectionScene) { + this.needSaveReflectionScene = needSaveReflectionScene; + } + + /** + * @return true if need to save reflection scene. + */ + public boolean isNeedSaveReflectionScene() { + return needSaveReflectionScene; + } +} \ No newline at end of file