/*
 * Copyright (c) 2009-2012 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.material;

import com.jme3.export.*;
import com.jme3.scene.Mesh;
import com.jme3.scene.Mesh.Mode;
import java.io.IOException;

/**
 * <code>RenderState</code> specifies material rendering properties that cannot
 * be controlled by a shader on a {@link Material}. The properties
 * allow manipulation of rendering features such as depth testing, alpha blending,
 * face culling, stencil operations, and much more.
 *
 * @author Kirill Vainer
 */
public class RenderState implements Cloneable, Savable {

    /**
     * The <code>DEFAULT</code> render state is the one used by default
     * on all materials unless changed otherwise by the user.
     *
     * <p>
     * It has the following properties:
     * <ul>
     * <li>Back Face Culling</li>
     * <li>Depth Testing Enabled</li>
     * <li>Depth Writing Enabled</li>
     * </ul>
     */
    public static final RenderState DEFAULT = new RenderState();
    /**
     * The <code>NULL</code> render state is identical to the {@link RenderState#DEFAULT}
     * render state except that depth testing and face culling are disabled.
     */
    public static final RenderState NULL = new RenderState();
    /**
     * The <code>ADDITIONAL</code> render state is identical to the
     * {@link RenderState#DEFAULT} render state except that all apply
     * values are set to false. This allows the <code>ADDITIONAL</code> render
     * state to be combined with other state but only influencing values
     * that were changed from the original.
     */
    public static final RenderState ADDITIONAL = new RenderState();

    /**
     * <code>TestFunction</code> specifies the testing function for stencil test
     * function.
     *
     * <p>
     * The reference value given in the stencil command is the input value while
     * the reference is the value already in the stencil buffer.
     */
    public enum TestFunction {

        /**
         * The test always fails
         */
        Never,
        /**
         * The test succeeds if the input value is equal to the reference value.
         */
        Equal,
        /**
         * The test succeeds if the input value is less than the reference value.
         */
        Less,
        /**
         * The test succeeds if the input value is less than or equal to
         * the reference value.
         */
        LessOrEqual,
        /**
         * The test succeeds if the input value is greater than the reference value.
         */
        Greater,
        /**
         * The test succeeds if the input value is greater than or equal to
         * the reference value.
         */
        GreaterOrEqual,
        /**
         * The test succeeds if the input value does not equal the
         * reference value.
         */
        NotEqual,
        /**
         * The test always passes
         */
        Always
    }

    /**
     * <code>BlendEquation</code> specifies the blending equation to combine
     * pixels.
     */
    public enum BlendEquation {
        /**
         * Sets the blend equation so that the source and destination data are
         * added. (Default) Clamps to [0,1] Useful for things like antialiasing
         * and transparency.
         */
        Add,
        /**
         * Sets the blend equation so that the source and destination data are
         * subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if
         * supportsSubtract is false.
         */
        Subtract,
        /**
         * Same as Subtract, but the order is reversed (Dst - Src). Clamps to
         * [0,1] Falls back to Add if supportsSubtract is false.
         */
        ReverseSubtract,
        /**
         * Sets the blend equation so that each component of the result color is
         * the minimum of the corresponding components of the source and
         * destination colors. This and Max are useful for applications that
         * analyze image data (image thresholding against a constant color, for
         * example). Falls back to Add if supportsMinMax is false.
         */
        Min,
        /**
         * Sets the blend equation so that each component of the result color is
         * the maximum of the corresponding components of the source and
         * destination colors. This and Min are useful for applications that
         * analyze image data (image thresholding against a constant color, for
         * example). Falls back to Add if supportsMinMax is false.
         */
        Max
    }
    
    /**
     * <code>BlendEquationAlpha</code> specifies the blending equation to
     * combine pixels for the alpha component.
     */
    public enum BlendEquationAlpha {
        /**
         * Sets the blend equation to be the same as the one defined by
         * {@link #blendEquation}.
         *
         */
        InheritColor,
        /**
         * Sets the blend equation so that the source and destination data are
         * added. (Default) Clamps to [0,1] Useful for things like antialiasing
         * and transparency.
         */
        Add,
        /**
         * Sets the blend equation so that the source and destination data are
         * subtracted (Src - Dest). Clamps to [0,1] Falls back to Add if
         * supportsSubtract is false.
         */
        Subtract,
        /**
         * Same as Subtract, but the order is reversed (Dst - Src). Clamps to
         * [0,1] Falls back to Add if supportsSubtract is false.
         */
        ReverseSubtract,
        /**
         * Sets the blend equation so that the result alpha is the minimum of
         * the source alpha and destination alpha. This and Max are useful for
         * applications that analyze image data (image thresholding against a
         * constant color, for example). Falls back to Add if supportsMinMax is
         * false.
         */
        Min,
        /**
         * sSets the blend equation so that the result alpha is the maximum of
         * the source alpha and destination alpha. This and Min are useful for
         * applications that analyze image data (image thresholding against a
         * constant color, for example). Falls back to Add if supportsMinMax is
         * false.
         */
        Max
    }

    /**
     * <code>BlendMode</code> specifies the blending operation to use.
     *
     * @see RenderState#setBlendMode(com.jme3.material.RenderState.BlendMode)
     */
    public enum BlendMode {

