Package com.ardor3d.extension.effect.water

Source Code of com.ardor3d.extension.effect.water.WaterNode

/**
* Copyright (c) 2008-2012 Ardor Labs, Inc.
*
* This file is part of Ardor3D.
*
* Ardor3D is free software: you can redistribute it and/or modify it
* under the terms of its license which may be found in the accompanying
* LICENSE file or at <http://www.ardor3d.com/LICENSE>.
*/

package com.ardor3d.extension.effect.water;

import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.ardor3d.image.Texture;
import com.ardor3d.image.Texture2D;
import com.ardor3d.image.TextureStoreFormat;
import com.ardor3d.math.ColorRGBA;
import com.ardor3d.math.Matrix4;
import com.ardor3d.math.Plane;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.Vector4;
import com.ardor3d.math.type.ReadOnlyMatrix4;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.renderer.Camera;
import com.ardor3d.renderer.Camera.ProjectionMode;
import com.ardor3d.renderer.ContextCapabilities;
import com.ardor3d.renderer.ContextManager;
import com.ardor3d.renderer.Renderer;
import com.ardor3d.renderer.TextureRenderer;
import com.ardor3d.renderer.TextureRendererFactory;
import com.ardor3d.renderer.queue.RenderBucketType;
import com.ardor3d.renderer.state.BlendState;
import com.ardor3d.renderer.state.ClipState;
import com.ardor3d.renderer.state.CullState;
import com.ardor3d.renderer.state.FogState;
import com.ardor3d.renderer.state.GLSLShaderObjectsState;
import com.ardor3d.renderer.state.TextureState;
import com.ardor3d.scenegraph.Node;
import com.ardor3d.scenegraph.Spatial;
import com.ardor3d.scenegraph.hint.CullHint;
import com.ardor3d.scenegraph.hint.LightCombineMode;
import com.ardor3d.scenegraph.hint.TextureCombineMode;
import com.ardor3d.scenegraph.shape.Quad;
import com.ardor3d.util.TextureManager;
import com.ardor3d.util.resource.ResourceLocatorTool;
import com.google.common.collect.Lists;

/**
* The WaterNode handles rendering of a water effect on all of it's children. What is reflected in the water is
* controlled through setReflectedScene/addReflectedScene. The skybox (if any) needs to be explicitly set through
* setSkybox since it needs to be relocated when rendering the reflection. The water is treated as a plane no matter
* what the geometry is, which is controlled through the water node plane equation settings.
*/
public class WaterNode extends Node {
    private static final Logger logger = Logger.getLogger(WaterNode.class.getName());

    protected Camera cam;
    protected double tpf;
    protected double reflectionThrottle = 0f, refractionThrottle = 0f;
    protected double reflectionTime = 0, refractionTime = 0;
    protected boolean useFadeToFogColor = false;

    protected TextureRenderer tRenderer;
    protected Texture2D textureReflect;
    protected Texture2D textureReflectBlur;
    protected Texture2D textureRefract;
    protected Texture2D textureDepth;

    protected ArrayList<Spatial> renderList = Lists.newArrayList();
    protected ArrayList<Texture> texArray = Lists.newArrayList();
    protected Node skyBox;

    protected GLSLShaderObjectsState waterShader;
    protected CullState cullBackFace;
    protected TextureState textureState;
    protected TextureState fallbackTextureState;

    private Texture normalmapTexture;
    private Texture dudvTexture;
    private Texture foamTexture;
    private Texture fallbackTexture;
    private Matrix4 fallbackTextureStateMatrix;

    protected BlendState as1;
    protected FogState noFog;

    protected Plane waterPlane;
    protected Vector3 tangent;
    protected Vector3 binormal;
    protected Vector3 calcVect = new Vector3();
    protected double clipBias;
    protected ColorRGBA waterColorStart;
    protected ColorRGBA waterColorEnd;
    protected double heightFalloffStart;
    protected double heightFalloffSpeed;
    protected double waterMaxAmplitude;
    protected double speedReflection;
    protected double speedRefraction;

    protected boolean aboveWater;
    protected double normalTranslation = 0.0;
    protected double refractionTranslation = 0.0;
    protected boolean supported = true;
    protected boolean useProjectedShader = false;
    protected boolean useRefraction = false;
    protected boolean useReflection = true;
    protected int renderScale;

