1/*
2 * Copyright (c) 2009-2010 jMonkeyEngine
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 *   notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 *   notice, this list of conditions and the following disclaimer in the
14 *   documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17 *   may be used to endorse or promote products derived from this software
18 *   without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32package com.jme3.material;
33
34import com.jme3.asset.TextureKey;
35import com.jme3.export.*;
36import com.jme3.math.*;
37import com.jme3.renderer.GL1Renderer;
38import com.jme3.renderer.Renderer;
39import com.jme3.shader.VarType;
40import com.jme3.texture.Texture;
41import com.jme3.texture.Texture.WrapMode;
42import java.io.IOException;
43
44/**
45 * Describes a material parameter. This is used for both defining a name and type
46 * as well as a material parameter value.
47 *
48 * @author Kirill Vainer
49 */
50public class MatParam implements Savable, Cloneable {
51
52    protected VarType type;
53    protected String name;
54    protected String prefixedName;
55    protected Object value;
56    protected FixedFuncBinding ffBinding;
57
58    /**
59     * Create a new material parameter. For internal use only.
60     */
61    public MatParam(VarType type, String name, Object value, FixedFuncBinding ffBinding) {
62        this.type = type;
63        this.name = name;
64        this.prefixedName = "m_" + name;
65        this.value = value;
66        this.ffBinding = ffBinding;
67    }
68
69    /**
70     * Serialization only. Do not use.
71     */
72    public MatParam() {
73    }
74
75    /**
76     * Returns the fixed function binding.
77     *
78     * @return the fixed function binding.
79     */
80    public FixedFuncBinding getFixedFuncBinding() {
81        return ffBinding;
82    }
83
84    /**
85     * Returns the material parameter type.
86     *
87     * @return the material parameter type.
88     */
89    public VarType getVarType() {
90        return type;
91    }
92
93    /**
94     * Returns the name of the material parameter.
95     * @return the name of the material parameter.
96     */
97    public String getName() {
98        return name;
99    }
100
101    /**
102     * Returns the name with "m_" prefixed to it.
103     *
104     * @return the name with "m_" prefixed to it
105     */
106    public String getPrefixedName() {
107        return prefixedName;
108    }
109
110    /**
111     * Used internally
112     * @param name
113     */
114    void setName(String name) {
115        this.name = name;
116        this.prefixedName = "m_" + name;
117    }
118
119    /**
120     * Returns the value of this material parameter.
121     * <p>
122     * Material parameters that are used for material definitions
123     * will not have a value, unless there's a default value declared
124     * in the definition.
125     *
126     * @return the value of this material parameter.
127     */
128    public Object getValue() {
129        return value;
130    }
131
132    /**
133     * Sets the value of this material parameter.
134     * <p>
135     * It is assumed the value is of the same {@link MatParam#getVarType() type}
136     * as this material parameter.
137     *
138     * @param value the value of this material parameter.
139     */
140    public void setValue(Object value) {
141        this.value = value;
142    }
143
144    void apply(Renderer r, Technique technique) {
145        TechniqueDef techDef = technique.getDef();
146        if (techDef.isUsingShaders()) {
147            technique.updateUniformParam(getPrefixedName(), getVarType(), getValue(), true);
148        }
149        if (ffBinding != null && r instanceof GL1Renderer) {
150            ((GL1Renderer) r).setFixedFuncBinding(ffBinding, getValue());
151        }
152    }
153
154    /**
155     * Returns the material parameter value as it would appear in a J3M
156     * file. E.g.<br/>
157     * <code>
158     * MaterialParameters {<br/>
159     *     ABC : 1 2 3 4<br/>
160     * }<br/>
161     * </code>
162     * Assuming "ABC" is a Vector4 parameter, then the value
163     * "1 2 3 4" would be returned by this method.
164     * <br/><br/>
165     * @return material parameter value as it would appear in a J3M file.
166     */
167    public String getValueAsString() {
168        switch (type) {
169            case Boolean:
170            case Float:
171            case Int:
172                return value.toString();
173            case Vector2:
174                Vector2f v2 = (Vector2f) value;
175                return v2.getX() + " " + v2.getY();
176/*
177This may get used at a later point of time
178When arrays can be inserted in J3M files
179
180            case Vector2Array:
181                Vector2f[] v2Arr = (Vector2f[]) value;
182                String v2str = "";
183                for (int i = 0; i < v2Arr.length ; i++) {
184                    v2str += v2Arr[i].getX() + " " + v2Arr[i].getY() + "\n";
185                }
186                return v2str;
187*/
188            case Vector3:
189                Vector3f v3 = (Vector3f) value;
190                return v3.getX() + " " + v3.getY() + " " + v3.getZ();
191/*
192            case Vector3Array:
193                Vector3f[] v3Arr = (Vector3f[]) value;
194                String v3str = "";
195                for (int i = 0; i < v3Arr.length ; i++) {
196                    v3str += v3Arr[i].getX() + " "
197                            + v3Arr[i].getY() + " "
198                            + v3Arr[i].getZ() + "\n";
199                }
200                return v3str;
201            case Vector4Array:
202                // can be either ColorRGBA, Vector4f or Quaternion
203                if (value instanceof Vector4f) {
204                    Vector4f[] v4arr = (Vector4f[]) value;
205                    String v4str = "";
206                    for (int i = 0; i < v4arr.length ; i++) {
207                        v4str += v4arr[i].getX() + " "
208                                + v4arr[i].getY() + " "
209                                + v4arr[i].getZ() + " "
210                                + v4arr[i].getW() + "\n";
211                    }
212                    return v4str;
213                } else if (value instanceof ColorRGBA) {
214                    ColorRGBA[] colorArr = (ColorRGBA[]) value;
215                    String colStr = "";
216                    for (int i = 0; i < colorArr.length ; i++) {
217                        colStr += colorArr[i].getRed() + " "
218                                + colorArr[i].getGreen() + " "
219                                + colorArr[i].getBlue() + " "
220                                + colorArr[i].getAlpha() + "\n";
221                    }
222                    return colStr;
223                } else if (value instanceof Quaternion) {
224                    Quaternion[] quatArr = (Quaternion[]) value;
225                    String quatStr = "";
226                    for (int i = 0; i < quatArr.length ; i++) {
227                        quatStr += quatArr[i].getX() + " "
228                                + quatArr[i].getY() + " "
229                                + quatArr[i].getZ() + " "
230                                + quatArr[i].getW() + "\n";
231                    }
232                    return quatStr;
233                } else {
234                    throw new UnsupportedOperationException("Unexpected Vector4Array type: " + value);
235                }
236*/
237            case Vector4:
238                // can be either ColorRGBA, Vector4f or Quaternion
239                if (value instanceof Vector4f) {
240                    Vector4f v4 = (Vector4f) value;
241                    return v4.getX() + " " + v4.getY() + " "
242                            + v4.getZ() + " " + v4.getW();
243                } else if (value instanceof ColorRGBA) {
244                    ColorRGBA color = (ColorRGBA) value;
245                    return color.getRed() + " " + color.getGreen() + " "
246                            + color.getBlue() + " " + color.getAlpha();
247                } else if (value instanceof Quaternion) {
248                    Quaternion quat = (Quaternion) value;
249                    return quat.getX() + " " + quat.getY() + " "
250                            + quat.getZ() + " " + quat.getW();
251                } else {
252                    throw new UnsupportedOperationException("Unexpected Vector4 type: " + value);
253                }
254            case Texture2D:
255            case Texture3D:
256            case TextureArray:
257            case TextureBuffer:
258            case TextureCubeMap:
259                Texture texVal = (Texture) value;
260                TextureKey texKey = (TextureKey) texVal.getKey();
261                if (texKey == null){
262                    throw new UnsupportedOperationException("The specified MatParam cannot be represented in J3M");
263                }
264
265                String ret = "";
266                if (texKey.isFlipY()) {
267                    ret += "Flip ";
268                }
269                if (texVal.getWrap(Texture.WrapAxis.S) == WrapMode.Repeat) {
270                    ret += "Repeat ";
271                }
272
273                return ret + texKey.getName();
274            default:
275                return null; // parameter type not supported in J3M
276        }
277    }
278
279    @Override
280    public MatParam clone() {
281        try {
282            MatParam param = (MatParam) super.clone();
283            return param;
284        } catch (CloneNotSupportedException ex) {
285            throw new AssertionError();
286        }
287    }
288
289    public void write(JmeExporter ex) throws IOException {
290        OutputCapsule oc = ex.getCapsule(this);
291        oc.write(type, "varType", null);
292        oc.write(name, "name", null);
293        oc.write(ffBinding, "ff_binding", null);
294        if (value instanceof Savable) {
295            Savable s = (Savable) value;
296            oc.write(s, "value_savable", null);
297        } else if (value instanceof Float) {
298            Float f = (Float) value;
299            oc.write(f.floatValue(), "value_float", 0f);
300        } else if (value instanceof Integer) {
301            Integer i = (Integer) value;
302            oc.write(i.intValue(), "value_int", 0);
303        } else if (value instanceof Boolean) {
304            Boolean b = (Boolean) value;
305            oc.write(b.booleanValue(), "value_bool", false);
306        }
307    }
308
309    public void read(JmeImporter im) throws IOException {
310        InputCapsule ic = im.getCapsule(this);
311        type = ic.readEnum("varType", VarType.class, null);
312        name = ic.readString("name", null);
313        ffBinding = ic.readEnum("ff_binding", FixedFuncBinding.class, null);
314        switch (getVarType()) {
315            case Boolean:
316                value = ic.readBoolean("value_bool", false);
317                break;
318            case Float:
319                value = ic.readFloat("value_float", 0f);
320                break;
321            case Int:
322                value = ic.readInt("value_int", 0);
323                break;
324            default:
325                value = ic.readSavable("value_savable", null);
326                break;
327        }
328    }
329
330    @Override
331    public boolean equals(Object other) {
332        if (!(other instanceof MatParam)) {
333            return false;
334        }
335
336        MatParam otherParam = (MatParam) other;
337        return otherParam.type == type
338                && otherParam.name.equals(name);
339    }
340
341    @Override
342    public int hashCode() {
343        int hash = 5;
344        hash = 17 * hash + (this.type != null ? this.type.hashCode() : 0);
345        hash = 17 * hash + (this.name != null ? this.name.hashCode() : 0);
346        return hash;
347    }
348
349    @Override
350    public String toString() {
351        return type.name() + " " + name + " : " + getValueAsString();
352    }
353}
354