        /**
         * No blending mode is used.
         */
        Off,
        /**
         * Additive blending. For use with glows and particle emitters.
         * <p>
         * Result = Source Color + Destination Color -> (GL_ONE, GL_ONE)
         */
        Additive,
        /**
         * Premultiplied alpha blending, for use with premult alpha textures.
         * <p>
         * Result = Source Color + (Dest Color * (1 - Source Alpha) ) -> (GL_ONE, GL_ONE_MINUS_SRC_ALPHA)
         */
        PremultAlpha,
        /**
         * Additive blending that is multiplied with source alpha.
         * For use with glows and particle emitters.
         * <p>
         * Result = (Source Alpha * Source Color) + Dest Color -> (GL_SRC_ALPHA, GL_ONE)
         */
        AlphaAdditive,
        /**
         * Color blending, blends in color from dest color
         * using source color.
         * <p>
         * Result = Source Color + (1 - Source Color) * Dest Color -> (GL_ONE, GL_ONE_MINUS_SRC_COLOR)
         */
        Color,
        /**
         * Alpha blending, interpolates to source color from dest color
         * using source alpha.
         * <p>
         * Result = Source Alpha * Source Color +
         *          (1 - Source Alpha) * Dest Color -> (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
         */
        Alpha,
        /**
         * Multiplies the source and dest colors.
         * <p>
         * Result = Source Color * Dest Color -> (GL_DST_COLOR, GL_ZERO)
         */
        Modulate,
        /**
         * Multiplies the source and dest colors then doubles the result.
         * <p>
         * Result = 2 * Source Color * Dest Color -> (GL_DST_COLOR, GL_SRC_COLOR)
         */
        ModulateX2,
        /**
         * Opposite effect of Modulate/Multiply. Invert both colors, multiply and
         * then invert the result.
         * <p>
         * Result = 1 - (1 - Source Color) * (1 - Dest Color) -> (GL_ONE, GL_ONE_MINUS_SRC_COLOR)
         */
        Screen,
        /**
         * Mixes the destination and source colors similar to a color-based XOR
         * operation.  This is directly equivalent to Photoshop's "Exclusion" blend.
         * <p>
         * Result = (Source Color * (1 - Dest Color)) + (Dest Color * (1 - Source Color))
         *  -> (GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR) 
         */
        Exclusion
    }

    /**
     * <code>FaceCullMode</code> specifies the criteria for faces to be culled.
     *
     * @see RenderState#setFaceCullMode(com.jme3.material.RenderState.FaceCullMode)
     */
    public enum FaceCullMode {

        /**
         * Face culling is disabled.
         */
        Off,
        /**
         * Cull front faces
         */
        Front,
        /**
         * Cull back faces
         */
        Back,
        /**
         * Cull both front and back faces.
         */
        FrontAndBack
    }

    /**
     * <code>StencilOperation</code> specifies the stencil operation to use
     * in a certain scenario as specified in {@link RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction) }
     */
    public enum StencilOperation {

        /**
         * Keep the current value.
         */
        Keep,
        /**
         * Set the value to 0
         */
        Zero,
        /**
         * Replace the value in the stencil buffer with the reference value.
         */
        Replace,
        /**
         * Increment the value in the stencil buffer, clamp once reaching
         * the maximum value.
         */
        Increment,
        /**
         * Increment the value in the stencil buffer and wrap to 0 when
         * reaching the maximum value.
         */
        IncrementWrap,
        /**
         * Decrement the value in the stencil buffer and clamp once reaching 0.
         */
        Decrement,
        /**
         * Decrement the value in the stencil buffer and wrap to the maximum
         * value when reaching 0.
         */
        DecrementWrap,
        /**
         * Does a bitwise invert of the value in the stencil buffer.
         */
        Invert
    }

    static {
        NULL.cullMode = FaceCullMode.Off;
        NULL.depthTest = false;
    }

    static {
        ADDITIONAL.applyWireFrame = false;
        ADDITIONAL.applyCullMode = false;
        ADDITIONAL.applyDepthWrite = false;
        ADDITIONAL.applyDepthTest = false;
        ADDITIONAL.applyColorWrite = false;
        ADDITIONAL.applyBlendEquation = false;
        ADDITIONAL.applyBlendEquationAlpha = false;
        ADDITIONAL.applyBlendMode = false;
        ADDITIONAL.applyPolyOffset = false;
    }
    boolean wireframe = false;
    boolean applyWireFrame = true;
    FaceCullMode cullMode = FaceCullMode.Back;
    boolean applyCullMode = true;
    boolean depthWrite = true;
    boolean applyDepthWrite = true;
    boolean depthTest = true;
    boolean applyDepthTest = true;
    boolean colorWrite = true;
    boolean applyColorWrite = true;
    BlendEquation blendEquation = BlendEquation.Add;
    boolean applyBlendEquation = true;
    BlendEquationAlpha blendEquationAlpha = BlendEquationAlpha.InheritColor;
    boolean applyBlendEquationAlpha = true;
    BlendMode blendMode = BlendMode.Off;
    boolean applyBlendMode = true;
    float offsetFactor = 0;
    float offsetUnits = 0;
    boolean offsetEnabled = false;
    boolean applyPolyOffset = true;
    boolean stencilTest = false;
    boolean applyStencilTest = false;
    float lineWidth = 1;
    boolean applyLineWidth = false;
    TestFunction depthFunc = TestFunction.LessOrEqual;
    //by default depth func will be applied anyway if depth test is applied
    boolean applyDepthFunc = false;
    StencilOperation frontStencilStencilFailOperation = StencilOperation.Keep;
    StencilOperation frontStencilDepthFailOperation = StencilOperation.Keep;
    StencilOperation frontStencilDepthPassOperation = StencilOperation.Keep;
    StencilOperation backStencilStencilFailOperation = StencilOperation.Keep;
    StencilOperation backStencilDepthFailOperation = StencilOperation.Keep;
    StencilOperation backStencilDepthPassOperation = StencilOperation.Keep;
    TestFunction frontStencilFunction = TestFunction.Always;
    TestFunction backStencilFunction = TestFunction.Always;
    int cachedHashCode = -1;