    protected String simpleShaderStr = "com/ardor3d/extension/effect/water/flatwatershader";
    protected String simpleShaderRefractionStr = "com/ardor3d/extension/effect/water/flatwatershader_refraction";
    protected String projectedShaderStr = "com/ardor3d/extension/effect/water/projectedwatershader";
    protected String projectedShaderRefractionStr = "com/ardor3d/extension/effect/water/projectedwatershader_refraction";
    protected String currentShaderStr;

    protected String normalMapTextureString = "";
    protected String dudvMapTextureString = "";
    protected String foamMapTextureString = "";
    protected String fallbackMapTextureString = "";

    private GLSLShaderObjectsState blurShaderVertical = null;
    private float blurSampleDistance = 0.002f;
    private Quad fullScreenQuad = null;
    private boolean doBlurReflection = true;

    private boolean initialized;

    /**
     * Resets water parameters to default values
     *
     */
    public void resetParameters() {
        waterPlane = new Plane(new Vector3(0.0, 1.0, 0.0), 0.0);
        tangent = new Vector3(1.0, 0.0, 0.0);
        binormal = new Vector3(0.0, 0.0, 1.0);

        waterMaxAmplitude = 0.0;
        clipBias = 1.0;
        waterColorStart = new ColorRGBA(0.0f, 0.0f, 0.1f, 1.0f);
        waterColorEnd = new ColorRGBA(0.0f, 0.3f, 0.1f, 1.0f);
        heightFalloffStart = 400.0;
        heightFalloffSpeed = 500.0;
        speedReflection = 0.1;
        speedRefraction = -0.05;
    }

    /**
     * Release pbuffers in TextureRenderer's. Preferably called from user cleanup method.
     */
    public void cleanup() {
        if (isSupported() && tRenderer != null) {
            tRenderer.cleanup();
        }
    }

    public boolean isSupported() {
        return supported;
    }

    /**
     * Creates a new WaterRenderPass
     *
     * @param cam
     *            main rendercam to use for reflection settings etc
     * @param renderScale
     *            how many times smaller the reflection/refraction textures should be compared to the main display
     * @param useProjectedShader
     *            true - use the projected setup for variable height water meshes, false - use the flast shader setup
     * @param useRefraction
     *            enable/disable rendering of refraction textures
     */
    public WaterNode(final Camera cam, final int renderScale, final boolean useProjectedShader,
            final boolean useRefraction) {
        this.cam = cam;
        this.useProjectedShader = useProjectedShader;
        this.useRefraction = useRefraction;
        this.renderScale = renderScale;
        resetParameters();

        waterShader = new GLSLShaderObjectsState();
        blurShaderVertical = new GLSLShaderObjectsState();

        cullBackFace = new CullState();
        cullBackFace.setEnabled(true);
        cullBackFace.setCullFace(CullState.Face.None);
    }

    /**
     * Initialize texture renderers. Load water textures. Create shaders.
     *
     * @param r
     */
    private void initialize(final Renderer r) {
        if (cam == null || initialized) {
            return;
        }
        initialized = true;

        final ContextCapabilities caps = ContextManager.getCurrentContext().getCapabilities();

        if (useRefraction && useProjectedShader && caps.getNumberOfFragmentTextureUnits() < 6 || useRefraction
                && caps.getNumberOfFragmentTextureUnits() < 5) {
            useRefraction = false;
            logger.info("Not enough textureunits, falling back to non refraction water");
        }

        if (!caps.isGLSLSupported()) {
            supported = false;
        }
        if (!(caps.isPbufferSupported() || caps.isFBOSupported())) {
            supported = false;
        }

        if (isSupported()) {
            tRenderer = TextureRendererFactory.INSTANCE.createTextureRenderer( //
                    cam.getWidth() / renderScale, // width
                    cam.getHeight() / renderScale, // height
                    8, // Depth bits... TODO: Make configurable?
                    0, // Samples... TODO: Make configurable?
                    r, caps);

            // blurSampleDistance = 1f / ((float) cam.getHeight() / renderScale);

            tRenderer.setMultipleTargets(true);
            tRenderer.setBackgroundColor(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f));
            tRenderer.getCamera().setFrustum(cam.getFrustumNear(), cam.getFrustumFar(), cam.getFrustumLeft(),
                    cam.getFrustumRight(), cam.getFrustumTop(), cam.getFrustumBottom());

            textureState = new TextureState();
            textureState.setEnabled(true);

            setupTextures();

            fullScreenQuad = new Quad("FullScreenQuad", cam.getWidth() / 4, cam.getHeight() / 4);
            fullScreenQuad.setTranslation(cam.getWidth() / 2, cam.getHeight() / 2, 0);
            fullScreenQuad.getSceneHints().setRenderBucketType(RenderBucketType.Ortho);
            fullScreenQuad.getSceneHints().setCullHint(CullHint.Never);
            fullScreenQuad.getSceneHints().setTextureCombineMode(TextureCombineMode.Replace);
            fullScreenQuad.getSceneHints().setLightCombineMode(LightCombineMode.Off);
            final TextureState ts = new TextureState();
            ts.setTexture(textureReflect);
            fullScreenQuad.setRenderState(ts);
            fullScreenQuad.setRenderState(blurShaderVertical);
            fullScreenQuad.updateWorldRenderStates(false);
        }

