aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/material
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/core/com/jme3/material')
-rw-r--r--engine/src/core/com/jme3/material/FixedFuncBinding.java80
-rw-r--r--engine/src/core/com/jme3/material/MatParam.java353
-rw-r--r--engine/src/core/com/jme3/material/MatParamTexture.java67
-rw-r--r--engine/src/core/com/jme3/material/Material.java1152
-rw-r--r--engine/src/core/com/jme3/material/MaterialDef.java190
-rw-r--r--engine/src/core/com/jme3/material/MaterialList.java44
-rw-r--r--engine/src/core/com/jme3/material/RenderState.java1070
-rw-r--r--engine/src/core/com/jme3/material/Technique.java261
-rw-r--r--engine/src/core/com/jme3/material/TechniqueDef.java396
-rw-r--r--engine/src/core/com/jme3/material/package.html58
10 files changed, 3671 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/material/FixedFuncBinding.java b/engine/src/core/com/jme3/material/FixedFuncBinding.java
new file mode 100644
index 0000000..e316ad8
--- /dev/null
+++ b/engine/src/core/com/jme3/material/FixedFuncBinding.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2009-2010 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;
+
+/**
+ * Fixed function binding is used to specify a binding for a {@link MatParam}
+ * in case that shaders are not supported on the system.
+ *
+ * @author Kirill Vainer
+ */
+public enum FixedFuncBinding {
+ /**
+ * Specifies the material ambient color.
+ * Same as GL_AMBIENT for OpenGL.
+ */
+ MaterialAmbient,
+
+ /**
+ * Specifies the material diffuse color.
+ * Same as GL_DIFFUSE for OpenGL.
+ */
+ MaterialDiffuse,
+
+ /**
+ * Specifies the material specular color.
+ * Same as GL_SPECULAR for OpenGL
+ */
+ MaterialSpecular,
+
+ /**
+ * Specifies the color of the object.
+ * <p>
+ * Used only for non-lit materials.
+ */
+ Color,
+
+ /**
+ * Specifies the material shininess value.
+ *
+ * Same as GL_SHININESS for OpenGL.
+ */
+ MaterialShininess,
+
+ /**
+ * Use vertex color as an additional diffuse color, if lighting is enabled.
+ * If lighting is disabled, vertex color is modulated with
+ * {@link #Color material color}.
+ */
+ UseVertexColor
+}
diff --git a/engine/src/core/com/jme3/material/MatParam.java b/engine/src/core/com/jme3/material/MatParam.java
new file mode 100644
index 0000000..b0ef117
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MatParam.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2009-2010 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.asset.TextureKey;
+import com.jme3.export.*;
+import com.jme3.math.*;
+import com.jme3.renderer.GL1Renderer;
+import com.jme3.renderer.Renderer;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture.WrapMode;
+import java.io.IOException;
+
+/**
+ * Describes a material parameter. This is used for both defining a name and type
+ * as well as a material parameter value.
+ *
+ * @author Kirill Vainer
+ */
+public class MatParam implements Savable, Cloneable {
+
+ protected VarType type;
+ protected String name;
+ protected String prefixedName;
+ protected Object value;
+ protected FixedFuncBinding ffBinding;
+
+ /**
+ * Create a new material parameter. For internal use only.
+ */
+ public MatParam(VarType type, String name, Object value, FixedFuncBinding ffBinding) {
+ this.type = type;
+ this.name = name;
+ this.prefixedName = "m_" + name;
+ this.value = value;
+ this.ffBinding = ffBinding;
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public MatParam() {
+ }
+
+ /**
+ * Returns the fixed function binding.
+ *
+ * @return the fixed function binding.
+ */
+ public FixedFuncBinding getFixedFuncBinding() {
+ return ffBinding;
+ }
+
+ /**
+ * Returns the material parameter type.
+ *
+ * @return the material parameter type.
+ */
+ public VarType getVarType() {
+ return type;
+ }
+
+ /**
+ * Returns the name of the material parameter.
+ * @return the name of the material parameter.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the name with "m_" prefixed to it.
+ *
+ * @return the name with "m_" prefixed to it
+ */
+ public String getPrefixedName() {
+ return prefixedName;
+ }
+
+ /**
+ * Used internally
+ * @param name
+ */
+ void setName(String name) {
+ this.name = name;
+ this.prefixedName = "m_" + name;
+ }
+
+ /**
+ * Returns the value of this material parameter.
+ * <p>
+ * Material parameters that are used for material definitions
+ * will not have a value, unless there's a default value declared
+ * in the definition.
+ *
+ * @return the value of this material parameter.
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value of this material parameter.
+ * <p>
+ * It is assumed the value is of the same {@link MatParam#getVarType() type}
+ * as this material parameter.
+ *
+ * @param value the value of this material parameter.
+ */
+ public void setValue(Object value) {
+ this.value = value;
+ }
+
+ void apply(Renderer r, Technique technique) {
+ TechniqueDef techDef = technique.getDef();
+ if (techDef.isUsingShaders()) {
+ technique.updateUniformParam(getPrefixedName(), getVarType(), getValue(), true);
+ }
+ if (ffBinding != null && r instanceof GL1Renderer) {
+ ((GL1Renderer) r).setFixedFuncBinding(ffBinding, getValue());
+ }
+ }
+
+ /**
+ * Returns the material parameter value as it would appear in a J3M
+ * file. E.g.<br/>
+ * <code>
+ * MaterialParameters {<br/>
+ * ABC : 1 2 3 4<br/>
+ * }<br/>
+ * </code>
+ * Assuming "ABC" is a Vector4 parameter, then the value
+ * "1 2 3 4" would be returned by this method.
+ * <br/><br/>
+ * @return material parameter value as it would appear in a J3M file.
+ */
+ public String getValueAsString() {
+ switch (type) {
+ case Boolean:
+ case Float:
+ case Int:
+ return value.toString();
+ case Vector2:
+ Vector2f v2 = (Vector2f) value;
+ return v2.getX() + " " + v2.getY();
+/*
+This may get used at a later point of time
+When arrays can be inserted in J3M files
+
+ case Vector2Array:
+ Vector2f[] v2Arr = (Vector2f[]) value;
+ String v2str = "";
+ for (int i = 0; i < v2Arr.length ; i++) {
+ v2str += v2Arr[i].getX() + " " + v2Arr[i].getY() + "\n";
+ }
+ return v2str;
+*/
+ case Vector3:
+ Vector3f v3 = (Vector3f) value;
+ return v3.getX() + " " + v3.getY() + " " + v3.getZ();
+/*
+ case Vector3Array:
+ Vector3f[] v3Arr = (Vector3f[]) value;
+ String v3str = "";
+ for (int i = 0; i < v3Arr.length ; i++) {
+ v3str += v3Arr[i].getX() + " "
+ + v3Arr[i].getY() + " "
+ + v3Arr[i].getZ() + "\n";
+ }
+ return v3str;
+ case Vector4Array:
+ // can be either ColorRGBA, Vector4f or Quaternion
+ if (value instanceof Vector4f) {
+ Vector4f[] v4arr = (Vector4f[]) value;
+ String v4str = "";
+ for (int i = 0; i < v4arr.length ; i++) {
+ v4str += v4arr[i].getX() + " "
+ + v4arr[i].getY() + " "
+ + v4arr[i].getZ() + " "
+ + v4arr[i].getW() + "\n";
+ }
+ return v4str;
+ } else if (value instanceof ColorRGBA) {
+ ColorRGBA[] colorArr = (ColorRGBA[]) value;
+ String colStr = "";
+ for (int i = 0; i < colorArr.length ; i++) {
+ colStr += colorArr[i].getRed() + " "
+ + colorArr[i].getGreen() + " "
+ + colorArr[i].getBlue() + " "
+ + colorArr[i].getAlpha() + "\n";
+ }
+ return colStr;
+ } else if (value instanceof Quaternion) {
+ Quaternion[] quatArr = (Quaternion[]) value;
+ String quatStr = "";
+ for (int i = 0; i < quatArr.length ; i++) {
+ quatStr += quatArr[i].getX() + " "
+ + quatArr[i].getY() + " "
+ + quatArr[i].getZ() + " "
+ + quatArr[i].getW() + "\n";
+ }
+ return quatStr;
+ } else {
+ throw new UnsupportedOperationException("Unexpected Vector4Array type: " + value);
+ }
+*/
+ case Vector4:
+ // can be either ColorRGBA, Vector4f or Quaternion
+ if (value instanceof Vector4f) {
+ Vector4f v4 = (Vector4f) value;
+ return v4.getX() + " " + v4.getY() + " "
+ + v4.getZ() + " " + v4.getW();
+ } else if (value instanceof ColorRGBA) {
+ ColorRGBA color = (ColorRGBA) value;
+ return color.getRed() + " " + color.getGreen() + " "
+ + color.getBlue() + " " + color.getAlpha();
+ } else if (value instanceof Quaternion) {
+ Quaternion quat = (Quaternion) value;
+ return quat.getX() + " " + quat.getY() + " "
+ + quat.getZ() + " " + quat.getW();
+ } else {
+ throw new UnsupportedOperationException("Unexpected Vector4 type: " + value);
+ }
+ case Texture2D:
+ case Texture3D:
+ case TextureArray:
+ case TextureBuffer:
+ case TextureCubeMap:
+ Texture texVal = (Texture) value;
+ TextureKey texKey = (TextureKey) texVal.getKey();
+ if (texKey == null){
+ throw new UnsupportedOperationException("The specified MatParam cannot be represented in J3M");
+ }
+
+ String ret = "";
+ if (texKey.isFlipY()) {
+ ret += "Flip ";
+ }
+ if (texVal.getWrap(Texture.WrapAxis.S) == WrapMode.Repeat) {
+ ret += "Repeat ";
+ }
+
+ return ret + texKey.getName();
+ default:
+ return null; // parameter type not supported in J3M
+ }
+ }
+
+ @Override
+ public MatParam clone() {
+ try {
+ MatParam param = (MatParam) super.clone();
+ return param;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(type, "varType", null);
+ oc.write(name, "name", null);
+ oc.write(ffBinding, "ff_binding", null);
+ if (value instanceof Savable) {
+ Savable s = (Savable) value;
+ oc.write(s, "value_savable", null);
+ } else if (value instanceof Float) {
+ Float f = (Float) value;
+ oc.write(f.floatValue(), "value_float", 0f);
+ } else if (value instanceof Integer) {
+ Integer i = (Integer) value;
+ oc.write(i.intValue(), "value_int", 0);
+ } else if (value instanceof Boolean) {
+ Boolean b = (Boolean) value;
+ oc.write(b.booleanValue(), "value_bool", false);
+ }
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ type = ic.readEnum("varType", VarType.class, null);
+ name = ic.readString("name", null);
+ ffBinding = ic.readEnum("ff_binding", FixedFuncBinding.class, null);
+ switch (getVarType()) {
+ case Boolean:
+ value = ic.readBoolean("value_bool", false);
+ break;
+ case Float:
+ value = ic.readFloat("value_float", 0f);
+ break;
+ case Int:
+ value = ic.readInt("value_int", 0);
+ break;
+ default:
+ value = ic.readSavable("value_savable", null);
+ break;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof MatParam)) {
+ return false;
+ }
+
+ MatParam otherParam = (MatParam) other;
+ return otherParam.type == type
+ && otherParam.name.equals(name);
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 5;
+ hash = 17 * hash + (this.type != null ? this.type.hashCode() : 0);
+ hash = 17 * hash + (this.name != null ? this.name.hashCode() : 0);
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return type.name() + " " + name + " : " + getValueAsString();
+ }
+}
diff --git a/engine/src/core/com/jme3/material/MatParamTexture.java b/engine/src/core/com/jme3/material/MatParamTexture.java
new file mode 100644
index 0000000..fc8b469
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MatParamTexture.java
@@ -0,0 +1,67 @@
+package com.jme3.material;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.renderer.Renderer;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import java.io.IOException;
+
+public class MatParamTexture extends MatParam {
+
+ private Texture texture;
+ private int unit;
+
+ public MatParamTexture(VarType type, String name, Texture texture, int unit) {
+ super(type, name, texture, null);
+ this.texture = texture;
+ this.unit = unit;
+ }
+
+ public MatParamTexture() {
+ }
+
+ public Texture getTextureValue() {
+ return texture;
+ }
+
+ public void setTextureValue(Texture value) {
+ this.value = value;
+ this.texture = value;
+ }
+
+ public void setUnit(int unit) {
+ this.unit = unit;
+ }
+
+ public int getUnit() {
+ return unit;
+ }
+
+ @Override
+ public void apply(Renderer r, Technique technique) {
+ TechniqueDef techDef = technique.getDef();
+ r.setTexture(getUnit(), getTextureValue());
+ if (techDef.isUsingShaders()) {
+ technique.updateUniformParam(getPrefixedName(), getVarType(), getUnit(), true);
+ }
+ }
+
+ @Override
+ public void write(JmeExporter ex) throws IOException {
+ super.write(ex);
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(unit, "texture_unit", -1);
+ oc.write(texture, "texture", null);
+ }
+
+ @Override
+ public void read(JmeImporter im) throws IOException {
+ super.read(im);
+ InputCapsule ic = im.getCapsule(this);
+ unit = ic.readInt("texture_unit", -1);
+ texture = (Texture) ic.readSavable("texture", null);
+ }
+} \ No newline at end of file
diff --git a/engine/src/core/com/jme3/material/Material.java b/engine/src/core/com/jme3/material/Material.java
new file mode 100644
index 0000000..8d43853
--- /dev/null
+++ b/engine/src/core/com/jme3/material/Material.java
@@ -0,0 +1,1152 @@
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine All rights reserved.
+ * <p/>
+ * 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.
+ * <p/>
+ * * 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.
+ * <p/>
+ * * 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.
+ * <p/>
+ * 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.asset.Asset;
+import com.jme3.asset.AssetKey;
+import com.jme3.asset.AssetManager;
+import com.jme3.export.*;
+import com.jme3.light.*;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.material.RenderState.FaceCullMode;
+import com.jme3.material.TechniqueDef.LightMode;
+import com.jme3.material.TechniqueDef.ShadowMode;
+import com.jme3.math.*;
+import com.jme3.renderer.Caps;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.shader.Shader;
+import com.jme3.shader.Uniform;
+import com.jme3.shader.VarType;
+import com.jme3.texture.Texture;
+import com.jme3.util.ListMap;
+import com.jme3.util.TempVars;
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * <code>Material</code> describes the rendering style for a given
+ * {@link Geometry}.
+ * <p>A material is essentially a list of {@link MatParam parameters},
+ * those parameters map to uniforms which are defined in a shader.
+ * Setting the parameters can modify the behavior of a
+ * shader.
+ * <p/>
+ * @author Kirill Vainer
+ */
+public class Material implements Asset, Cloneable, Savable, Comparable<Material> {
+
+ // Version #2: Fixed issue with RenderState.apply*** flags not getting exported
+ public static final int SAVABLE_VERSION = 2;
+
+ private static final Logger logger = Logger.getLogger(Material.class.getName());
+ private static final RenderState additiveLight = new RenderState();
+ private static final RenderState depthOnly = new RenderState();
+ private static final Quaternion nullDirLight = new Quaternion(0, -1, 0, -1);
+
+ static {
+ depthOnly.setDepthTest(true);
+ depthOnly.setDepthWrite(true);
+ depthOnly.setFaceCullMode(RenderState.FaceCullMode.Back);
+ depthOnly.setColorWrite(false);
+
+ additiveLight.setBlendMode(RenderState.BlendMode.AlphaAdditive);
+ additiveLight.setDepthWrite(false);
+ }
+ private AssetKey key;
+ private String name;
+ private MaterialDef def;
+ private ListMap<String, MatParam> paramValues = new ListMap<String, MatParam>();
+ private Technique technique;
+ private HashMap<String, Technique> techniques = new HashMap<String, Technique>();
+ private int nextTexUnit = 0;
+ private RenderState additionalState = null;
+ private RenderState mergedRenderState = new RenderState();
+ private boolean transparent = false;
+ private boolean receivesShadows = false;
+ private int sortingId = -1;
+ private transient ColorRGBA ambientLightColor = new ColorRGBA(0, 0, 0, 1);
+
+ public Material(MaterialDef def) {
+ if (def == null) {
+ throw new NullPointerException("Material definition cannot be null");
+ }
+ this.def = def;
+
+ // Load default values from definition (if any)
+ for (MatParam param : def.getMaterialParams()){
+ if (param.getValue() != null){
+ setParam(param.getName(), param.getVarType(), param.getValue());
+ }
+ }
+ }
+
+ public Material(AssetManager contentMan, String defName) {
+ this((MaterialDef) contentMan.loadAsset(new AssetKey(defName)));
+ }
+
+ /**
+ * Do not use this constructor. Serialization purposes only.
+ */
+ public Material() {
+ }
+
+ /**
+ * Returns the asset key name of the asset from which this material was loaded.
+ *
+ * <p>This value will be <code>null</code> unless this material was loaded
+ * from a .j3m file.
+ *
+ * @return Asset key name of the j3m file
+ */
+ public String getAssetName() {
+ return key != null ? key.getName() : null;
+ }
+
+ /**
+ * @return the name of the material (not the same as the asset name), the returned value can be null
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * This method sets the name of the material.
+ * The name is not the same as the asset name.
+ * It can be null and there is no guarantee of its uniqness.
+ * @param name the name of the material
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setKey(AssetKey key) {
+ this.key = key;
+ }
+
+ public AssetKey getKey() {
+ return key;
+ }
+
+ /**
+ * Returns the sorting ID or sorting index for this material.
+ *
+ * <p>The sorting ID is used internally by the system to sort rendering
+ * of geometries. It sorted to reduce shader switches, if the shaders
+ * are equal, then it is sorted by textures.
+ *
+ * @return The sorting ID used for sorting geometries for rendering.
+ */
+ public int getSortId() {
+ Technique t = getActiveTechnique();
+ if (sortingId == -1 && t != null && t.getShader() != null) {
+ int texId = -1;
+ for (int i = 0; i < paramValues.size(); i++) {
+ MatParam param = paramValues.getValue(i);
+ if (param instanceof MatParamTexture) {
+ MatParamTexture tex = (MatParamTexture) param;
+ if (tex.getTextureValue() != null && tex.getTextureValue().getImage() != null) {
+ if (texId == -1) {
+ texId = 0;
+ }
+ texId += tex.getTextureValue().getImage().getId() % 0xff;
+ }
+ }
+ }
+ sortingId = texId + t.getShader().getId() * 1000;
+ }
+ return sortingId;
+ }
+
+ /**
+ * Uses the sorting ID for each material to compare them.
+ *
+ * @param m The other material to compare to.
+ *
+ * @return zero if the materials are equal, returns a negative value
+ * if <code>this</code> has a lower sorting ID than <code>m</code>,
+ * otherwise returns a positive value.
+ */
+ public int compareTo(Material m) {
+ return m.getSortId() - getSortId();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if(obj instanceof Material){
+ return ((Material)obj).compareTo(this) == 0;
+ }
+ return super.equals(obj);
+ }
+
+ /**
+ * Clones this material. The result is returned.
+ */
+ @Override
+ public Material clone() {
+ try {
+ Material mat = (Material) super.clone();
+
+ if (additionalState != null) {
+ mat.additionalState = additionalState.clone();
+ }
+ mat.technique = null;
+ mat.techniques = new HashMap<String, Technique>();
+
+ mat.paramValues = new ListMap<String, MatParam>();
+ for (int i = 0; i < paramValues.size(); i++) {
+ Map.Entry<String, MatParam> entry = paramValues.getEntry(i);
+ mat.paramValues.put(entry.getKey(), entry.getValue().clone());
+ }
+
+ return mat;
+ } catch (CloneNotSupportedException ex) {
+ throw new AssertionError();
+ }
+ }
+
+ /**
+ * Returns the currently active technique.
+ * <p>
+ * The technique is selected automatically by the {@link RenderManager}
+ * based on system capabilities. Users may select their own
+ * technique by using
+ * {@link #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager) }.
+ *
+ * @return the currently active technique.
+ *
+ * @see #selectTechnique(java.lang.String, com.jme3.renderer.RenderManager)
+ */
+ public Technique getActiveTechnique() {
+ return technique;
+ }
+
+ /**
+ * Check if the transparent value marker is set on this material.
+ * @return True if the transparent value marker is set on this material.
+ * @see #setTransparent(boolean)
+ */
+ public boolean isTransparent() {
+ return transparent;
+ }
+
+ /**
+ * Set the transparent value marker.
+ *
+ * <p>This value is merely a marker, by itself it does nothing.
+ * Generally model loaders will use this marker to indicate further
+ * up that the material is transparent and therefore any geometries
+ * using it should be put into the {@link Bucket#Transparent transparent
+ * bucket}.
+ *
+ * @param transparent the transparent value marker.
+ */
+ public void setTransparent(boolean transparent) {
+ this.transparent = transparent;
+ }
+
+ /**
+ * Check if the material should receive shadows or not.
+ *
+ * @return True if the material should receive shadows.
+ *
+ * @see Material#setReceivesShadows(boolean)
+ */
+ public boolean isReceivesShadows() {
+ return receivesShadows;
+ }
+
+ /**
+ * Set if the material should receive shadows or not.
+ *
+ * <p>This value is merely a marker, by itself it does nothing.
+ * Generally model loaders will use this marker to indicate
+ * the material should receive shadows and therefore any
+ * geometries using it should have the {@link ShadowMode#Receive} set
+ * on them.
+ *
+ * @param receivesShadows if the material should receive shadows or not.
+ */
+ public void setReceivesShadows(boolean receivesShadows) {
+ this.receivesShadows = receivesShadows;
+ }
+
+ /**
+ * Acquire the additional {@link RenderState render state} to apply
+ * for this material.
+ *
+ * <p>The first call to this method will create an additional render
+ * state which can be modified by the user to apply any render
+ * states in addition to the ones used by the renderer. Only render
+ * states which are modified in the additional render state will be applied.
+ *
+ * @return The additional render state.
+ */
+ public RenderState getAdditionalRenderState() {
+ if (additionalState == null) {
+ additionalState = RenderState.ADDITIONAL.clone();
+ }
+ return additionalState;
+ }
+
+ /**
+ * Get the material definition (j3md file info) that <code>this</code>
+ * material is implementing.
+ *
+ * @return the material definition this material implements.
+ */
+ public MaterialDef getMaterialDef() {
+ return def;
+ }
+
+ /**
+ * Returns the parameter set on this material with the given name,
+ * returns <code>null</code> if the parameter is not set.
+ *
+ * @param name The parameter name to look up.
+ * @return The MatParam if set, or null if not set.
+ */
+ public MatParam getParam(String name) {
+ MatParam param = paramValues.get(name);
+ return param;
+ }
+
+ /**
+ * Returns the texture parameter set on this material with the given name,
+ * returns <code>null</code> if the parameter is not set.
+ *
+ * @param name The parameter name to look up.
+ * @return The MatParamTexture if set, or null if not set.
+ */
+ public MatParamTexture getTextureParam(String name) {
+ MatParam param = paramValues.get(name);
+ if (param instanceof MatParamTexture) {
+ return (MatParamTexture) param;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a collection of all parameters set on this material.
+ *
+ * @return a collection of all parameters set on this material.
+ *
+ * @see #setParam(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
+ */
+ public Collection<MatParam> getParams() {
+ return paramValues.values();
+ }
+
+ private String checkSetParam(VarType type, String name) {
+ MatParam paramDef = def.getMaterialParam(name);
+ String newName = name;
+
+ if (paramDef == null && name.startsWith("m_")) {
+ newName = name.substring(2);
+ paramDef = def.getMaterialParam(newName);
+ if (paramDef == null) {
+ throw new IllegalArgumentException("Material parameter is not defined: " + name);
+ } else {
+ logger.log(Level.WARNING, "Material parameter {0} uses a deprecated naming convention use {1} instead ", new Object[]{name, newName});
+ }
+ } else if (paramDef == null) {
+ throw new IllegalArgumentException("Material parameter is not defined: " + name);
+ }
+
+ if (type != null && paramDef.getVarType() != type) {
+ logger.log(Level.WARNING, "Material parameter being set: {0} with "
+ + "type {1} doesn''t match definition types {2}", new Object[]{name, type.name(), paramDef.getVarType()} );
+ }
+
+ return newName;
+ }
+
+ /**
+ * Pass a parameter to the material shader.
+ *
+ * @param name the name of the parameter defined in the material definition (j3md)
+ * @param type the type of the parameter {@link VarType}
+ * @param value the value of the parameter
+ */
+ public void setParam(String name, VarType type, Object value) {
+ name = checkSetParam(type, name);
+
+ MatParam val = getParam(name);
+ if (technique != null) {
+ technique.notifySetParam(name, type, value);
+ }
+ if (val == null) {
+ MatParam paramDef = def.getMaterialParam(name);
+ paramValues.put(name, new MatParam(type, name, value, paramDef.getFixedFuncBinding()));
+ } else {
+ val.setValue(value);
+ }
+ }
+
+ /**
+ * Clear a parameter from this material. The parameter must exist
+ * @param name the name of the parameter to clear
+ */
+ public void clearParam(String name) {
+ //On removal, we don't check if the param exists in the paramDef, and just go on with the process.
+ // name = checkSetParam(null, name);
+
+ MatParam matParam = getParam(name);
+ if (matParam != null) {
+ paramValues.remove(name);
+ if (technique != null) {
+ technique.notifyClearParam(name);
+ }
+ if (matParam instanceof MatParamTexture) {
+ int texUnit = ((MatParamTexture) matParam).getUnit();
+ nextTexUnit--;
+ for (MatParam param : paramValues.values()) {
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texParam = (MatParamTexture) param;
+ if (texParam.getUnit() > texUnit) {
+ texParam.setUnit(texParam.getUnit() - 1);
+ }
+ }
+ }
+ }
+ }
+// else {
+// throw new IllegalArgumentException("The given parameter is not set.");
+// }
+ }
+
+ private void clearTextureParam(String name) {
+ name = checkSetParam(null, name);
+
+ MatParamTexture val = getTextureParam(name);
+ if (val == null) {
+ throw new IllegalArgumentException("The given texture for parameter \"" + name + "\" is null.");
+ }
+
+ int texUnit = val.getUnit();
+ paramValues.remove(name);
+ nextTexUnit--;
+ for (MatParam param : paramValues.values()) {
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texParam = (MatParamTexture) param;
+ if (texParam.getUnit() > texUnit) {
+ texParam.setUnit(texParam.getUnit() - 1);
+ }
+ }
+ }
+
+ sortingId = -1;
+ }
+
+ /**
+ * Set a texture parameter.
+ *
+ * @param name The name of the parameter
+ * @param type The variable type {@link VarType}
+ * @param value The texture value of the parameter.
+ *
+ * @throws IllegalArgumentException is value is null
+ */
+ public void setTextureParam(String name, VarType type, Texture value) {
+ if (value == null) {
+ throw new IllegalArgumentException();
+ }
+
+ name = checkSetParam(type, name);
+ MatParamTexture val = getTextureParam(name);
+ if (val == null) {
+ paramValues.put(name, new MatParamTexture(type, name, value, nextTexUnit++));
+ } else {
+ val.setTextureValue(value);
+ }
+
+ if (technique != null) {
+ technique.notifySetParam(name, type, nextTexUnit - 1);
+ }
+
+ // need to recompute sort ID
+ sortingId = -1;
+ }
+
+ /**
+ * Pass a texture to the material shader.
+ *
+ * @param name the name of the texture defined in the material definition
+ * (j3md) (for example Texture for Lighting.j3md)
+ * @param value the Texture object previously loaded by the asset manager
+ */
+ public void setTexture(String name, Texture value) {
+ if (value == null) {
+ // clear it
+ clearTextureParam(name);
+ return;
+ }
+
+ VarType paramType = null;
+ switch (value.getType()) {
+ case TwoDimensional:
+ paramType = VarType.Texture2D;
+ break;
+ case TwoDimensionalArray:
+ paramType = VarType.TextureArray;
+ break;
+ case ThreeDimensional:
+ paramType = VarType.Texture3D;
+ break;
+ case CubeMap:
+ paramType = VarType.TextureCubeMap;
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown texture type: " + value.getType());
+ }
+
+ setTextureParam(name, paramType, value);
+ }
+
+ /**
+ * Pass a Matrix4f to the material shader.
+ *
+ * @param name the name of the matrix defined in the material definition (j3md)
+ * @param value the Matrix4f object
+ */
+ public void setMatrix4(String name, Matrix4f value) {
+ setParam(name, VarType.Matrix4, value);
+ }
+
+ /**
+ * Pass a boolean to the material shader.
+ *
+ * @param name the name of the boolean defined in the material definition (j3md)
+ * @param value the boolean value
+ */
+ public void setBoolean(String name, boolean value) {
+ setParam(name, VarType.Boolean, value);
+ }
+
+ /**
+ * Pass a float to the material shader.
+ *
+ * @param name the name of the float defined in the material definition (j3md)
+ * @param value the float value
+ */
+ public void setFloat(String name, float value) {
+ setParam(name, VarType.Float, value);
+ }
+
+ /**
+ * Pass an int to the material shader.
+ *
+ * @param name the name of the int defined in the material definition (j3md)
+ * @param value the int value
+ */
+ public void setInt(String name, int value) {
+ setParam(name, VarType.Int, value);
+ }
+
+ /**
+ * Pass a Color to the material shader.
+ *
+ * @param name the name of the color defined in the material definition (j3md)
+ * @param value the ColorRGBA value
+ */
+ public void setColor(String name, ColorRGBA value) {
+ setParam(name, VarType.Vector4, value);
+ }
+
+ /**
+ * Pass a Vector2f to the material shader.
+ *
+ * @param name the name of the Vector2f defined in the material definition (j3md)
+ * @param value the Vector2f value
+ */
+ public void setVector2(String name, Vector2f value) {
+ setParam(name, VarType.Vector2, value);
+ }
+
+ /**
+ * Pass a Vector3f to the material shader.
+ *
+ * @param name the name of the Vector3f defined in the material definition (j3md)
+ * @param value the Vector3f value
+ */
+ public void setVector3(String name, Vector3f value) {
+ setParam(name, VarType.Vector3, value);
+ }
+
+ /**
+ * Pass a Vector4f to the material shader.
+ *
+ * @param name the name of the Vector4f defined in the material definition (j3md)
+ * @param value the Vector4f value
+ */
+ public void setVector4(String name, Vector4f value) {
+ setParam(name, VarType.Vector4, value);
+ }
+
+ private ColorRGBA getAmbientColor(LightList lightList) {
+ ambientLightColor.set(0, 0, 0, 1);
+ for (int j = 0; j < lightList.size(); j++) {
+ Light l = lightList.get(j);
+ if (l instanceof AmbientLight) {
+ ambientLightColor.addLocal(l.getColor());
+ }
+ }
+ ambientLightColor.a = 1.0f;
+ return ambientLightColor;
+ }
+
+ /**
+ * Uploads the lights in the light list as two uniform arrays.<br/><br/>
+ * * <p>
+ * <code>uniform vec4 g_LightColor[numLights];</code><br/>
+ * // g_LightColor.rgb is the diffuse/specular color of the light.<br/>
+ * // g_Lightcolor.a is the type of light, 0 = Directional, 1 = Point, <br/>
+ * // 2 = Spot. <br/>
+ * <br/>
+ * <code>uniform vec4 g_LightPosition[numLights];</code><br/>
+ * // g_LightPosition.xyz is the position of the light (for point lights)<br/>
+ * // or the direction of the light (for directional lights).<br/>
+ * // g_LightPosition.w is the inverse radius (1/r) of the light (for attenuation) <br/>
+ * </p>
+ */
+ protected void updateLightListUniforms(Shader shader, Geometry g, int numLights) {
+ if (numLights == 0) { // this shader does not do lighting, ignore.
+ return;
+ }
+
+ LightList lightList = g.getWorldLightList();
+ Uniform lightColor = shader.getUniform("g_LightColor");
+ Uniform lightPos = shader.getUniform("g_LightPosition");
+ Uniform lightDir = shader.getUniform("g_LightDirection");
+ lightColor.setVector4Length(numLights);
+ lightPos.setVector4Length(numLights);
+ lightDir.setVector4Length(numLights);
+
+ Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
+ ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+
+ int lightIndex = 0;
+
+ for (int i = 0; i < numLights; i++) {
+ if (lightList.size() <= i) {
+ lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+ lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+ } else {
+ Light l = lightList.get(i);
+ ColorRGBA color = l.getColor();
+ lightColor.setVector4InArray(color.getRed(),
+ color.getGreen(),
+ color.getBlue(),
+ l.getType().getId(),
+ i);
+
+ switch (l.getType()) {
+ case Directional:
+ DirectionalLight dl = (DirectionalLight) l;
+ Vector3f dir = dl.getDirection();
+ lightPos.setVector4InArray(dir.getX(), dir.getY(), dir.getZ(), -1, lightIndex);
+ break;
+ case Point:
+ PointLight pl = (PointLight) l;
+ Vector3f pos = pl.getPosition();
+ float invRadius = pl.getInvRadius();
+ lightPos.setVector4InArray(pos.getX(), pos.getY(), pos.getZ(), invRadius, lightIndex);
+ break;
+ case Spot:
+ SpotLight sl = (SpotLight) l;
+ Vector3f pos2 = sl.getPosition();
+ Vector3f dir2 = sl.getDirection();
+ float invRange = sl.getInvSpotRange();
+ float spotAngleCos = sl.getPackedAngleCos();
+
+ lightPos.setVector4InArray(pos2.getX(), pos2.getY(), pos2.getZ(), invRange, lightIndex);
+ lightDir.setVector4InArray(dir2.getX(), dir2.getY(), dir2.getZ(), spotAngleCos, lightIndex);
+ break;
+ case Ambient:
+ // skip this light. Does not increase lightIndex
+ continue;
+ default:
+ throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
+ }
+ }
+
+ lightIndex++;
+ }
+
+ while (lightIndex < numLights) {
+ lightColor.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+ lightPos.setVector4InArray(0f, 0f, 0f, 0f, lightIndex);
+
+ lightIndex++;
+ }
+ }
+
+ protected void renderMultipassLighting(Shader shader, Geometry g, RenderManager rm) {
+
+ Renderer r = rm.getRenderer();
+ LightList lightList = g.getWorldLightList();
+ Uniform lightDir = shader.getUniform("g_LightDirection");
+ Uniform lightColor = shader.getUniform("g_LightColor");
+ Uniform lightPos = shader.getUniform("g_LightPosition");
+ Uniform ambientColor = shader.getUniform("g_AmbientLightColor");
+ boolean isFirstLight = true;
+ boolean isSecondLight = false;
+
+ for (int i = 0; i < lightList.size(); i++) {
+ Light l = lightList.get(i);
+ if (l instanceof AmbientLight) {
+ continue;
+ }
+
+ if (isFirstLight) {
+ // set ambient color for first light only
+ ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+ isFirstLight = false;
+ isSecondLight = true;
+ } else if (isSecondLight) {
+ ambientColor.setValue(VarType.Vector4, ColorRGBA.Black);
+ // apply additive blending for 2nd and future lights
+ r.applyRenderState(additiveLight);
+ isSecondLight = false;
+ }
+
+ TempVars vars = TempVars.get();
+ Quaternion tmpLightDirection = vars.quat1;
+ Quaternion tmpLightPosition = vars.quat2;
+ ColorRGBA tmpLightColor = vars.color;
+ Vector4f tmpVec = vars.vect4f;
+
+ ColorRGBA color = l.getColor();
+ tmpLightColor.set(color);
+ tmpLightColor.a = l.getType().getId();
+ lightColor.setValue(VarType.Vector4, tmpLightColor);
+
+ switch (l.getType()) {
+ case Directional:
+ DirectionalLight dl = (DirectionalLight) l;
+ Vector3f dir = dl.getDirection();
+
+ tmpLightPosition.set(dir.getX(), dir.getY(), dir.getZ(), -1);
+ lightPos.setValue(VarType.Vector4, tmpLightPosition);
+ tmpLightDirection.set(0, 0, 0, 0);
+ lightDir.setValue(VarType.Vector4, tmpLightDirection);
+ break;
+ case Point:
+ PointLight pl = (PointLight) l;
+ Vector3f pos = pl.getPosition();
+ float invRadius = pl.getInvRadius();
+
+ tmpLightPosition.set(pos.getX(), pos.getY(), pos.getZ(), invRadius);
+ lightPos.setValue(VarType.Vector4, tmpLightPosition);
+ tmpLightDirection.set(0, 0, 0, 0);
+ lightDir.setValue(VarType.Vector4, tmpLightDirection);
+ break;
+ case Spot:
+ SpotLight sl = (SpotLight) l;
+ Vector3f pos2 = sl.getPosition();
+ Vector3f dir2 = sl.getDirection();
+ float invRange = sl.getInvSpotRange();
+ float spotAngleCos = sl.getPackedAngleCos();
+
+ tmpLightPosition.set(pos2.getX(), pos2.getY(), pos2.getZ(), invRange);
+ lightPos.setValue(VarType.Vector4, tmpLightPosition);
+
+ //We transform the spot directoin in view space here to save 5 varying later in the lighting shader
+ //one vec4 less and a vec4 that becomes a vec3
+ //the downside is that spotAngleCos decoding happen now in the frag shader.
+ tmpVec.set(dir2.getX(), dir2.getY(), dir2.getZ(),0);
+ rm.getCurrentCamera().getViewMatrix().mult(tmpVec, tmpVec);
+ tmpLightDirection.set(tmpVec.getX(), tmpVec.getY(), tmpVec.getZ(), spotAngleCos);
+
+ lightDir.setValue(VarType.Vector4, tmpLightDirection);
+
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown type of light: " + l.getType());
+ }
+ vars.release();
+ r.setShader(shader);
+ r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
+ }
+
+ if (isFirstLight && lightList.size() > 0) {
+ // There are only ambient lights in the scene. Render
+ // a dummy "normal light" so we can see the ambient
+ ambientColor.setValue(VarType.Vector4, getAmbientColor(lightList));
+ lightColor.setValue(VarType.Vector4, ColorRGBA.BlackNoAlpha);
+ lightPos.setValue(VarType.Vector4, nullDirLight);
+ r.setShader(shader);
+ r.renderMesh(g.getMesh(), g.getLodLevel(), 1);
+ }
+ }
+
+ /**
+ * Select the technique to use for rendering this material.
+ * <p>
+ * If <code>name</code> is "Default", then one of the
+ * {@link MaterialDef#getDefaultTechniques() default techniques}
+ * on the material will be selected. Otherwise, the named technique
+ * will be found in the material definition.
+ * <p>
+ * Any candidate technique for selection (either default or named)
+ * must be verified to be compatible with the system, for that, the
+ * <code>renderManager</code> is queried for capabilities.
+ *
+ * @param name The name of the technique to select, pass "Default" to
+ * select one of the default techniques.
+ * @param renderManager The {@link RenderManager render manager}
+ * to query for capabilities.
+ *
+ * @throws IllegalArgumentException If "Default" is passed and no default
+ * techniques are available on the material definition, or if a name
+ * is passed but there's no technique by that name.
+ * @throws UnsupportedOperationException If no candidate technique supports
+ * the system capabilities.
+ */
+ public void selectTechnique(String name, RenderManager renderManager) {
+ // check if already created
+ Technique tech = techniques.get(name);
+ if (tech == null) {
+ // When choosing technique, we choose one that
+ // supports all the caps.
+ EnumSet<Caps> rendererCaps = renderManager.getRenderer().getCaps();
+
+ if (name.equals("Default")) {
+ List<TechniqueDef> techDefs = def.getDefaultTechniques();
+ if (techDefs == null || techDefs.isEmpty()) {
+ throw new IllegalArgumentException("No default techniques are available on material '" + def.getName() + "'");
+ }
+
+ TechniqueDef lastTech = null;
+ for (TechniqueDef techDef : techDefs) {
+ if (rendererCaps.containsAll(techDef.getRequiredCaps())) {
+ // use the first one that supports all the caps
+ tech = new Technique(this, techDef);
+ techniques.put(name, tech);
+ break;
+ }
+ lastTech = techDef;
+ }
+ if (tech == null) {
+ throw new UnsupportedOperationException("No default technique on material '" + def.getName() + "'\n"
+ + " is supported by the video hardware. The caps "
+ + lastTech.getRequiredCaps() + " are required.");
+ }
+
+ } else {
+ // create "special" technique instance
+ TechniqueDef techDef = def.getTechniqueDef(name);
+ if (techDef == null) {
+ throw new IllegalArgumentException("For material " + def.getName() + ", technique not found: " + name);
+ }
+
+ if (!rendererCaps.containsAll(techDef.getRequiredCaps())) {
+ throw new UnsupportedOperationException("The explicitly chosen technique '" + name + "' on material '" + def.getName() + "'\n"
+ + "requires caps " + techDef.getRequiredCaps() + " which are not "
+ + "supported by the video renderer");
+ }
+
+ tech = new Technique(this, techDef);
+ techniques.put(name, tech);
+ }
+ } else if (technique == tech) {
+ // attempting to switch to an already
+ // active technique.
+ return;
+ }
+
+ technique = tech;
+ tech.makeCurrent(def.getAssetManager());
+
+ // shader was changed
+ sortingId = -1;
+ }
+
+ private void autoSelectTechnique(RenderManager rm) {
+ if (technique == null) {
+ // NOTE: Not really needed anymore since we have technique
+ // selection by caps. Rename all "FixedFunc" techniques to "Default"
+ // and remove this hack.
+ if (!rm.getRenderer().getCaps().contains(Caps.GLSL100)) {
+ selectTechnique("FixedFunc", rm);
+ } else {
+ selectTechnique("Default", rm);
+ }
+ } else if (technique.isNeedReload()) {
+ technique.makeCurrent(def.getAssetManager());
+ }
+ }
+
+ /**
+ * Preloads this material for the given render manager.
+ * <p>
+ * Preloading the material can ensure that when the material is first
+ * used for rendering, there won't be any delay since the material has
+ * been already been setup for rendering.
+ *
+ * @param rm The render manager to preload for
+ */
+ public void preload(RenderManager rm) {
+ autoSelectTechnique(rm);
+
+ Renderer r = rm.getRenderer();
+ TechniqueDef techDef = technique.getDef();
+
+ Collection<MatParam> params = paramValues.values();
+ for (MatParam param : params) {
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texParam = (MatParamTexture) param;
+ r.setTexture(0, texParam.getTextureValue());
+ } else {
+ if (!techDef.isUsingShaders()) {
+ continue;
+ }
+
+ technique.updateUniformParam(param.getName(),
+ param.getVarType(),
+ param.getValue(), true);
+ }
+ }
+
+ Shader shader = technique.getShader();
+ if (techDef.isUsingShaders()) {
+ r.setShader(shader);
+ }
+ }
+
+ private void clearUniformsSetByCurrent(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+ int size = uniforms.size();
+ for (int i = 0; i < size; i++) {
+ Uniform u = uniforms.getValue(i);
+ u.clearSetByCurrentMaterial();
+ }
+ }
+
+ private void resetUniformsNotSetByCurrent(Shader shader) {
+ ListMap<String, Uniform> uniforms = shader.getUniformMap();
+ int size = uniforms.size();
+ for (int i = 0; i < size; i++) {
+ Uniform u = uniforms.getValue(i);
+ if (!u.isSetByCurrentMaterial()) {
+ u.clearValue();
+ }
+ }
+ }
+
+ /**
+ * Called by {@link RenderManager} to render the geometry by
+ * using this material.
+ *
+ * @param geom The geometry to render
+ * @param rm The render manager requesting the rendering
+ */
+ public void render(Geometry geom, RenderManager rm) {
+ autoSelectTechnique(rm);
+
+ Renderer r = rm.getRenderer();
+
+ TechniqueDef techDef = technique.getDef();
+
+ if (techDef.getLightMode() == LightMode.MultiPass
+ && geom.getWorldLightList().size() == 0) {
+ return;
+ }
+
+ if (rm.getForcedRenderState() != null) {
+ r.applyRenderState(rm.getForcedRenderState());
+ } else {
+ if (techDef.getRenderState() != null) {
+ r.applyRenderState(techDef.getRenderState().copyMergedTo(additionalState, mergedRenderState));
+ } else {
+ r.applyRenderState(RenderState.DEFAULT.copyMergedTo(additionalState, mergedRenderState));
+ }
+ }
+
+
+ // update camera and world matrices
+ // NOTE: setWorldTransform should have been called already
+ if (techDef.isUsingShaders()) {
+ // reset unchanged uniform flag
+ clearUniformsSetByCurrent(technique.getShader());
+ rm.updateUniformBindings(technique.getWorldBindUniforms());
+ }
+
+ // setup textures and uniforms
+ for (int i = 0; i < paramValues.size(); i++) {
+ MatParam param = paramValues.getValue(i);
+ param.apply(r, technique);
+ }
+
+ Shader shader = technique.getShader();
+
+ // send lighting information, if needed
+ switch (techDef.getLightMode()) {
+ case Disable:
+ r.setLighting(null);
+ break;
+ case SinglePass:
+ updateLightListUniforms(shader, geom, 4);
+ break;
+ case FixedPipeline:
+ r.setLighting(geom.getWorldLightList());
+ break;
+ case MultiPass:
+ // NOTE: Special case!
+ resetUniformsNotSetByCurrent(shader);
+ renderMultipassLighting(shader, geom, rm);
+ // very important, notice the return statement!
+ return;
+ }
+
+ // upload and bind shader
+ if (techDef.isUsingShaders()) {
+ // any unset uniforms will be set to 0
+ resetUniformsNotSetByCurrent(shader);
+ r.setShader(shader);
+ }
+
+ r.renderMesh(geom.getMesh(), geom.getLodLevel(), 1);
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(def.getAssetName(), "material_def", null);
+ oc.write(additionalState, "render_state", null);
+ oc.write(transparent, "is_transparent", false);
+ oc.writeStringSavableMap(paramValues, "parameters", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+
+ additionalState = (RenderState) ic.readSavable("render_state", null);
+ transparent = ic.readBoolean("is_transparent", false);
+
+ // Load the material def
+ String defName = ic.readString("material_def", null);
+ HashMap<String, MatParam> params = (HashMap<String, MatParam>) ic.readStringSavableMap("parameters", null);
+
+ boolean enableVcolor = false;
+ boolean separateTexCoord = false;
+ boolean applyDefaultValues = false;
+ boolean guessRenderStateApply = false;
+
+ int ver = ic.getSavableVersion(Material.class);
+ if (ver < 1){
+ applyDefaultValues = true;
+ }
+ if (ver < 2){
+ guessRenderStateApply = true;
+ }
+ if (im.getFormatVersion() == 0) {
+ // Enable compatibility with old models
+ if (defName.equalsIgnoreCase("Common/MatDefs/Misc/VertexColor.j3md")) {
+ // Using VertexColor, switch to Unshaded and set VertexColor=true
+ enableVcolor = true;
+ defName = "Common/MatDefs/Misc/Unshaded.j3md";
+ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/SimpleTextured.j3md")
+ || defName.equalsIgnoreCase("Common/MatDefs/Misc/SolidColor.j3md")) {
+ // Using SimpleTextured/SolidColor, just switch to Unshaded
+ defName = "Common/MatDefs/Misc/Unshaded.j3md";
+ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/WireColor.j3md")) {
+ // Using WireColor, set wireframe renderstate = true and use Unshaded
+ getAdditionalRenderState().setWireframe(true);
+ defName = "Common/MatDefs/Misc/Unshaded.j3md";
+ } else if (defName.equalsIgnoreCase("Common/MatDefs/Misc/Unshaded.j3md")) {
+ // Uses unshaded, ensure that the proper param is set
+ MatParam value = params.get("SeperateTexCoord");
+ if (value != null && ((Boolean) value.getValue()) == true) {
+ params.remove("SeperateTexCoord");
+ separateTexCoord = true;
+ }
+ }
+ assert applyDefaultValues && guessRenderStateApply;
+ }
+
+ def = (MaterialDef) im.getAssetManager().loadAsset(new AssetKey(defName));
+ paramValues = new ListMap<String, MatParam>();
+
+ // load the textures and update nextTexUnit
+ for (Map.Entry<String, MatParam> entry : params.entrySet()) {
+ MatParam param = entry.getValue();
+ if (param instanceof MatParamTexture) {
+ MatParamTexture texVal = (MatParamTexture) param;
+
+ if (nextTexUnit < texVal.getUnit() + 1) {
+ nextTexUnit = texVal.getUnit() + 1;
+ }
+
+ // the texture failed to load for this param
+ // do not add to param values
+ if (texVal.getTextureValue() == null || texVal.getTextureValue().getImage() == null) {
+ continue;
+ }
+ }
+ param.setName(checkSetParam(param.getVarType(), param.getName()));
+ paramValues.put(param.getName(), param);
+ }
+
+ if (applyDefaultValues){
+ // compatability with old versions where default vars were
+ // not available
+ for (MatParam param : def.getMaterialParams()){
+ if (param.getValue() != null && paramValues.get(param.getName()) == null){
+ setParam(param.getName(), param.getVarType(), param.getValue());
+ }
+ }
+ }
+ if (guessRenderStateApply && additionalState != null){
+ // Try to guess values of "apply" render state based on defaults
+ // if value != default then set apply to true
+ additionalState.applyPolyOffset = additionalState.offsetEnabled;
+ additionalState.applyAlphaFallOff = additionalState.alphaTest;
+ additionalState.applyAlphaTest = additionalState.alphaTest;
+ additionalState.applyBlendMode = additionalState.blendMode != BlendMode.Off;
+ additionalState.applyColorWrite = !additionalState.colorWrite;
+ additionalState.applyCullMode = additionalState.cullMode != FaceCullMode.Back;
+ additionalState.applyDepthTest = !additionalState.depthTest;
+ additionalState.applyDepthWrite = !additionalState.depthWrite;
+ additionalState.applyPointSprite = additionalState.pointSprite;
+ additionalState.applyStencilTest = additionalState.stencilTest;
+ additionalState.applyWireFrame = additionalState.wireframe;
+ }
+ if (enableVcolor) {
+ setBoolean("VertexColor", true);
+ }
+ if (separateTexCoord) {
+ setBoolean("SeparateTexCoord", true);
+ }
+ }
+}
diff --git a/engine/src/core/com/jme3/material/MaterialDef.java b/engine/src/core/com/jme3/material/MaterialDef.java
new file mode 100644
index 0000000..e7ec3fc
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MaterialDef.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2009-2010 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.asset.AssetManager;
+import com.jme3.shader.VarType;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Describes a J3MD (Material definition).
+ *
+ * @author Kirill Vainer
+ */
+public class MaterialDef {
+
+ private static final Logger logger = Logger.getLogger(MaterialDef.class.getName());
+
+ private String name;
+ private String assetName;
+ private AssetManager assetManager;
+
+ private List<TechniqueDef> defaultTechs;
+ private Map<String, TechniqueDef> techniques;
+ private Map<String, MatParam> matParams;
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public MaterialDef(){
+ }
+
+ /**
+ * Creates a new material definition with the given name.
+ *
+ * @param assetManager The asset manager to use to load shaders
+ * @param name The debug name of the material definition
+ */
+ public MaterialDef(AssetManager assetManager, String name){
+ this.assetManager = assetManager;
+ this.name = name;
+ techniques = new HashMap<String, TechniqueDef>();
+ matParams = new HashMap<String, MatParam>();
+ defaultTechs = new ArrayList<TechniqueDef>();
+ logger.log(Level.INFO, "Loaded material definition: {0}", name);
+ }
+
+ /**
+ * Returns the asset key name of the asset from which this material
+ * definition was loaded.
+ *
+ * @return Asset key name of the j3md file
+ */
+ public String getAssetName() {
+ return assetName;
+ }
+
+ /**
+ * Set the asset key name.
+ *
+ * @param assetName the asset key name
+ */
+ public void setAssetName(String assetName) {
+ this.assetName = assetName;
+ }
+
+ /**
+ * Returns the AssetManager passed in the constructor.
+ *
+ * @return the AssetManager passed in the constructor.
+ */
+ public AssetManager getAssetManager(){
+ return assetManager;
+ }
+
+ /**
+ * The debug name of the material definition.
+ *
+ * @return debug name of the material definition.
+ */
+ public String getName(){
+ return name;
+ }
+
+ /**
+ * Adds a new material parameter.
+ *
+ * @param type Type of the parameter
+ * @param name Name of the parameter
+ * @param value Default value of the parameter
+ * @param ffBinding Fixed function binding for the parameter
+ */
+ public void addMaterialParam(VarType type, String name, Object value, FixedFuncBinding ffBinding) {
+ matParams.put(name, new MatParam(type, name, value, ffBinding));
+ }
+
+ /**
+ * Returns the material parameter with the given name.
+ *
+ * @param name The name of the parameter to retrieve
+ *
+ * @return The material parameter, or null if it does not exist.
+ */
+ public MatParam getMaterialParam(String name){
+ return matParams.get(name);
+ }
+
+ /**
+ * Returns a collection of all material parameters declared in this
+ * material definition.
+ * <p>
+ * Modifying the material parameters or the collection will lead
+ * to undefined results.
+ *
+ * @return All material parameters declared in this definition.
+ */
+ public Collection<MatParam> getMaterialParams(){
+ return matParams.values();
+ }
+
+ /**
+ * Adds a new technique definition to this material definition.
+ * <p>
+ * If the technique name is "Default", it will be added
+ * to the list of {@link MaterialDef#getDefaultTechniques() default techniques}.
+ *
+ * @param technique The technique definition to add.
+ */
+ public void addTechniqueDef(TechniqueDef technique){
+ if (technique.getName().equals("Default")){
+ defaultTechs.add(technique);
+ }else{
+ techniques.put(technique.getName(), technique);
+ }
+ }
+
+ /**
+ * Returns a list of all default techniques.
+ *
+ * @return a list of all default techniques.
+ */
+ public List<TechniqueDef> getDefaultTechniques(){
+ return defaultTechs;
+ }
+
+ /**
+ * Returns a technique definition with the given name.
+ * This does not include default techniques which can be
+ * retrieved via {@link MaterialDef#getDefaultTechniques() }.
+ *
+ * @param name The name of the technique definition to find
+ *
+ * @return The technique definition, or null if cannot be found.
+ */
+ public TechniqueDef getTechniqueDef(String name) {
+ return techniques.get(name);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/material/MaterialList.java b/engine/src/core/com/jme3/material/MaterialList.java
new file mode 100644
index 0000000..9f2a512
--- /dev/null
+++ b/engine/src/core/com/jme3/material/MaterialList.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2009-2010 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 java.util.HashMap;
+
+/**
+ * A map from material name to a material. Used by loaders to locate
+ * materials for meshes inside a model.
+ *
+ * @author Kirill Vainer
+ */
+public class MaterialList extends HashMap<String, Material> {
+}
diff --git a/engine/src/core/com/jme3/material/RenderState.java b/engine/src/core/com/jme3/material/RenderState.java
new file mode 100644
index 0000000..37897fd
--- /dev/null
+++ b/engine/src/core/com/jme3/material/RenderState.java
@@ -0,0 +1,1070 @@
+/*
+ * Copyright (c) 2009-2010 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 and alpha test function.
+ *
+ * <p>The functions work similarly as described except that for stencil
+ * test function, 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>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
+ }
+
+ /**
+ * <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.StencilFunction,
+ * com.jme3.material.RenderState.StencilFunction)}
+ */
+ 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.applyPointSprite = false;
+ ADDITIONAL.applyWireFrame = false;
+ ADDITIONAL.applyCullMode = false;
+ ADDITIONAL.applyDepthWrite = false;
+ ADDITIONAL.applyDepthTest = false;
+ ADDITIONAL.applyColorWrite = false;
+ ADDITIONAL.applyBlendMode = false;
+ ADDITIONAL.applyAlphaTest = false;
+ ADDITIONAL.applyAlphaFallOff = false;
+ ADDITIONAL.applyPolyOffset = false;
+ }
+
+ boolean pointSprite = false;
+ boolean applyPointSprite = true;
+
+ 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;
+
+ BlendMode blendMode = BlendMode.Off;
+ boolean applyBlendMode = true;
+
+ boolean alphaTest = false;
+ boolean applyAlphaTest = true;
+
+ float alphaFallOff = 0;
+ boolean applyAlphaFallOff = true;
+
+ float offsetFactor = 0;
+ float offsetUnits = 0;
+ boolean offsetEnabled = false;
+ boolean applyPolyOffset = true;
+
+ boolean stencilTest = false;
+ boolean applyStencilTest = 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;
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(pointSprite, "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(alphaTest, "alphaTest", false);
+ oc.write(alphaFallOff, "alphaFallOff", 0);
+ 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);
+
+ // Only "additional render state" has them set to false by default
+ oc.write(applyPointSprite, "applyPointSprite", true);
+ 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(applyBlendMode, "applyBlendMode", true);
+ oc.write(applyAlphaTest, "applyAlphaTest", true);
+ oc.write(applyAlphaFallOff, "applyAlphaFallOff", true);
+ oc.write(applyPolyOffset, "applyPolyOffset", true);
+
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ pointSprite = ic.readBoolean("pointSprite", false);
+ 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);
+ alphaTest = ic.readBoolean("alphaTest", false);
+ alphaFallOff = ic.readFloat("alphaFallOff", 0);
+ 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);
+
+ applyPointSprite = ic.readBoolean("applyPointSprite", true);
+ 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);
+ applyBlendMode = ic.readBoolean("applyBlendMode", true);
+ applyAlphaTest = ic.readBoolean("applyAlphaTest", true);
+ applyAlphaFallOff = ic.readBoolean("applyAlphaFallOff", true);
+ applyPolyOffset = ic.readBoolean("applyPolyOffset", 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();
+ }
+ }
+
+ /**
+ * Enables point sprite mode.
+ *
+ * <p>When point sprite is enabled, any meshes
+ * with the type of {@link Mode#Points} will be rendered as 2D quads
+ * with texturing enabled. Fragment shaders can write to the
+ * <code>gl_PointCoord</code> variable to manipulate the texture coordinate
+ * for each pixel. The size of the 2D quad can be controlled by writing
+ * to the <code>gl_PointSize</code> variable in the vertex shader.
+ *
+ * @param pointSprite Enables Point Sprite mode.
+ */
+ public void setPointSprite(boolean pointSprite) {
+ applyPointSprite = true;
+ this.pointSprite = pointSprite;
+ }
+
+ /**
+ * Sets the alpha fall off value for alpha testing.
+ *
+ * <p>If the pixel's alpha value is greater than the
+ * <code>alphaFallOff</code> then the pixel will be rendered, otherwise
+ * the pixel will be discarded.
+ *
+ * @param alphaFallOff The alpha of all rendered pixels must be higher
+ * than this value to be rendered. This value should be between 0 and 1.
+ *
+ * @see RenderState#setAlphaTest(boolean)
+ */
+ public void setAlphaFallOff(float alphaFallOff) {
+ applyAlphaFallOff = true;
+ this.alphaFallOff = alphaFallOff;
+ }
+
+ /**
+ * Enable alpha testing.
+ *
+ * <p>When alpha testing is enabled, all input pixels' alpha are compared
+ * to the {@link RenderState#setAlphaFallOff(float) constant alpha falloff}.
+ * If the input alpha is greater than the falloff, the pixel will be rendered,
+ * otherwise it will be discarded.
+ *
+ * @param alphaTest Set to true to enable alpha testing.
+ *
+ * @see RenderState#setAlphaFallOff(float)
+ */
+ public void setAlphaTest(boolean alphaTest) {
+ applyAlphaTest = true;
+ this.alphaTest = 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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;
+ offsetEnabled = true;
+ offsetFactor = factor;
+ offsetUnits = units;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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 mode.
+ *
+ * @return the blend mode.
+ */
+ public BlendMode getBlendMode() {
+ return blendMode;
+ }
+
+ /**
+ * Check if point sprite mode is enabled
+ *
+ * @return True if point sprite mode is enabled.
+ *
+ * @see RenderState#setPointSprite(boolean)
+ */
+ public boolean isPointSprite() {
+ return pointSprite;
+ }
+
+ /**
+ * Check if alpha test is enabled.
+ *
+ * @return True if alpha test is enabled.
+ *
+ * @see RenderState#setAlphaTest(boolean)
+ */
+ public boolean isAlphaTest() {
+ return alphaTest;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Retrieve the alpha falloff value.
+ *
+ * @return the alpha falloff value.
+ *
+ * @see RenderState#setAlphaFallOff(float)
+ */
+ public float getAlphaFallOff() {
+ return alphaFallOff;
+ }
+
+ public boolean isApplyAlphaFallOff() {
+ return applyAlphaFallOff;
+ }
+
+ public boolean isApplyAlphaTest() {
+ return applyAlphaTest;
+ }
+
+ public boolean isApplyBlendMode() {
+ return applyBlendMode;
+ }
+
+ public boolean isApplyColorWrite() {
+ return applyColorWrite;
+ }
+
+ public boolean isApplyCullMode() {
+ return applyCullMode;
+ }
+
+ public boolean isApplyDepthTest() {
+ return applyDepthTest;
+ }
+
+ public boolean isApplyDepthWrite() {
+ return applyDepthWrite;
+ }
+
+ public boolean isApplyPointSprite() {
+ return applyPointSprite;
+ }
+
+ public boolean isApplyPolyOffset() {
+ return applyPolyOffset;
+ }
+
+ public boolean isApplyWireFrame() {
+ return applyWireFrame;
+ }
+
+ /**
+ * 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.applyPointSprite) {
+ state.pointSprite = additionalState.pointSprite;
+ } else {
+ state.pointSprite = pointSprite;
+ }
+ 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.applyColorWrite) {
+ state.colorWrite = additionalState.colorWrite;
+ } else {
+ state.colorWrite = colorWrite;
+ }
+ if (additionalState.applyBlendMode) {
+ state.blendMode = additionalState.blendMode;
+ } else {
+ state.blendMode = blendMode;
+ }
+ if (additionalState.applyAlphaTest) {
+ state.alphaTest = additionalState.alphaTest;
+ } else {
+ state.alphaTest = alphaTest;
+ }
+
+ if (additionalState.applyAlphaFallOff) {
+ state.alphaFallOff = additionalState.alphaFallOff;
+ } else {
+ state.alphaFallOff = alphaFallOff;
+ }
+ 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;
+ }
+ return state;
+ }
+
+ @Override
+ public String toString() {
+ return "RenderState[\n" + "pointSprite=" + pointSprite + "\napplyPointSprite=" + applyPointSprite + "\nwireframe=" + wireframe + "\napplyWireFrame=" + applyWireFrame + "\ncullMode=" + cullMode + "\napplyCullMode=" + applyCullMode + "\ndepthWrite=" + depthWrite + "\napplyDepthWrite=" + applyDepthWrite + "\ndepthTest=" + depthTest + "\napplyDepthTest=" + applyDepthTest + "\ncolorWrite=" + colorWrite + "\napplyColorWrite=" + applyColorWrite + "\nblendMode=" + blendMode + "\napplyBlendMode=" + applyBlendMode + "\nalphaTest=" + alphaTest + "\napplyAlphaTest=" + applyAlphaTest + "\nalphaFallOff=" + alphaFallOff + "\napplyAlphaFallOff=" + applyAlphaFallOff + "\noffsetEnabled=" + offsetEnabled + "\napplyPolyOffset=" + applyPolyOffset + "\noffsetFactor=" + offsetFactor + "\noffsetUnits=" + offsetUnits + "\n]";
+ }
+}
diff --git a/engine/src/core/com/jme3/material/Technique.java b/engine/src/core/com/jme3/material/Technique.java
new file mode 100644
index 0000000..5ae20a5
--- /dev/null
+++ b/engine/src/core/com/jme3/material/Technique.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2009-2010 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.asset.AssetManager;
+import com.jme3.export.*;
+import com.jme3.shader.*;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ * Represents a technique instance.
+ */
+public class Technique implements Savable {
+
+ private static final Logger logger = Logger.getLogger(Technique.class.getName());
+ private TechniqueDef def;
+ private Material owner;
+ private ArrayList<Uniform> worldBindUniforms;
+ private DefineList defines;
+ private Shader shader;
+ private boolean needReload = true;
+
+ /**
+ * Creates a new technique instance that implements the given
+ * technique definition.
+ *
+ * @param owner The material that will own this technique
+ * @param def The technique definition being implemented.
+ */
+ public Technique(Material owner, TechniqueDef def) {
+ this.owner = owner;
+ this.def = def;
+ if (def.isUsingShaders()) {
+ this.worldBindUniforms = new ArrayList<Uniform>();
+ this.defines = new DefineList();
+ }
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public Technique() {
+ }
+
+ /**
+ * Returns the technique definition that is implemented by this technique
+ * instance.
+ *
+ * @return the technique definition that is implemented by this technique
+ * instance.
+ */
+ public TechniqueDef getDef() {
+ return def;
+ }
+
+ /**
+ * Returns the shader currently used by this technique instance.
+ * <p>
+ * Shaders are typically loaded dynamically when the technique is first
+ * used, therefore, this variable will most likely be null most of the time.
+ *
+ * @return the shader currently used by this technique instance.
+ */
+ public Shader getShader() {
+ return shader;
+ }
+
+ /**
+ * Returns a list of uniforms that implements the world parameters
+ * that were requested by the material definition.
+ *
+ * @return a list of uniforms implementing the world parameters.
+ */
+ public List<Uniform> getWorldBindUniforms() {
+ return worldBindUniforms;
+ }
+
+ /**
+ * Called by the material to tell the technique a parameter was modified
+ */
+ void notifySetParam(String paramName, VarType type, Object value) {
+ String defineName = def.getShaderParamDefine(paramName);
+ if (defineName != null) {
+ needReload = defines.set(defineName, type, value);
+ }
+ if (shader != null) {
+ updateUniformParam(paramName, type, value);
+ }
+ }
+
+ /**
+ * Called by the material to tell the technique a parameter was cleared
+ */
+ void notifyClearParam(String paramName) {
+ String defineName = def.getShaderParamDefine(paramName);
+ if (defineName != null) {
+ needReload = defines.remove(defineName);
+ }
+ if (shader != null) {
+ if (!paramName.startsWith("m_")) {
+ paramName = "m_" + paramName;
+ }
+ shader.removeUniform(paramName);
+ }
+ }
+
+ void updateUniformParam(String paramName, VarType type, Object value, boolean ifNotOwner) {
+ Uniform u = shader.getUniform(paramName);
+
+// if (ifNotOwner && u.getLastChanger() == owner)
+// return;
+
+ switch (type) {
+ case Texture2D: // fall intentional
+ case Texture3D:
+ case TextureArray:
+ case TextureCubeMap:
+ case Int:
+ u.setValue(VarType.Int, value);
+ break;
+ default:
+ u.setValue(type, value);
+ break;
+ }
+// u.setLastChanger(owner);
+ }
+
+ void updateUniformParam(String paramName, VarType type, Object value) {
+ updateUniformParam(paramName, type, value, false);
+ }
+
+ /**
+ * Returns true if the technique must be reloaded.
+ * <p>
+ * If a technique needs to reload, then the {@link Material} should
+ * call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this
+ * technique.
+ *
+ * @return true if the technique must be reloaded.
+ */
+ public boolean isNeedReload() {
+ return needReload;
+ }
+
+ /**
+ * Prepares the technique for use by loading the shader and setting
+ * the proper defines based on material parameters.
+ *
+ * @param assetManager The asset manager to use for loading shaders.
+ */
+ public void makeCurrent(AssetManager assetManager) {
+ // check if reload is needed..
+ if (def.isUsingShaders()) {
+ DefineList newDefines = new DefineList();
+ Collection<MatParam> params = owner.getParams();
+ for (MatParam param : params) {
+ String defineName = def.getShaderParamDefine(param.getName());
+ if (defineName != null) {
+ newDefines.set(defineName, param.getVarType(), param.getValue());
+ }
+ }
+
+ if (!needReload && defines.getCompiled().equals(newDefines.getCompiled())) {
+ newDefines = null;
+ // defines have not been changed..
+ } else {
+ defines.clear();
+ defines.addFrom(newDefines);
+ // defines changed, recompile needed
+ loadShader(assetManager);
+ }
+ }
+ }
+
+ private void loadShader(AssetManager manager) {
+ // recompute define list
+ DefineList allDefines = new DefineList();
+ allDefines.addFrom(def.getShaderPresetDefines());
+ allDefines.addFrom(defines);
+
+ ShaderKey key = new ShaderKey(def.getVertexShaderName(),
+ def.getFragmentShaderName(),
+ allDefines,
+ def.getShaderLanguage());
+ shader = manager.loadShader(key);
+ if (shader == null) {
+ logger.warning("Failed to reload shader!");
+ return;
+ }
+
+ // refresh the uniform links
+ //owner.updateUniformLinks();
+
+ // register the world bound uniforms
+ worldBindUniforms.clear();
+ if (def.getWorldBindings() != null) {
+ for (UniformBinding binding : def.getWorldBindings()) {
+ Uniform uniform = shader.getUniform("g_" + binding.name());
+ uniform.setBinding(binding);
+ if (uniform != null) {
+ worldBindUniforms.add(uniform);
+ }
+ }
+ }
+
+ needReload = false;
+ }
+
+ public void write(JmeExporter ex) throws IOException {
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(def, "def", null);
+ // TODO:
+ // oc.write(owner, "owner", null);
+ oc.writeSavableArrayList(worldBindUniforms, "worldBindUniforms", null);
+ oc.write(defines, "defines", null);
+ oc.write(shader, "shader", null);
+ }
+
+ public void read(JmeImporter im) throws IOException {
+ InputCapsule ic = im.getCapsule(this);
+ def = (TechniqueDef) ic.readSavable("def", null);
+ worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null);
+ defines = (DefineList) ic.readSavable("defines", null);
+ shader = (Shader) ic.readSavable("shader", null);
+ //if (shader != null)
+ // owner.updateUniformLinks();
+ }
+}
diff --git a/engine/src/core/com/jme3/material/TechniqueDef.java b/engine/src/core/com/jme3/material/TechniqueDef.java
new file mode 100644
index 0000000..aaeb340
--- /dev/null
+++ b/engine/src/core/com/jme3/material/TechniqueDef.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2009-2010 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.renderer.Caps;
+import com.jme3.renderer.Renderer;
+import com.jme3.shader.DefineList;
+import com.jme3.shader.UniformBinding;
+import com.jme3.shader.VarType;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Describes a technique definition.
+ *
+ * @author Kirill Vainer
+ */
+public class TechniqueDef implements Savable {
+
+ /**
+ * Describes light rendering mode.
+ */
+ public enum LightMode {
+ /**
+ * Disable light-based rendering
+ */
+ Disable,
+
+ /**
+ * Enable light rendering by using a single pass.
+ * <p>
+ * An array of light positions and light colors is passed to the shader
+ * containing the world light list for the geometry being rendered.
+ */
+ SinglePass,
+
+ /**
+ * Enable light rendering by using multi-pass rendering.
+ * <p>
+ * The geometry will be rendered once for each light. Each time the
+ * light position and light color uniforms are updated to contain
+ * the values for the current light. The ambient light color uniform
+ * is only set to the ambient light color on the first pass, future
+ * passes have it set to black.
+ */
+ MultiPass,
+
+ /**
+ * Enable light rendering by using the
+ * {@link Renderer#setLighting(com.jme3.light.LightList) renderer's setLighting}
+ * method.
+ * <p>
+ * The specific details of rendering the lighting is up to the
+ * renderer implementation.
+ */
+ FixedPipeline,
+ }
+
+ public enum ShadowMode {
+ Disable,
+ InPass,
+ PostPass,
+ }
+
+ private EnumSet<Caps> requiredCaps = EnumSet.noneOf(Caps.class);
+ private String name;
+
+ private String vertName;
+ private String fragName;
+ private String shaderLang;
+ private DefineList presetDefines;
+ private boolean usesShaders;
+
+ private RenderState renderState;
+ private LightMode lightMode = LightMode.Disable;
+ private ShadowMode shadowMode = ShadowMode.Disable;
+
+ private HashMap<String, String> defineParams;
+ private ArrayList<UniformBinding> worldBinds;
+
+ /**
+ * Creates a new technique definition.
+ * <p>
+ * Used internally by the J3M/J3MD loader.
+ *
+ * @param name The name of the technique, should be set to <code>null</code>
+ * for default techniques.
+ */
+ public TechniqueDef(String name){
+ this.name = name == null ? "Default" : name;
+ }
+
+ /**
+ * Serialization only. Do not use.
+ */
+ public TechniqueDef(){
+ }
+
+ /**
+ * Returns the name of this technique as specified in the J3MD file.
+ * Default techniques have the name "Default".
+ *
+ * @return the name of this technique
+ */
+ public String getName(){
+ return name;
+ }
+
+ /**
+ * Returns the light mode.
+ * @return the light mode.
+ * @see LightMode
+ */
+ public LightMode getLightMode() {
+ return lightMode;
+ }
+
+ /**
+ * Set the light mode
+ *
+ * @param lightMode the light mode
+ *
+ * @see LightMode
+ */
+ public void setLightMode(LightMode lightMode) {
+ this.lightMode = lightMode;
+ }
+
+ /**
+ * Returns the shadow mode.
+ * @return the shadow mode.
+ */
+ public ShadowMode getShadowMode() {
+ return shadowMode;
+ }
+
+ /**
+ * Set the shadow mode.
+ *
+ * @param shadowMode the shadow mode.
+ *
+ * @see ShadowMode
+ */
+ public void setShadowMode(ShadowMode shadowMode) {
+ this.shadowMode = shadowMode;
+ }
+
+ /**
+ * Returns the render state that this technique is using
+ * @return the render state that this technique is using
+ * @see #setRenderState(com.jme3.material.RenderState)
+ */
+ public RenderState getRenderState() {
+ return renderState;
+ }
+
+ /**
+ * Sets the render state that this technique is using.
+ *
+ * @param renderState the render state that this technique is using.
+ *
+ * @see RenderState
+ */
+ public void setRenderState(RenderState renderState) {
+ this.renderState = renderState;
+ }
+
+ /**
+ * Returns true if this technique uses shaders, false otherwise.
+ *
+ * @return true if this technique uses shaders, false otherwise.
+ *
+ * @see #setShaderFile(java.lang.String, java.lang.String, java.lang.String)
+ */
+ public boolean isUsingShaders(){
+ return usesShaders;
+ }
+
+ /**
+ * Gets the {@link Caps renderer capabilities} that are required
+ * by this technique.
+ *
+ * @return the required renderer capabilities
+ */
+ public EnumSet<Caps> getRequiredCaps() {
+ return requiredCaps;
+ }
+
+ /**
+ * Sets the shaders that this technique definition will use.
+ *
+ * @param vertexShader The name of the vertex shader
+ * @param fragmentShader The name of the fragment shader
+ * @param shaderLanguage The shader language
+ */
+ public void setShaderFile(String vertexShader, String fragmentShader, String shaderLanguage){
+ this.vertName = vertexShader;
+ this.fragName = fragmentShader;
+ this.shaderLang = shaderLanguage;
+
+ Caps langCap = Caps.valueOf(shaderLanguage);
+ requiredCaps.add(langCap);
+
+ usesShaders = true;
+ }
+
+ /**
+ * Returns the define name which the given material parameter influences.
+ *
+ * @param paramName The parameter name to look up
+ * @return The define name
+ *
+ * @see #addShaderParamDefine(java.lang.String, java.lang.String)
+ */
+ public String getShaderParamDefine(String paramName){
+ if (defineParams == null)
+ return null;
+
+ return defineParams.get(paramName);
+ }
+
+ /**
+ * Adds a define linked to a material parameter.
+ * <p>
+ * Any time the material parameter on the parent material is altered,
+ * the appropriate define on the technique will be modified as well.
+ * See the method
+ * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
+ * on the exact details of how the material parameter changes the define.
+ *
+ * @param paramName The name of the material parameter to link to.
+ * @param defineName The name of the define parameter, e.g. USE_LIGHTING
+ */
+ public void addShaderParamDefine(String paramName, String defineName){
+ if (defineParams == null)
+ defineParams = new HashMap<String, String>();
+
+ defineParams.put(paramName, defineName);
+ }
+
+ /**
+ * Returns the {@link DefineList} for the preset defines.
+ *
+ * @return the {@link DefineList} for the preset defines.
+ *
+ * @see #addShaderPresetDefine(java.lang.String, com.jme3.shader.VarType, java.lang.Object)
+ */
+ public DefineList getShaderPresetDefines() {
+ return presetDefines;
+ }
+
+ /**
+ * Adds a preset define.
+ * <p>
+ * Preset defines do not depend upon any parameters to be activated,
+ * they are always passed to the shader as long as this technique is used.
+ *
+ * @param defineName The name of the define parameter, e.g. USE_LIGHTING
+ * @param type The type of the define. See
+ * {@link DefineList#set(java.lang.String, com.jme3.shader.VarType, java.lang.Object) }
+ * to see why it matters.
+ *
+ * @param value The value of the define
+ */
+ public void addShaderPresetDefine(String defineName, VarType type, Object value){
+ if (presetDefines == null)
+ presetDefines = new DefineList();
+
+ presetDefines.set(defineName, type, value);
+ }
+
+ /**
+ * Returns the name of the fragment shader used by the technique, or null
+ * if no fragment shader is specified.
+ *
+ * @return the name of the fragment shader to be used.
+ */
+ public String getFragmentShaderName() {
+ return fragName;
+ }
+
+
+ /**
+ * Returns the name of the vertex shader used by the technique, or null
+ * if no vertex shader is specified.
+ *
+ * @return the name of the vertex shader to be used.
+ */
+ public String getVertexShaderName() {
+ return vertName;
+ }
+
+ /**
+ * Returns the shader language of the shaders used in this technique.
+ *
+ * @return the shader language of the shaders used in this technique.
+ */
+ public String getShaderLanguage() {
+ return shaderLang;
+ }
+
+ /**
+ * Adds a new world parameter by the given name.
+ *
+ * @param name The world parameter to add.
+ * @return True if the world parameter name was found and added
+ * to the list of world parameters, false otherwise.
+ */
+ public boolean addWorldParam(String name) {
+ if (worldBinds == null){
+ worldBinds = new ArrayList<UniformBinding>();
+ }
+
+ try {
+ worldBinds.add( UniformBinding.valueOf(name) );
+ return true;
+ } catch (IllegalArgumentException ex){
+ return false;
+ }
+ }
+
+ /**
+ * Returns a list of world parameters that are used by this
+ * technique definition.
+ *
+ * @return The list of world parameters
+ */
+ public List<UniformBinding> getWorldBindings() {
+ return worldBinds;
+ }
+
+ public void write(JmeExporter ex) throws IOException{
+ OutputCapsule oc = ex.getCapsule(this);
+ oc.write(name, "name", null);
+ oc.write(vertName, "vertName", null);
+ oc.write(fragName, "fragName", null);
+ oc.write(shaderLang, "shaderLang", null);
+ oc.write(presetDefines, "presetDefines", null);
+ oc.write(lightMode, "lightMode", LightMode.Disable);
+ oc.write(shadowMode, "shadowMode", ShadowMode.Disable);
+ oc.write(renderState, "renderState", null);
+ oc.write(usesShaders, "usesShaders", false);
+ // TODO: Finish this when Map<String, String> export is available
+// oc.write(defineParams, "defineParams", null);
+ // TODO: Finish this when List<Enum> export is available
+// oc.write(worldBinds, "worldBinds", null);
+ }
+
+ public void read(JmeImporter im) throws IOException{
+ InputCapsule ic = im.getCapsule(this);
+ name = ic.readString("name", null);
+ vertName = ic.readString("vertName", null);
+ fragName = ic.readString("fragName", null);
+ shaderLang = ic.readString("shaderLang", null);
+ presetDefines = (DefineList) ic.readSavable("presetDefines", null);
+ lightMode = ic.readEnum("lightMode", LightMode.class, LightMode.Disable);
+ shadowMode = ic.readEnum("shadowMode", ShadowMode.class, ShadowMode.Disable);
+ renderState = (RenderState) ic.readSavable("renderState", null);
+ usesShaders = ic.readBoolean("usesShaders", false);
+ }
+
+}
diff --git a/engine/src/core/com/jme3/material/package.html b/engine/src/core/com/jme3/material/package.html
new file mode 100644
index 0000000..9af9cc8
--- /dev/null
+++ b/engine/src/core/com/jme3/material/package.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+
+The <code>com.jme3.material</code> package contains classes for manipulating
+jMonkeyEngine materials.
+Materials are applied to {@link com.jme3.scene.Geometry geometries} in the
+scene.
+Each geometry has a single material which is used to render that
+geometry.
+<p>
+Materials (also known as material instances) are extended from
+material definitions.
+
+<h3>Material definitions</h3>
+<p>
+Material definitions provide the "logic" for the material. Usually a shader that
+will handle drawing the object, and corresponding parameters that allow
+configuration of the shader.
+Material definitions can be created through J3MD files.
+The J3MD file abstracts the shader and its configuration away from the user, allowing a
+simple interface where one can simply set a few parameters on the material to change its
+appearance and the way its handled.
+
+<h3>Techniques</h3>
+<p>
+Techniques specify a specific way of rendering a material. Typically
+a technique is used to implement the same material for each configuration
+of the system. For GPUs that do not support shaders, a "fixed function pipeline"
+technique could exist to take care of rendering for that configuration
+
+<h3>Render states</h3>
+<p>
+See {@link com.jme3.material.RenderState}.
+
+<h3>Example Usage</h3>
+<p>
+Creating a textured material
+<code>
+// Create a material instance
+Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+
+// Load the texture.
+Texture tex = assetManager.loadTexture("Textures/Test/Test.jpg");
+
+// Set the parameters
+mat.setTexture("ColorMap", tex);
+</code>
+
+
+
+</body>
+</html>