    public void write(JmeExporter ex) throws IOException {
        OutputCapsule oc = ex.getCapsule(this);
        oc.write(true, "pointSprite", false);
        oc.write(wireframe, "wireframe", false);
        oc.write(cullMode, "cullMode", FaceCullMode.Back);
        oc.write(depthWrite, "depthWrite", true);
        oc.write(depthTest, "depthTest", true);
        oc.write(colorWrite, "colorWrite", true);
        oc.write(blendMode, "blendMode", BlendMode.Off);
        oc.write(offsetEnabled, "offsetEnabled", false);
        oc.write(offsetFactor, "offsetFactor", 0);
        oc.write(offsetUnits, "offsetUnits", 0);
        oc.write(stencilTest, "stencilTest", false);
        oc.write(frontStencilStencilFailOperation, "frontStencilStencilFailOperation", StencilOperation.Keep);
        oc.write(frontStencilDepthFailOperation, "frontStencilDepthFailOperation", StencilOperation.Keep);
        oc.write(frontStencilDepthPassOperation, "frontStencilDepthPassOperation", StencilOperation.Keep);
        oc.write(backStencilStencilFailOperation, "frontStencilStencilFailOperation", StencilOperation.Keep);
        oc.write(backStencilDepthFailOperation, "backStencilDepthFailOperation", StencilOperation.Keep);
        oc.write(backStencilDepthPassOperation, "backStencilDepthPassOperation", StencilOperation.Keep);
        oc.write(frontStencilFunction, "frontStencilFunction", TestFunction.Always);
        oc.write(backStencilFunction, "backStencilFunction", TestFunction.Always);
        oc.write(blendEquation, "blendEquation", BlendEquation.Add);
        oc.write(blendEquationAlpha, "blendEquationAlpha", BlendEquationAlpha.InheritColor);
        oc.write(depthFunc, "depthFunc", TestFunction.LessOrEqual);
        oc.write(lineWidth, "lineWidth", 1);

        // Only "additional render state" has them set to false by default
        oc.write(applyWireFrame, "applyWireFrame", true);
        oc.write(applyCullMode, "applyCullMode", true);
        oc.write(applyDepthWrite, "applyDepthWrite", true);
        oc.write(applyDepthTest, "applyDepthTest", true);
        oc.write(applyColorWrite, "applyColorWrite", true);
        oc.write(applyBlendEquation, "applyBlendEquation", true);
        oc.write(applyBlendEquationAlpha, "applyBlendEquationAlpha", true);
        oc.write(applyBlendMode, "applyBlendMode", true);
        oc.write(applyPolyOffset, "applyPolyOffset", true);
        oc.write(applyDepthFunc, "applyDepthFunc", true);
        oc.write(applyLineWidth, "applyLineWidth", true);

    }

