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  */
32 package com.jme3.material;
33 
34 import com.jme3.asset.TextureKey;
35 import com.jme3.export.*;
36 import com.jme3.math.*;
37 import com.jme3.renderer.GL1Renderer;
38 import com.jme3.renderer.Renderer;
39 import com.jme3.shader.VarType;
40 import com.jme3.texture.Texture;
41 import com.jme3.texture.Texture.WrapMode;
42 import 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  */
50 public 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      */
MatParam(VarType type, String name, Object value, FixedFuncBinding ffBinding)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      */
MatParam()72     public MatParam() {
73     }
74 
75     /**
76      * Returns the fixed function binding.
77      *
78      * @return the fixed function binding.
79      */
getFixedFuncBinding()80     public FixedFuncBinding getFixedFuncBinding() {
81         return ffBinding;
82     }
83 
84     /**
85      * Returns the material parameter type.
86      *
87      * @return the material parameter type.
88      */
getVarType()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      */
getName()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      */
getPrefixedName()106     public String getPrefixedName() {
107         return prefixedName;
108     }
109 
110     /**
111      * Used internally
112      * @param name
113      */
setName(String name)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      */
getValue()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      */
setValue(Object value)140     public void setValue(Object value) {
141         this.value = value;
142     }
143 
apply(Renderer r, Technique technique)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      */
getValueAsString()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 /*
177 This may get used at a later point of time
178 When 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
clone()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 
write(JmeExporter ex)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 
read(JmeImporter im)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
equals(Object other)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
hashCode()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
toString()350     public String toString() {
351         return type.name() + " " + name + " : " + getValueAsString();
352     }
353 }
354