        if (!isSupported()) {
            createFallbackData();
        } else {
            noFog = new FogState();
            noFog.setEnabled(false);
        }

        getSceneHints().setCullHint(CullHint.Never);

        setWaterEffectOnSpatial(this);
    }

    /**
     * Load water textures.
     */
    protected void setupTextures() {
        textureReflect = new Texture2D();
        textureReflect.setWrap(Texture.WrapMode.EdgeClamp);
        textureReflect.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
        Matrix4 matrix = new Matrix4();
        matrix.setM00(-1.0);
        matrix.setM30(1.0);
        textureReflect.setTextureMatrix(matrix);
        tRenderer.setupTexture(textureReflect);

        normalmapTexture = TextureManager.load(normalMapTextureString, Texture.MinificationFilter.Trilinear,
                TextureStoreFormat.GuessCompressedFormat, true);
        textureState.setTexture(normalmapTexture, 0);
        normalmapTexture.setWrap(Texture.WrapMode.Repeat);

        textureReflectBlur = new Texture2D();
        textureReflectBlur.setWrap(Texture.WrapMode.EdgeClamp);
        textureReflectBlur.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
        textureReflectBlur.setTextureMatrix(matrix);
        tRenderer.setupTexture(textureReflectBlur);

        textureState.setTexture(textureReflectBlur, 1);

        dudvTexture = TextureManager.load(dudvMapTextureString, Texture.MinificationFilter.Trilinear,
                TextureStoreFormat.GuessNoCompressedFormat, true);
        matrix = new Matrix4();
        matrix.setM00(0.8);
        matrix.setM11(0.8);
        dudvTexture.setTextureMatrix(matrix);
        textureState.setTexture(dudvTexture, 2);
        dudvTexture.setWrap(Texture.WrapMode.Repeat);

        if (useRefraction) {
            textureRefract = new Texture2D();
            textureRefract.setWrap(Texture.WrapMode.EdgeClamp);
            textureRefract.setMagnificationFilter(Texture.MagnificationFilter.Bilinear);
            tRenderer.setupTexture(textureRefract);

            textureDepth = new Texture2D();
            textureDepth.setWrap(Texture.WrapMode.EdgeClamp);
            textureDepth.setMagnificationFilter(Texture.MagnificationFilter.NearestNeighbor);
            textureDepth.setTextureStoreFormat(TextureStoreFormat.Depth24);
            tRenderer.setupTexture(textureDepth);

            textureState.setTexture(textureRefract, 3);
            textureState.setTexture(textureDepth, 4);
        }

        if (useProjectedShader) {
            foamTexture = TextureManager.load(foamMapTextureString, Texture.MinificationFilter.Trilinear,
                    TextureStoreFormat.GuessCompressedFormat, true);
            if (useRefraction) {
                textureState.setTexture(foamTexture, 5);
            } else {
                textureState.setTexture(foamTexture, 3);
            }
            foamTexture.setWrap(Texture.WrapMode.Repeat);
        }

        reloadShader();
    }

    /**
     * Create setup to use as fallback if fancy water is not supported.
     */
    private void createFallbackData() {
        fallbackTextureState = new TextureState();
        fallbackTextureState.setEnabled(true);

        fallbackTexture = TextureManager.load(fallbackMapTextureString, Texture.MinificationFilter.Trilinear,
                TextureStoreFormat.GuessCompressedFormat, true);
        fallbackTextureState.setTexture(fallbackTexture, 0);
        fallbackTexture.setWrap(Texture.WrapMode.Repeat);

        fallbackTextureStateMatrix = new Matrix4();

        as1 = new BlendState();
        as1.setBlendEnabled(true);
        as1.setTestEnabled(true);
        as1.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
        as1.setDestinationFunction(BlendState.DestinationFunction.OneMinusSourceAlpha);
        as1.setEnabled(true);
    }

    public void update(final double tpf) {
        this.tpf = tpf;
    }

    @Override
    public void draw(final Renderer r) {
        initialize(r);

        updateTranslations();

        final double camWaterDist = waterPlane.pseudoDistance(cam.getLocation());
        aboveWater = camWaterDist >= 0;

        if (isSupported()) {
            waterShader.setUniform("tangent", tangent);
            waterShader.setUniform("binormal", binormal);
            waterShader.setUniform("useFadeToFogColor", useFadeToFogColor);
            waterShader.setUniform("waterColor", waterColorStart);
            waterShader.setUniform("waterColorEnd", waterColorEnd);
            waterShader.setUniform("normalTranslation", (float) normalTranslation);
            waterShader.setUniform("refractionTranslation", (float) refractionTranslation);
            waterShader.setUniform("abovewater", aboveWater);
            if (useProjectedShader) {
                waterShader.setUniform("cameraPos", cam.getLocation());
                waterShader.setUniform("waterHeight", (float) waterPlane.getConstant());
                waterShader.setUniform("amplitude", (float) waterMaxAmplitude);
                waterShader.setUniform("heightFalloffStart", (float) heightFalloffStart);
                waterShader.setUniform("heightFalloffSpeed", (float) heightFalloffSpeed);
            }

            final double heightTotal = clipBias + waterMaxAmplitude - waterPlane.getConstant();
            final Vector4 clipPlane = Vector4.fetchTempInstance();

            if (useReflection) {
                clipPlane.set(waterPlane.getNormal().getX(), waterPlane.getNormal().getY(), waterPlane.getNormal()
                        .getZ(), heightTotal);
                renderReflection(clipPlane);
            }

            if (useRefraction && aboveWater) {
                clipPlane.set(-waterPlane.getNormal().getX(), -waterPlane.getNormal().getY(), -waterPlane.getNormal()
                        .getZ(), -waterPlane.getConstant());
                renderRefraction(clipPlane);
            }
        }

        if (fallbackTextureState != null) {
            fallbackTextureStateMatrix.setM31(normalTranslation);
            fallbackTexture.setTextureMatrix(fallbackTextureStateMatrix);
        }

        super.draw(r);
    }

    protected void updateTranslations() {
        normalTranslation += speedReflection * tpf;
        refractionTranslation += speedRefraction * tpf;
    }

    public void reloadShader() {
        if (useProjectedShader) {
            if (useRefraction) {
                currentShaderStr = projectedShaderRefractionStr;
            } else {
                currentShaderStr = projectedShaderStr;
            }
        } else {
            if (useRefraction) {
                currentShaderStr = simpleShaderRefractionStr;
            } else {
                currentShaderStr = simpleShaderStr;
            }
        }

        try {
            logger.info("loading " + currentShaderStr);
            waterShader.setVertexShader(ResourceLocatorTool.getClassPathResourceAsStream(WaterNode.class,
                    currentShaderStr + ".vert"));
            waterShader.setFragmentShader(ResourceLocatorTool.getClassPathResourceAsStream(WaterNode.class,
                    currentShaderStr + ".frag"));
        } catch (final IOException e) {
            logger.log(Level.WARNING, "Error loading shader", e);
            return;
        }

        waterShader.setUniform("normalMap", 0);
        waterShader.setUniform("reflection", 1);
        waterShader.setUniform("dudvMap", 2);
        if (useRefraction) {
            waterShader.setUniform("refraction", 3);
            waterShader.setUniform("depthMap", 4);
        }
        if (useProjectedShader) {
            if (useRefraction) {
                waterShader.setUniform("foamMap", 5);
            } else {
                waterShader.setUniform("foamMap", 3);
            }
        }

        waterShader._needSendShader = true;

        try {
            blurShaderVertical.setVertexShader(ResourceLocatorTool.getClassPathResourceAsStream(WaterNode.class,
                    "com/ardor3d/extension/effect/bloom/bloom_blur.vert"));
            blurShaderVertical.setFragmentShader(ResourceLocatorTool.getClassPathResourceAsStream(WaterNode.class,
                    "com/ardor3d/extension/effect/bloom/bloom_blur_vertical5_down.frag"));
        } catch (final IOException ex) {
            logger.logp(Level.SEVERE, getClass().getName(), "init(Renderer)", "Could not load shaders.", ex);
        }
        blurShaderVertical.setUniform("RT", 0);
        blurShaderVertical.setUniform("sampleDist", blurSampleDistance);
        blurShaderVertical._needSendShader = true;

        logger.info("Shader reloaded...");
    }

    /**
     * Sets a spatial up for being rendered with the watereffect
     *
     * @param spatial
     *            Spatial to use as base for the watereffect
     */
    public void setWaterEffectOnSpatial(final Spatial spatial) {
        spatial.setRenderState(cullBackFace);
        if (isSupported()) {
            // spatial.setRenderBucketType(RenderBucketType.Skip);
            spatial.setRenderState(waterShader);
            spatial.setRenderState(textureState);
        } else {
            spatial.getSceneHints().setRenderBucketType(RenderBucketType.Transparent);
            spatial.getSceneHints().setLightCombineMode(LightCombineMode.Off);
            spatial.setRenderState(fallbackTextureState);
            spatial.setRenderState(as1);
        }
    }

    // temporary vectors for mem opt.
    private final Vector3 tmpLocation = new Vector3();
    private final Vector3 camReflectPos = new Vector3();
    private final Vector3 camReflectDir = new Vector3();
    private final Vector3 camReflectUp = new Vector3();
    private final Vector3 camReflectLeft = new Vector3();
    private final Vector3 camLocation = new Vector3();

    /**
     * Render water reflection RTT
     */
    private void renderReflection(final Vector4 clipPlane) {
        if (renderList.isEmpty()) {
            return;
        }

        reflectionTime += tpf;
        if (reflectionTime < reflectionThrottle) {
            return;
        }
        reflectionTime = 0;

        if (aboveWater) {
            camLocation.set(cam.getLocation());

            double planeDistance = waterPlane.pseudoDistance(camLocation);
            calcVect.set(waterPlane.getNormal()).multiplyLocal(planeDistance * 2.0f);
            camReflectPos.set(camLocation.subtractLocal(calcVect));

            camLocation.set(cam.getLocation()).addLocal(cam.getDirection());
            planeDistance = waterPlane.pseudoDistance(camLocation);
            calcVect.set(waterPlane.getNormal()).multiplyLocal(planeDistance * 2.0f);
            camReflectDir.set(camLocation.subtractLocal(calcVect)).subtractLocal(camReflectPos).normalizeLocal();

            camLocation.set(cam.getLocation()).addLocal(cam.getUp());
            planeDistance = waterPlane.pseudoDistance(camLocation);
            calcVect.set(waterPlane.getNormal()).multiplyLocal(planeDistance * 2.0f);
            camReflectUp.set(camLocation.subtractLocal(calcVect)).subtractLocal(camReflectPos).normalizeLocal();

            camReflectLeft.set(camReflectUp).crossLocal(camReflectDir).normalizeLocal();

            tRenderer.getCamera().setLocation(camReflectPos);
            tRenderer.getCamera().setDirection(camReflectDir);
            tRenderer.getCamera().setUp(camReflectUp);
            tRenderer.getCamera().setLeft(camReflectLeft);
        } else {
            tRenderer.getCamera().setLocation(cam.getLocation());
            tRenderer.getCamera().setDirection(cam.getDirection());
            tRenderer.getCamera().setUp(cam.getUp());
            tRenderer.getCamera().setLeft(cam.getLeft());
        }

        if (skyBox != null) {
            tmpLocation.set(skyBox.getTranslation());
            skyBox.setTranslation(tRenderer.getCamera().getLocation());
            skyBox.updateGeometricState(0.0f);
            skyBox.getSceneHints().setCullHint(CullHint.Never);
        }

        texArray.clear();
        if (doBlurReflection) {
            texArray.add(textureReflect);
        } else {
            texArray.add(textureReflectBlur);
        }

        tRenderer.getCamera().setProjectionMode(ProjectionMode.Custom);
        tRenderer.getCamera().setProjectionMatrix(cam.getProjectionMatrix());
        tRenderer.render(skyBox, texArray, Renderer.BUFFER_COLOR_AND_DEPTH);

        if (skyBox != null) {
            skyBox.getSceneHints().setCullHint(CullHint.Always);
        }

        modifyProjectionMatrix(clipPlane);

        tRenderer.render(renderList, texArray, Renderer.BUFFER_NONE);

        if (doBlurReflection) {
            blurReflectionTexture();
        }

        if (skyBox != null) {
            skyBox.setTranslation(tmpLocation);
            skyBox.updateGeometricState(0.0f);
            skyBox.getSceneHints().setCullHint(CullHint.Never);
        }
    }

    private void blurReflectionTexture() {
        tRenderer.render(fullScreenQuad, textureReflectBlur, Renderer.BUFFER_NONE);
    }

    /**
     * Render water refraction RTT
     */
    private void renderRefraction(final Vector4 clipPlane) {
        if (renderList.isEmpty()) {
            return;
        }

        refractionTime += tpf;
        if (refractionTime < refractionThrottle) {
            return;
        }
        refractionTime = 0;

        // tRenderer.getCamera().set(cam);
        tRenderer.getCamera().setLocation(cam.getLocation());
        tRenderer.getCamera().setDirection(cam.getDirection());
        tRenderer.getCamera().setUp(cam.getUp());
        tRenderer.getCamera().setLeft(cam.getLeft());

        CullHint cullMode = CullHint.Dynamic;
        if (skyBox != null) {
            cullMode = skyBox.getSceneHints().getCullHint();
            skyBox.getSceneHints().setCullHint(CullHint.Always);
        }

        tRenderer.getCamera().setProjectionMatrix(cam.getProjectionMatrix());

        texArray.clear();
        texArray.add(textureRefract);
        texArray.add(textureDepth);

        tRenderer.getCamera().update();
        tRenderer.getCamera().getModelViewMatrix();
        tRenderer.getCamera().getProjectionMatrix();

        tRenderer.render(renderList, texArray, Renderer.BUFFER_COLOR_AND_DEPTH);

        if (skyBox != null) {
            skyBox.getSceneHints().setCullHint(cullMode);
        }
    }

    private double sign(final double a) {
        if (a > 0.0) {
            return 1.0;
        }
        if (a < 0.0) {
            return -1.0;
        }
        return 0.0;
    }

    private double projectionMatrix[] = new double[16];
    private final Vector4 cornerPoint = new Vector4();
    private final Matrix4 tmpMatrix = new Matrix4();

    private void modifyProjectionMatrix(final Vector4 clipPlane) {
        // Get the current projection matrix
        projectionMatrix = cam.getProjectionMatrix().toArray(projectionMatrix);

        // Get the inverse transpose of the current modelview matrix
        final ReadOnlyMatrix4 modelViewMatrixInvTrans = tRenderer.getCamera().getModelViewMatrix().invert(tmpMatrix)
                .transposeLocal();
        modelViewMatrixInvTrans.applyPre(clipPlane, clipPlane);

        // Calculate the clip-space corner point opposite the clipping plane
        // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
        // transform it into camera space by multiplying it
        // by the inverse of the projection matrix
        cornerPoint.setX((sign(clipPlane.getX()) + projectionMatrix[8]) / projectionMatrix[0]);
        cornerPoint.setY((sign(clipPlane.getY()) + projectionMatrix[9]) / projectionMatrix[5]);
        cornerPoint.setZ(-1.0);
        cornerPoint.setW((1.0 + projectionMatrix[10]) / projectionMatrix[14]);

        // Calculate the scaled plane vector
        final Vector4 scaledPlaneVector = clipPlane.multiply((2.0 / clipPlane.dot(cornerPoint)), cornerPoint);

        // Replace the third row of the projection matrix
        projectionMatrix[2] = scaledPlaneVector.getX();
        projectionMatrix[6] = scaledPlaneVector.getY();
        projectionMatrix[10] = scaledPlaneVector.getZ() + 1.0;
        projectionMatrix[14] = scaledPlaneVector.getW();

        // Load it back into OpenGL
        final Matrix4 newProjectionMatrix = tmpMatrix.fromArray(projectionMatrix);
        tRenderer.getCamera().setProjectionMatrix(newProjectionMatrix);
    }

    public void removeReflectedScene(final Spatial renderNode) {
        if (logger.isLoggable(Level.INFO)) {
            logger.info("Removed reflected scene: " + renderList.remove(renderNode));
        }
    }

    public void clearReflectedScene() {
        renderList.clear();
    }

    /**
     * Adds a spatial to the list of spatials used as reflection in the water
     *
     * @param renderNode
     *            Spatial to add to the list of objects used as reflection in the water
     */
    public void addReflectedScene(final Spatial renderNode) {
        if (renderNode == null) {
            return;
        }

        if (!renderList.contains(renderNode)) {
            renderList.add(renderNode);
        }
    }

    /**
     * Sets up a node to be transformed and clipped for skybox usage
     *
     * @param skyBox
     *            Handle to a node to use as skybox
     */
    public void setSkybox(final Node skyBox) {
        if (skyBox != null) {
            final ClipState skyboxClipState = new ClipState();
            skyboxClipState.setEnabled(false);
            skyBox.setRenderState(skyboxClipState);
        }

        this.skyBox = skyBox;
    }

    public Camera getCam() {
        return cam;
    }

    public void setCam(final Camera cam) {
        this.cam = cam;
    }

    public ColorRGBA getWaterColorStart() {
        return waterColorStart;
    }

    /**
     * Color to use when the incident angle to the surface is low
     */
    public void setWaterColorStart(final ColorRGBA waterColorStart) {
        this.waterColorStart = waterColorStart;
    }

    public ColorRGBA getWaterColorEnd() {
        return waterColorEnd;
    }

    /**
     * Color to use when the incident angle to the surface is high
     */
    public void setWaterColorEnd(final ColorRGBA waterColorEnd) {
        this.waterColorEnd = waterColorEnd;
    }

    public double getHeightFalloffStart() {
        return heightFalloffStart;
    }

    /**
     * Set at what distance the waveheights should start to fade out(for projected water only)
     *
     * @param heightFalloffStart
     */
    public void setHeightFalloffStart(final double heightFalloffStart) {
        this.heightFalloffStart = heightFalloffStart;
    }

    public double getHeightFalloffSpeed() {
        return heightFalloffSpeed;
    }

    /**
     * Set the fadeout length of the waveheights, when over falloff start(for projected water only)
     *
     * @param heightFalloffStart
     */
    public void setHeightFalloffSpeed(final double heightFalloffSpeed) {
        this.heightFalloffSpeed = heightFalloffSpeed;
    }

    public double getWaterHeight() {
        return waterPlane.getConstant();
    }

    /**
     * Set base height of the waterplane(Used for reflecting the camera for rendering reflection)
     *
     * @param waterHeight
     *            Waterplane height
     */
    public void setWaterHeight(final double waterHeight) {
        waterPlane.setConstant(waterHeight);
    }

    public ReadOnlyVector3 getNormal() {
        return waterPlane.getNormal();
    }

    /**
     * Set the normal of the waterplane(Used for reflecting the camera for rendering reflection)
     *
     * @param normal
     *            Waterplane normal
     */
    public void setNormal(final Vector3 normal) {
        waterPlane.setNormal(normal);
    }

    public double getSpeedReflection() {
        return speedReflection;
    }

    /**
     * Set the movement speed of the reflectiontexture
     *
     * @param speedReflection
     *            Speed of reflectiontexture
     */
    public void setSpeedReflection(final double speedReflection) {
        this.speedReflection = speedReflection;
    }

    public double getSpeedRefraction() {
        return speedRefraction;
    }

    /**
     * Set the movement speed of the refractiontexture
     *
     * @param speedRefraction
     *            Speed of refractiontexture
     */
    public void setSpeedRefraction(final double speedRefraction) {
        this.speedRefraction = speedRefraction;
    }

    public double getWaterMaxAmplitude() {
        return waterMaxAmplitude;
    }

    /**
     * Maximum amplitude of the water, used for clipping correctly(projected water only)
     *
     * @param waterMaxAmplitude
     *            Maximum amplitude
     */
    public void setWaterMaxAmplitude(final double waterMaxAmplitude) {
        this.waterMaxAmplitude = waterMaxAmplitude;
    }

    public double getClipBias() {
        return clipBias;
    }

    public void setClipBias(final double clipBias) {
        this.clipBias = clipBias;
    }

    public Plane getWaterPlane() {
        return waterPlane;
    }

    public void setWaterPlane(final Plane waterPlane) {
        this.waterPlane = waterPlane;
    }

    public Vector3 getTangent() {
        return tangent;
    }

    public void setTangent(final Vector3 tangent) {
        this.tangent = tangent;
    }

    public Vector3 getBinormal() {
        return binormal;
    }

    public void setBinormal(final Vector3 binormal) {
        this.binormal = binormal;
    }

    public Texture getTextureReflect() {
        return textureReflect;
    }

    public Texture getTextureRefract() {
        return textureRefract;
    }

    public Texture getTextureDepth() {
        return textureDepth;
    }

    /**
     * If true, fade to fogcolor. If false, fade to 100% reflective surface
     *
     * @param value
     */
    public void useFadeToFogColor(final boolean value) {
        useFadeToFogColor = value;
    }

    public boolean isUseFadeToFogColor() {
        return useFadeToFogColor;
    }

    public boolean isUseReflection() {
        return useReflection;
    }

    /**
     * Turn reflection on and off
     *
     * @param useReflection
     */
    public void setUseReflection(final boolean useReflection) {
        if (useReflection == this.useReflection) {
            return;
        }
        this.useReflection = useReflection;
        reloadShader();
    }

    public boolean isUseRefraction() {
        return useRefraction;
    }

    /**
     * Turn refraction on and off
     *
     * @param useRefraction
     */
    public void setUseRefraction(final boolean useRefraction) {
        if (useRefraction == this.useRefraction) {
            return;
        }
        this.useRefraction = useRefraction;
        reloadShader();
    }

    public int getRenderScale() {
        return renderScale;
    }

    public void setRenderScale(final int renderScale) {
        this.renderScale = renderScale;
    }

    public boolean isUseProjectedShader() {
        return useProjectedShader;
    }

    public void setUseProjectedShader(final boolean useProjectedShader) {
        if (useProjectedShader == this.useProjectedShader) {
            return;
        }
        this.useProjectedShader = useProjectedShader;
        reloadShader();
    }

    public double getReflectionThrottle() {
        return reflectionThrottle;
    }

    public void setReflectionThrottle(final double reflectionThrottle) {
        this.reflectionThrottle = reflectionThrottle;
    }

    public double getRefractionThrottle() {
        return refractionThrottle;
    }

    public void setRefractionThrottle(final double refractionThrottle) {
        this.refractionThrottle = refractionThrottle;
    }

    public TextureState getTextureState() {
        return textureState;
    }

    public void setTextureState(final TextureState textureState) {
        this.textureState = textureState;
    }

    public void updateCamera() {
        if (isSupported()) {
            tRenderer.getCamera().setFrustum(cam.getFrustumNear(), cam.getFrustumFar(), cam.getFrustumLeft(),
                    cam.getFrustumRight(), cam.getFrustumTop(), cam.getFrustumBottom());
        }
    }

    public void setFallbackTexture(final Texture fallbackTexture) {
        this.fallbackTexture = fallbackTexture;
    }

    public Texture getFallbackTexture() {
        return fallbackTexture;
    }

    public void setNormalmapTexture(final Texture normalmapTexture) {
        this.normalmapTexture = normalmapTexture;
    }

    public Texture getNormalmapTexture() {
        return normalmapTexture;
    }

    public void setDudvTexture(final Texture dudvTexture) {
        this.dudvTexture = dudvTexture;
    }

    public Texture getDudvTexture() {
        return dudvTexture;
    }

    public void setFoamTexture(final Texture foamTexture) {
        this.foamTexture = foamTexture;
    }

    public Texture getFoamTexture() {
        return foamTexture;
    }

    /**
     * @return the normalMapTextureString
     */
    public String getNormalMapTextureString() {
        return normalMapTextureString;
    }

    /**
     * @param normalMapTextureString
     *            the normalMapTextureString to set
     */
    public void setNormalMapTextureString(final String normalMapTextureString) {
        this.normalMapTextureString = normalMapTextureString;
    }

    /**
     * @return the dudvMapTextureString
     */
    public String getDudvMapTextureString() {
        return dudvMapTextureString;
    }

    /**
     * @param dudvMapTextureString
     *            the dudvMapTextureString to set
     */
    public void setDudvMapTextureString(final String dudvMapTextureString) {
        this.dudvMapTextureString = dudvMapTextureString;
    }

    /**
     * @return the foamMapTextureString
     */
    public String getFoamMapTextureString() {
        return foamMapTextureString;
    }

    /**
     * @param foamMapTextureString
     *            the foamMapTextureString to set
     */
    public void setFoamMapTextureString(final String foamMapTextureString) {
        this.foamMapTextureString = foamMapTextureString;
    }

    /**
     * @return the fallbackMapTextureString
     */
    public String getFallbackMapTextureString() {
        return fallbackMapTextureString;
    }

    /**
     * @param fallbackMapTextureString
     *            the fallbackMapTextureString to set
     */
    public void setFallbackMapTextureString(final String fallbackMapTextureString) {
        this.fallbackMapTextureString = fallbackMapTextureString;
    }

    public boolean isDoBlurReflection() {
        return doBlurReflection;
    }

    public void setDoBlurReflection(final boolean doBlurReflection) {
        this.doBlurReflection = doBlurReflection;
    }

    public float getBlurSampleDistance() {
        return blurSampleDistance;
    }

    public void setBlurSampleDistance(final float blurSampleDistance) {
        this.blurSampleDistance = blurSampleDistance;
    }
}
TOP

Related Classes of com.ardor3d.extension.effect.water.WaterNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.