    public void read(JmeImporter im) throws IOException {
        InputCapsule ic = im.getCapsule(this);
        wireframe = ic.readBoolean("wireframe", false);
        cullMode = ic.readEnum("cullMode", FaceCullMode.class, FaceCullMode.Back);
        depthWrite = ic.readBoolean("depthWrite", true);
        depthTest = ic.readBoolean("depthTest", true);
        colorWrite = ic.readBoolean("colorWrite", true);
        blendMode = ic.readEnum("blendMode", BlendMode.class, BlendMode.Off);
        offsetEnabled = ic.readBoolean("offsetEnabled", false);
        offsetFactor = ic.readFloat("offsetFactor", 0);
        offsetUnits = ic.readFloat("offsetUnits", 0);
        stencilTest = ic.readBoolean("stencilTest", false);
        frontStencilStencilFailOperation = ic.readEnum("frontStencilStencilFailOperation", StencilOperation.class, StencilOperation.Keep);
        frontStencilDepthFailOperation = ic.readEnum("frontStencilDepthFailOperation", StencilOperation.class, StencilOperation.Keep);
        frontStencilDepthPassOperation = ic.readEnum("frontStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep);
        backStencilStencilFailOperation = ic.readEnum("backStencilStencilFailOperation", StencilOperation.class, StencilOperation.Keep);
        backStencilDepthFailOperation = ic.readEnum("backStencilDepthFailOperation", StencilOperation.class, StencilOperation.Keep);
        backStencilDepthPassOperation = ic.readEnum("backStencilDepthPassOperation", StencilOperation.class, StencilOperation.Keep);
        frontStencilFunction = ic.readEnum("frontStencilFunction", TestFunction.class, TestFunction.Always);
        backStencilFunction = ic.readEnum("backStencilFunction", TestFunction.class, TestFunction.Always);
        blendEquation = ic.readEnum("blendEquation", BlendEquation.class, BlendEquation.Add);
        blendEquationAlpha = ic.readEnum("blendEquationAlpha", BlendEquationAlpha.class, BlendEquationAlpha.InheritColor);
        depthFunc = ic.readEnum("depthFunc", TestFunction.class, TestFunction.LessOrEqual);
        lineWidth = ic.readFloat("lineWidth", 1);


        applyWireFrame = ic.readBoolean("applyWireFrame", true);
        applyCullMode = ic.readBoolean("applyCullMode", true);
        applyDepthWrite = ic.readBoolean("applyDepthWrite", true);
        applyDepthTest = ic.readBoolean("applyDepthTest", true);
        applyColorWrite = ic.readBoolean("applyColorWrite", true);
        applyBlendEquation = ic.readBoolean("applyBlendEquation", true);
        applyBlendEquationAlpha = ic.readBoolean("applyBlendEquationAlpha", true);
        applyBlendMode = ic.readBoolean("applyBlendMode", true);
        applyPolyOffset = ic.readBoolean("applyPolyOffset", true);
        applyDepthFunc = ic.readBoolean("applyDepthFunc", true);
        applyLineWidth = ic.readBoolean("applyLineWidth", true);

        
    }

    /**
     * Create a clone of this <code>RenderState</code>
     *
     * @return Clone of this render state.
     */
    @Override
    public RenderState clone() {
        try {
            return (RenderState) super.clone();
        } catch (CloneNotSupportedException ex) {
            throw new AssertionError();
        }
    }

    /**
     * returns true if the given renderState is equal to this one
     * @param o the renderState to compare to
     * @return true if the renderStates are equal
     */
    @Override
    public boolean equals(Object o) {
        if (o == null) {
            return false;
        }
        if (!(o instanceof RenderState)) {
            return false;
        }
        RenderState rs = (RenderState) o;

        if (wireframe != rs.wireframe) {
            return false;
        }

        if (cullMode != rs.cullMode) {
            return false;
        }

        if (depthWrite != rs.depthWrite) {
            return false;
        }

        if (depthTest != rs.depthTest) {
            return false;
        }
        if (depthTest) {
            if (depthFunc != rs.depthFunc) {
                return false;
            }
        }

        if (colorWrite != rs.colorWrite) {
            return false;
        }

        if (blendEquation != rs.blendEquation) {
            return false;
        }

        if (blendEquationAlpha != rs.blendEquationAlpha) {
            return false;
        }

        if (blendMode != rs.blendMode) {
            return false;
        }


        if (offsetEnabled != rs.offsetEnabled) {
            return false;
        }

        if (offsetFactor != rs.offsetFactor) {
            return false;
        }

        if (offsetUnits != rs.offsetUnits) {
            return false;
        }

        if (stencilTest != rs.stencilTest) {
            return false;
        }

        if (stencilTest) {
            if (frontStencilStencilFailOperation != rs.frontStencilStencilFailOperation) {
                return false;
            }
            if (frontStencilDepthFailOperation != rs.frontStencilDepthFailOperation) {
                return false;
            }
            if (frontStencilDepthPassOperation != rs.frontStencilDepthPassOperation) {
                return false;
            }
            if (backStencilStencilFailOperation != rs.backStencilStencilFailOperation) {
                return false;
            }
            if (backStencilDepthFailOperation != rs.backStencilDepthFailOperation) {
                return false;
            }

            if (backStencilDepthPassOperation != rs.backStencilDepthPassOperation) {
                return false;
            }
            if (frontStencilFunction != rs.frontStencilFunction) {
                return false;
            }
            if (backStencilFunction != rs.backStencilFunction) {
                return false;
            }
        }

        if(lineWidth != rs.lineWidth){
            return false;
        }

        return true;
    }

    /**
     * @deprecated Does nothing. Point sprite is already enabled by default for
     * all supported platforms. jME3 does not support rendering conventional
     * point clouds.
     */
    @Deprecated
    public void setPointSprite(boolean pointSprite) {
    }

    /**
     * @deprecated Does nothing. To use alpha test, set the
     * <code>AlphaDiscardThreshold</code> material parameter.
     * @param alphaFallOff does nothing
     */
    @Deprecated
    public void setAlphaFallOff(float alphaFallOff) {
    }

    /**
     * @deprecated Does nothing. To use alpha test, set the
     * <code>AlphaDiscardThreshold</code> material parameter.
     * @param alphaTest does nothing
     */
    @Deprecated
    public void setAlphaTest(boolean alphaTest) {
    }

    /**
     * Enable writing color.
     *
     * <p>When color write is enabled, the result of a fragment shader, the
     * <code>gl_FragColor</code>, will be rendered into the color buffer
     * (including alpha).
     *
     * @param colorWrite Set to true to enable color writing.
     */
    public void setColorWrite(boolean colorWrite) {
        applyColorWrite = true;
        this.colorWrite = colorWrite;
        cachedHashCode = -1;
    }

    /**
     * Set the face culling mode.
     *
     * <p>See the {@link FaceCullMode} enum on what each value does.
     * Face culling will project the triangle's points onto the screen
     * and determine if the triangle is in counter-clockwise order or
     * clockwise order. If a triangle is in counter-clockwise order, then
     * it is considered a front-facing triangle, otherwise, it is considered
     * a back-facing triangle.
     *
     * @param cullMode the face culling mode.
     */
    public void setFaceCullMode(FaceCullMode cullMode) {
        applyCullMode = true;
        this.cullMode = cullMode;
        cachedHashCode = -1;
    }

    /**
     * Set the blending mode.
     *
     * <p>When blending is enabled, (<code>blendMode</code> is not {@link BlendMode#Off})
     * the input pixel will be blended with the pixel
     * already in the color buffer. The blending operation is determined
     * by the {@link BlendMode}. For example, the {@link BlendMode#Additive}
     * will add the input pixel's color to the color already in the color buffer:
     * <br/>
     * <code>Result = Source Color + Destination Color</code>
     *
     * @param blendMode The blend mode to use. Set to {@link BlendMode#Off}
     * to disable blending.
     */
    public void setBlendMode(BlendMode blendMode) {
        applyBlendMode = true;
        this.blendMode = blendMode;
        cachedHashCode = -1;
    }

    /**
     * Set the blending equation.
     * <p>
     * When blending is enabled, (<code>blendMode</code> is not
     * {@link BlendMode#Off}) the input pixel will be blended with the pixel
     * already in the color buffer. The blending equation is determined by the
     * {@link BlendEquation}. For example, the mode {@link BlendMode#Additive}
     * and {@link BlendEquation#Add} will add the input pixel's color to the
     * color already in the color buffer:
     * <br/>
     * <code>Result = Source Color + Destination Color</code>
     * <br/>
     * However, the mode {@link BlendMode#Additive}
     * and {@link BlendEquation#Subtract} will subtract the input pixel's color to the
     * color already in the color buffer:
     * <br/>
     * <code>Result = Source Color - Destination Color</code>
     *
     * @param blendEquation The blend equation to use. 
     */
    public void setBlendEquation(BlendEquation blendEquation) {
        applyBlendEquation = true;
        this.blendEquation = blendEquation;
        cachedHashCode = -1;
    }
    
    /**
     * Set the blending equation for the alpha component.
     * <p>
     * When blending is enabled, (<code>blendMode</code> is not
     * {@link BlendMode#Off}) the input pixel will be blended with the pixel
     * already in the color buffer. The blending equation is determined by the
     * {@link BlendEquation} and can be overrode for the alpha component using
     * the {@link BlendEquationAlpha} . For example, the mode
     * {@link BlendMode#Additive} and {@link BlendEquationAlpha#Add} will add
     * the input pixel's alpha to the alpha component already in the color
     * buffer:
     * <br/>
     * <code>Result = Source Alpha + Destination Alpha</code>
     * <br/>
     * However, the mode {@link BlendMode#Additive} and
     * {@link BlendEquationAlpha#Subtract} will subtract the input pixel's alpha
     * to the alpha component already in the color buffer:
     * <br/>
     * <code>Result = Source Alpha - Destination Alpha</code>
     *
     * @param blendEquationAlpha The blend equation to use for the alpha
     *                           component.
     */
    public void setBlendEquationAlpha(BlendEquationAlpha blendEquationAlpha) {
        applyBlendEquationAlpha = true;
        this.blendEquationAlpha = blendEquationAlpha;
        cachedHashCode = -1;
    }

    /**
     * Enable depth testing.
     *
     * <p>When depth testing is enabled, a pixel must pass the depth test
     * before it is written to the color buffer.
     * The input pixel's depth value must be less than or equal than
     * the value already in the depth buffer to pass the depth test.
     *
     * @param depthTest Enable or disable depth testing.
     */
    public void setDepthTest(boolean depthTest) {
        applyDepthTest = true;
        this.depthTest = depthTest;
        cachedHashCode = -1;
    }

    /**
     * Enable depth writing.
     *
     * <p>After passing the {@link RenderState#setDepthTest(boolean) depth test},
     * a pixel's depth value will be written into the depth buffer if
     * depth writing is enabled.
     *
     * @param depthWrite True to enable writing to the depth buffer.
     */
    public void setDepthWrite(boolean depthWrite) {
        applyDepthWrite = true;
        this.depthWrite = depthWrite;
        cachedHashCode = -1;
    }

    /**
     * Enables wireframe rendering mode.
     *
     * <p>When in wireframe mode, {@link Mesh meshes} rendered in triangle mode
     * will not be solid, but instead, only the edges of the triangles
     * will be rendered.
     *
     * @param wireframe True to enable wireframe mode.
     */
    public void setWireframe(boolean wireframe) {
        applyWireFrame = true;
        this.wireframe = wireframe;
        cachedHashCode = -1;
    }

    /**
     * Offsets the on-screen z-order of the material's polygons, to combat visual artefacts like
     * stitching, bleeding and z-fighting for overlapping polygons.
     * Factor and units are summed to produce the depth offset.
     * This offset is applied in screen space,
     * typically with positive Z pointing into the screen.
     * Typical values are (1.0f, 1.0f) or (-1.0f, -1.0f)
     *
     * @see <a href="http://www.opengl.org/resources/faq/technical/polygonoffset.htm" rel="nofollow">http://www.opengl.org/resources/faq/technical/polygonoffset.htm</a>
     * @param factor scales the maximum Z slope, with respect to X or Y of the polygon
     * @param units scales the minimum resolvable depth buffer value
     **/
    public void setPolyOffset(float factor, float units) {
        applyPolyOffset = true;
        if (factor == 0 && units == 0) {
            offsetEnabled = false;
        } else {
            offsetEnabled = true;
            offsetFactor = factor;
            offsetUnits = units;
        }
        cachedHashCode = -1;
    }    

    /**
     * Enable stencil testing.
     *
     * <p>Stencil testing can be used to filter pixels according to the stencil
     * buffer. Objects can be rendered with some stencil operation to manipulate
     * the values in the stencil buffer, then, other objects can be rendered
     * to test against the values written previously.
     *
     * @param enabled Set to true to enable stencil functionality. If false
     * all other parameters are ignored.
     *
     * @param _frontStencilStencilFailOperation Sets the operation to occur when
     * a front-facing triangle fails the front stencil function.
     * @param _frontStencilDepthFailOperation Sets the operation to occur when
     * a front-facing triangle fails the depth test.
     * @param _frontStencilDepthPassOperation Set the operation to occur when
     * a front-facing triangle passes the depth test.
     * @param _backStencilStencilFailOperation Set the operation to occur when
     * a back-facing triangle fails the back stencil function.
     * @param _backStencilDepthFailOperation Set the operation to occur when
     * a back-facing triangle fails the depth test.
     * @param _backStencilDepthPassOperation Set the operation to occur when
     * a back-facing triangle passes the depth test.
     * @param _frontStencilFunction Set the test function for front-facing triangles.
     * @param _backStencilFunction Set the test function for back-facing triangles.
     */
    public void setStencil(boolean enabled,
            StencilOperation _frontStencilStencilFailOperation,
            StencilOperation _frontStencilDepthFailOperation,
            StencilOperation _frontStencilDepthPassOperation,
            StencilOperation _backStencilStencilFailOperation,
            StencilOperation _backStencilDepthFailOperation,
            StencilOperation _backStencilDepthPassOperation,
            TestFunction _frontStencilFunction,
            TestFunction _backStencilFunction) {

        stencilTest = enabled;
        applyStencilTest = true;
        this.frontStencilStencilFailOperation = _frontStencilStencilFailOperation;
        this.frontStencilDepthFailOperation = _frontStencilDepthFailOperation;
        this.frontStencilDepthPassOperation = _frontStencilDepthPassOperation;
        this.backStencilStencilFailOperation = _backStencilStencilFailOperation;
        this.backStencilDepthFailOperation = _backStencilDepthFailOperation;
        this.backStencilDepthPassOperation = _backStencilDepthPassOperation;
        this.frontStencilFunction = _frontStencilFunction;
        this.backStencilFunction = _backStencilFunction;
        cachedHashCode = -1;
    }

    /**
     * Set the depth conparison function to the given TestFunction 
     * default is LessOrEqual (GL_LEQUAL)
     * @see TestFunction
     * @see RenderState#setDepthTest(boolean) 
     * @param depthFunc the depth comparison function
     */
    public void setDepthFunc(TestFunction depthFunc) {       
        applyDepthFunc = true;
        this.depthFunc = depthFunc;
        cachedHashCode = -1;
    }

    /**
     * @deprecated
     */
    @Deprecated
    public void setAlphaFunc(TestFunction alphaFunc) {
    }

    /**
     * Sets the mesh line width.
     * This is to use in conjunction with {@link #setWireframe(boolean)} or with a mesh in {@link Mesh.Mode#Lines} mode.
     * @param lineWidth the line width.
     */
    public void setLineWidth(float lineWidth) {
        if (lineWidth < 1f) {
            throw new IllegalArgumentException("lineWidth must be greater than or equal to 1.0");
        }
        this.lineWidth = lineWidth;
        this.applyLineWidth = true;
        cachedHashCode = -1;
    }

    /**
     * Check if stencil test is enabled.
     *
     * @return True if stencil test is enabled.
     */
    public boolean isStencilTest() {
        return stencilTest;
    }

    /**
     * Retrieve the front stencil fail operation.
     *
     * @return the front stencil fail operation.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public StencilOperation getFrontStencilStencilFailOperation() {
        return frontStencilStencilFailOperation;
    }

    /**
     * Retrieve the front depth test fail operation.
     *
     * @return the front depth test fail operation.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public StencilOperation getFrontStencilDepthFailOperation() {
        return frontStencilDepthFailOperation;
    }

    /**
     * Retrieve the front depth test pass operation.
     *
     * @return the front depth test pass operation.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public StencilOperation getFrontStencilDepthPassOperation() {
        return frontStencilDepthPassOperation;
    }

    /**
     * Retrieve the back stencil fail operation.
     *
     * @return the back stencil fail operation.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public StencilOperation getBackStencilStencilFailOperation() {
        return backStencilStencilFailOperation;
    }

    /**
     * Retrieve the back depth test fail operation.
     *
     * @return the back depth test fail operation.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public StencilOperation getBackStencilDepthFailOperation() {
        return backStencilDepthFailOperation;
    }

    /**
     * Retrieve the back depth test pass operation.
     *
     * @return the back depth test pass operation.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public StencilOperation getBackStencilDepthPassOperation() {
        return backStencilDepthPassOperation;
    }

    /**
     * Retrieve the front stencil function.
     *
     * @return the front stencil function.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public TestFunction getFrontStencilFunction() {
        return frontStencilFunction;
    }

    /**
     * Retrieve the back stencil function.
     *
     * @return the back stencil function.
     *
     * @see RenderState#setStencil(boolean,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.StencilOperation,
     * com.jme3.material.RenderState.TestFunction,
     * com.jme3.material.RenderState.TestFunction)
     */
    public TestFunction getBackStencilFunction() {
        return backStencilFunction;
    }

    /**
     * Retrieve the blend equation.
     *
     * @return the blend equation.
     */
    public BlendEquation getBlendEquation() {
        return blendEquation;
    }
    
    /**
     * Retrieve the blend equation used for the alpha component.
     *
     * @return the blend equation for the alpha component.
     */
    public BlendEquationAlpha getBlendEquationAlpha() {
        return blendEquationAlpha;
    }

    /**
     * Retrieve the blend mode.
     *
     * @return the blend mode.
     */
    public BlendMode getBlendMode() {
        return blendMode;
    }

    /**
     * @return true
     * @deprecated Always returns true since point sprite is always enabled.
     * @see #setPointSprite(boolean)
     */
    @Deprecated
    public boolean isPointSprite() {
        return true;
    }

    /**
     * @deprecated To use alpha test, set the <code>AlphaDiscardThreshold</code>
     * material parameter.
     * @return false
     */
    public boolean isAlphaTest() {
        return false;
    }

    /**
     * Retrieve the face cull mode.
     *
     * @return the face cull mode.
     *
     * @see RenderState#setFaceCullMode(com.jme3.material.RenderState.FaceCullMode)
     */
    public FaceCullMode getFaceCullMode() {
        return cullMode;
    }

    /**
     * Check if depth test is enabled.
     *
     * @return True if depth test is enabled.
     *
     * @see RenderState#setDepthTest(boolean)
     */
    public boolean isDepthTest() {
        return depthTest;
    }

    /**
     * Check if depth write is enabled.
     *
     * @return True if depth write is enabled.
     *
     * @see RenderState#setDepthWrite(boolean)
     */
    public boolean isDepthWrite() {
        return depthWrite;
    }

    /**
     * Check if wireframe mode is enabled.
     *
     * @return True if wireframe mode is enabled.
     *
     * @see RenderState#setWireframe(boolean)
     */
    public boolean isWireframe() {
        return wireframe;
    }

    /**
     * Check if color writing is enabled.
     *
     * @return True if color writing is enabled.
     *
     * @see RenderState#setColorWrite(boolean)
     */
    public boolean isColorWrite() {
        return colorWrite;
    }

    /**
     * Retrieve the poly offset factor value.
     *
     * @return the poly offset factor value.
     *
     * @see RenderState#setPolyOffset(float, float)
     */
    public float getPolyOffsetFactor() {
        return offsetFactor;
    }

    /**
     * Retrieve the poly offset units value.
     *
     * @return the poly offset units value.
     *
     * @see RenderState#setPolyOffset(float, float)
     */
    public float getPolyOffsetUnits() {
        return offsetUnits;
    }

    /**
     * Check if polygon offset is enabled.
     *
     * @return True if polygon offset is enabled.
     *
     * @see RenderState#setPolyOffset(float, float)
     */
    public boolean isPolyOffset() {
        return offsetEnabled;
    }

    /**
     * @return 0
     * @deprecated
     */
    @Deprecated
    public float getAlphaFallOff() {
        return 0f;
    }

    /**
     * Retrieve the depth comparison function
     *
     * @return the depth comparison function
     *
     * @see RenderState#setDepthFunc(com.jme3.material.RenderState.TestFunction)
     */
    public TestFunction getDepthFunc() {
        return depthFunc;
    }

    /**
     * @return {@link TestFunction#Greater}.
     * @deprecated
     */
    @Deprecated
    public TestFunction getAlphaFunc() {
        return TestFunction.Greater;
    }

    /**
     * returns the wireframe line width
     *
     * @return the line width
     */
    public float getLineWidth() {
        return lineWidth;
    }



    public boolean isApplyBlendMode() {
        return applyBlendMode;
    }

    public boolean isApplyBlendEquation() {
        return applyBlendEquation;
    }

    public boolean isApplyBlendEquationAlpha() {
        return applyBlendEquationAlpha;
    }

    public boolean isApplyColorWrite() {
        return applyColorWrite;
    }

    public boolean isApplyCullMode() {
        return applyCullMode;
    }

    public boolean isApplyDepthTest() {
        return applyDepthTest;
    }

    public boolean isApplyDepthWrite() {
        return applyDepthWrite;
    }


    public boolean isApplyPolyOffset() {
        return applyPolyOffset;
    }

    public boolean isApplyWireFrame() {
        return applyWireFrame;
    }

    public boolean isApplyDepthFunc() {
        return applyDepthFunc;
    }


    public boolean isApplyLineWidth() {
        return applyLineWidth;
    }

    /**
     *
     */
    public int contentHashCode() {
        if (cachedHashCode == -1){
            int hash = 7;
            hash = 79 * hash + (this.wireframe ? 1 : 0);
            hash = 79 * hash + (this.cullMode != null ? this.cullMode.hashCode() : 0);
            hash = 79 * hash + (this.depthWrite ? 1 : 0);
            hash = 79 * hash + (this.depthTest ? 1 : 0);
            hash = 79 * hash + (this.depthFunc != null ? this.depthFunc.hashCode() : 0);
            hash = 79 * hash + (this.colorWrite ? 1 : 0);
            hash = 79 * hash + (this.blendMode != null ? this.blendMode.hashCode() : 0);
            hash = 79 * hash + (this.blendEquation != null ? this.blendEquation.hashCode() : 0);
            hash = 79 * hash + (this.blendEquationAlpha != null ? this.blendEquationAlpha.hashCode() : 0);
            hash = 79 * hash + Float.floatToIntBits(this.offsetFactor);
            hash = 79 * hash + Float.floatToIntBits(this.offsetUnits);
            hash = 79 * hash + (this.offsetEnabled ? 1 : 0);
            hash = 79 * hash + (this.stencilTest ? 1 : 0);
            hash = 79 * hash + (this.frontStencilStencilFailOperation != null ? this.frontStencilStencilFailOperation.hashCode() : 0);
            hash = 79 * hash + (this.frontStencilDepthFailOperation != null ? this.frontStencilDepthFailOperation.hashCode() : 0);
            hash = 79 * hash + (this.frontStencilDepthPassOperation != null ? this.frontStencilDepthPassOperation.hashCode() : 0);
            hash = 79 * hash + (this.backStencilStencilFailOperation != null ? this.backStencilStencilFailOperation.hashCode() : 0);
            hash = 79 * hash + (this.backStencilDepthFailOperation != null ? this.backStencilDepthFailOperation.hashCode() : 0);
            hash = 79 * hash + (this.backStencilDepthPassOperation != null ? this.backStencilDepthPassOperation.hashCode() : 0);
            hash = 79 * hash + (this.frontStencilFunction != null ? this.frontStencilFunction.hashCode() : 0);
            hash = 79 * hash + (this.backStencilFunction != null ? this.backStencilFunction.hashCode() : 0);
            hash = 79 * hash + Float.floatToIntBits(this.lineWidth);
            cachedHashCode = hash;
        }
        return cachedHashCode;
    }

    /**
     * Merges <code>this</code> state and <code>additionalState</code> into
     * the parameter <code>state</code> based on a specific criteria.
     *
     * <p>The criteria for this merge is the following:<br/>
     * For every given property, such as alpha test or depth write, check
     * if it was modified from the original in the <code>additionalState</code>
     * if it was modified, then copy the property from the <code>additionalState</code>
     * into the parameter <code>state</code>, otherwise, copy the property from <code>this</code>
     * into the parameter <code>state</code>. If <code>additionalState</code>
     * is <code>null</code>, then no modifications are made and <code>this</code> is returned,
     * otherwise, the parameter <code>state</code> is returned with the result
     * of the merge.
     *
     * @param additionalState The <code>additionalState</code>, from which data is taken only
     * if it was modified by the user.
     * @param state Contains output of the method if <code>additionalState</code>
     * is not null.
     * @return <code>state</code> if <code>additionalState</code> is non-null,
     * otherwise returns <code>this</code>
     */
    public RenderState copyMergedTo(RenderState additionalState, RenderState state) {
        if (additionalState == null) {
            return this;
        }

        if (additionalState.applyWireFrame) {
            state.wireframe = additionalState.wireframe;
        } else {
            state.wireframe = wireframe;
        }

        if (additionalState.applyCullMode) {
            state.cullMode = additionalState.cullMode;
        } else {
            state.cullMode = cullMode;
        }
        if (additionalState.applyDepthWrite) {
            state.depthWrite = additionalState.depthWrite;
        } else {
            state.depthWrite = depthWrite;
        }
        if (additionalState.applyDepthTest) {
            state.depthTest = additionalState.depthTest;
        } else {
            state.depthTest = depthTest;
        }
        if (additionalState.applyDepthFunc) {
            state.depthFunc = additionalState.depthFunc;
        } else {
            state.depthFunc = depthFunc;
        }
        if (additionalState.applyColorWrite) {
            state.colorWrite = additionalState.colorWrite;
        } else {
            state.colorWrite = colorWrite;
        }
        if (additionalState.applyBlendEquation) {
            state.blendEquation = additionalState.blendEquation;
        } else {
            state.blendEquation = blendEquation;
        }
        if (additionalState.applyBlendEquationAlpha) {
            state.blendEquationAlpha = additionalState.blendEquationAlpha;
        } else {
            state.blendEquationAlpha = blendEquationAlpha;
        }        
        if (additionalState.applyBlendMode) {
            state.blendMode = additionalState.blendMode;
        } else {
            state.blendMode = blendMode;
        }

        if (additionalState.applyPolyOffset) {
            state.offsetEnabled = additionalState.offsetEnabled;
            state.offsetFactor = additionalState.offsetFactor;
            state.offsetUnits = additionalState.offsetUnits;
        } else {
            state.offsetEnabled = offsetEnabled;
            state.offsetFactor = offsetFactor;
            state.offsetUnits = offsetUnits;
        }
        if (additionalState.applyStencilTest) {
            state.stencilTest = additionalState.stencilTest;

            state.frontStencilStencilFailOperation = additionalState.frontStencilStencilFailOperation;
            state.frontStencilDepthFailOperation = additionalState.frontStencilDepthFailOperation;
            state.frontStencilDepthPassOperation = additionalState.frontStencilDepthPassOperation;

            state.backStencilStencilFailOperation = additionalState.backStencilStencilFailOperation;
            state.backStencilDepthFailOperation = additionalState.backStencilDepthFailOperation;
            state.backStencilDepthPassOperation = additionalState.backStencilDepthPassOperation;

            state.frontStencilFunction = additionalState.frontStencilFunction;
            state.backStencilFunction = additionalState.backStencilFunction;
        } else {
            state.stencilTest = stencilTest;

            state.frontStencilStencilFailOperation = frontStencilStencilFailOperation;
            state.frontStencilDepthFailOperation = frontStencilDepthFailOperation;
            state.frontStencilDepthPassOperation = frontStencilDepthPassOperation;

            state.backStencilStencilFailOperation = backStencilStencilFailOperation;
            state.backStencilDepthFailOperation = backStencilDepthFailOperation;
            state.backStencilDepthPassOperation = backStencilDepthPassOperation;

            state.frontStencilFunction = frontStencilFunction;
            state.backStencilFunction = backStencilFunction;
        }
        if (additionalState.applyLineWidth) {
            state.lineWidth = additionalState.lineWidth;
        } else {
            state.lineWidth = lineWidth;
        }
        state.cachedHashCode = -1;
        return state;
    }

    public void set(RenderState state) {
        wireframe = state.wireframe;
        cullMode = state.cullMode;
        depthWrite = state.depthWrite;
        depthTest = state.depthTest;
        colorWrite = state.colorWrite;
        blendMode = state.blendMode;
        offsetEnabled = state.offsetEnabled;
        offsetFactor = state.offsetFactor;
        offsetUnits = state.offsetUnits;
        stencilTest = state.stencilTest;
        frontStencilStencilFailOperation = state.frontStencilStencilFailOperation;
        frontStencilDepthFailOperation = state.frontStencilDepthFailOperation;
        frontStencilDepthPassOperation = state.frontStencilDepthPassOperation;
        backStencilStencilFailOperation = state.backStencilStencilFailOperation;
        backStencilDepthFailOperation = state.backStencilDepthFailOperation;
        backStencilDepthPassOperation = state.backStencilDepthPassOperation;
        frontStencilFunction = state.frontStencilFunction;
        backStencilFunction = state.backStencilFunction;
        blendEquationAlpha = state.blendEquationAlpha;
        blendEquation = state.blendEquation;
        depthFunc = state.depthFunc;
        lineWidth = state.lineWidth;

        applyWireFrame =  true;
        applyCullMode =  true;
        applyDepthWrite =  true;
        applyDepthTest =  true;
        applyColorWrite = true;
        applyBlendEquation =  true;
        applyBlendEquationAlpha =  true;
        applyBlendMode = true;
        applyPolyOffset =  true;
        applyDepthFunc = true;
        applyLineWidth = true;
    }

    @Override
    public String toString() {
        return "RenderState[\n"
                + "\nwireframe=" + wireframe
                + "\napplyWireFrame=" + applyWireFrame
                + "\ncullMode=" + cullMode
                + "\napplyCullMode=" + applyCullMode
                + "\ndepthWrite=" + depthWrite
                + "\napplyDepthWrite=" + applyDepthWrite
                + "\ndepthTest=" + depthTest
                + "\ndepthFunc=" + depthFunc
                + "\napplyDepthTest=" + applyDepthTest
                + "\ncolorWrite=" + colorWrite
                + "\napplyColorWrite=" + applyColorWrite
                + "\nblendEquation=" + blendEquation
                + "\napplyBlendEquation=" + applyBlendEquation
                + "\napplyBlendEquationAlpha=" + applyBlendEquationAlpha
                + "\nblendMode=" + blendMode
                + "\napplyBlendMode=" + applyBlendMode
                + "\noffsetEnabled=" + offsetEnabled
                + "\napplyPolyOffset=" + applyPolyOffset
                + "\noffsetFactor=" + offsetFactor
                + "\noffsetUnits=" + offsetUnits
                + "\nlineWidth=" + lineWidth
                + "\n]";
    }
}
