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
33package com.jme3.shader;
34
35import com.jme3.export.InputCapsule;
36import com.jme3.export.JmeExporter;
37import com.jme3.export.JmeImporter;
38import com.jme3.export.OutputCapsule;
39import com.jme3.math.*;
40import com.jme3.util.BufferUtils;
41import java.io.IOException;
42import java.nio.FloatBuffer;
43
44public class Uniform extends ShaderVariable {
45
46    private static final Integer ZERO_INT = Integer.valueOf(0);
47    private static final Float ZERO_FLT = Float.valueOf(0);
48    private static final FloatBuffer ZERO_BUF = BufferUtils.createFloatBuffer(4*4);
49
50    /**
51     * Currently set value of the uniform.
52     */
53    protected Object value = null;
54    protected FloatBuffer multiData = null;
55
56    /**
57     * Type of uniform
58     */
59    protected VarType varType;
60
61    /**
62     * Binding to a renderer value, or null if user-defined uniform
63     */
64    protected UniformBinding binding;
65
66    protected boolean setByCurrentMaterial = false;
67//    protected Object lastChanger = null;
68
69    @Override
70    public void write(JmeExporter ex) throws IOException{
71        super.write(ex);
72        OutputCapsule oc = ex.getCapsule(this);
73        oc.write(varType, "varType", null);
74        oc.write(binding, "binding", null);
75        switch (varType){
76            case Boolean:
77                oc.write( ((Boolean)value).booleanValue(), "valueBoolean", false );
78                break;
79            case Float:
80                oc.write( ((Float)value).floatValue(), "valueFloat", 0);
81                break;
82            case FloatArray:
83                oc.write( (FloatBuffer)value, "valueFloatArray", null);
84                break;
85            case Int:
86                oc.write( ((Integer)value).intValue(), "valueInt", 0);
87                break;
88            case Matrix3:
89                oc.write( (Matrix3f)value, "valueMatrix3", null);
90                break;
91            case Matrix3Array:
92            case Matrix4Array:
93            case Vector2Array:
94                throw new UnsupportedOperationException("Come again?");
95            case Matrix4:
96                oc.write( (Matrix4f)value, "valueMatrix4", null);
97                break;
98            case Vector2:
99                oc.write( (Vector2f)value, "valueVector2", null);
100                break;
101            case Vector3:
102                oc.write( (Vector3f)value, "valueVector3", null);
103                break;
104            case Vector3Array:
105                oc.write( (FloatBuffer)value, "valueVector3Array", null);
106                break;
107            case Vector4:
108                oc.write( (ColorRGBA)value, "valueVector4", null);
109                break;
110            case Vector4Array:
111                oc.write( (FloatBuffer)value, "valueVector4Array", null);
112                break;
113        }
114    }
115
116    @Override
117    public void read(JmeImporter im) throws IOException{
118        super.read(im);
119        InputCapsule ic = im.getCapsule(this);
120        varType = ic.readEnum("varType", VarType.class, null);
121        binding = ic.readEnum("binding", UniformBinding.class, null);
122        switch (varType){
123            case Boolean:
124                value = ic.readBoolean("valueBoolean", false);
125                break;
126            case Float:
127                value = ic.readFloat("valueFloat", 0);
128                break;
129            case FloatArray:
130                value = ic.readFloatBuffer("valueFloatArray", null);
131                break;
132            case Int:
133                value = ic.readInt("valueInt", 0);
134                break;
135            case Matrix3:
136                multiData = ic.readFloatBuffer("valueMatrix3", null);
137                value = multiData;
138                break;
139            case Matrix4:
140                multiData = ic.readFloatBuffer("valueMatrix4", null);
141                value = multiData;
142                break;
143            case Vector2:
144                value = ic.readSavable("valueVector2", null);
145                break;
146            case Vector3:
147                value = ic.readSavable("valueVector3", null);
148                break;
149            case Vector3Array:
150                value = ic.readFloatBuffer("valueVector3Array", null);
151                break;
152            case Vector4:
153                value = ic.readSavable("valueVector4", null);
154                break;
155            case Vector4Array:
156                value = ic.readFloatBuffer("valueVector4Array", null);
157                break;
158        }
159    }
160
161    @Override
162    public String toString(){
163        StringBuilder sb = new StringBuilder();
164        if (name != null){
165            sb.append("Uniform[name=");
166            sb.append(name);
167            if (varType != null){
168                sb.append(", type=");
169                sb.append(varType);
170                sb.append(", value=");
171                sb.append(value);
172            }else{
173                sb.append(", value=<not set>");
174            }
175        }
176        sb.append("]");
177        return sb.toString();
178    }
179
180    public void setBinding(UniformBinding binding){
181        this.binding = binding;
182    }
183
184    public UniformBinding getBinding(){
185        return binding;
186    }
187
188    public VarType getVarType() {
189        return varType;
190    }
191
192    public Object getValue(){
193        return value;
194    }
195
196    public boolean isSetByCurrentMaterial() {
197        return setByCurrentMaterial;
198    }
199
200    public void clearSetByCurrentMaterial(){
201        setByCurrentMaterial = false;
202    }
203
204//    public void setLastChanger(Object lastChanger){
205//        this.lastChanger = lastChanger;
206//    }
207//
208//    public Object getLastChanger(){
209//        return lastChanger;
210//    }
211
212    public void clearValue(){
213        updateNeeded = true;
214
215        if (multiData != null){
216            ZERO_BUF.clear();
217            multiData.clear();
218
219            while (multiData.remaining() > 0){
220                ZERO_BUF.limit( Math.min(multiData.remaining(), 16) );
221                multiData.put(ZERO_BUF);
222            }
223
224            multiData.clear();
225
226            return;
227        }
228
229        if (varType == null)
230            return;
231
232        switch (varType){
233            case Int:
234                this.value = ZERO_INT;
235                break;
236            case Boolean:
237                this.value = Boolean.FALSE;
238                break;
239            case Float:
240                this.value = ZERO_FLT;
241                break;
242            case Vector2:
243                this.value = Vector2f.ZERO;
244                break;
245            case Vector3:
246                this.value = Vector3f.ZERO;
247                break;
248            case Vector4:
249                if (this.value instanceof ColorRGBA){
250                    this.value = ColorRGBA.BlackNoAlpha;
251                }else{
252                    this.value = Quaternion.ZERO;
253                }
254                break;
255            default:
256                break; // won't happen because those are either textures
257                       // or multidata types
258        }
259    }
260
261    public void setValue(VarType type, Object value){
262        if (location == -1)
263            return;
264
265        if (varType != null && varType != type)
266            throw new IllegalArgumentException("Expected a "+varType.name()+" value!");
267
268        if (value == null)
269            throw new NullPointerException();
270
271        setByCurrentMaterial = true;
272
273        switch (type){
274            case Matrix3:
275                Matrix3f m3 = (Matrix3f) value;
276                if (multiData == null)
277                    multiData = BufferUtils.createFloatBuffer(9);
278
279                m3.fillFloatBuffer(multiData, true);
280                multiData.clear();
281                break;
282            case Matrix4:
283                Matrix4f m4 = (Matrix4f) value;
284                if (multiData == null)
285                    multiData = BufferUtils.createFloatBuffer(16);
286
287                m4.fillFloatBuffer(multiData, true);
288                multiData.clear();
289                break;
290            case FloatArray:
291                float[] fa = (float[]) value;
292                if (multiData == null){
293                    multiData = BufferUtils.createFloatBuffer(fa);
294                }else{
295                    multiData = BufferUtils.ensureLargeEnough(multiData, fa.length);
296                }
297
298                multiData.put(fa);
299                multiData.clear();
300                break;
301            case Vector2Array:
302                Vector2f[] v2a = (Vector2f[]) value;
303                if (multiData == null){
304                    multiData = BufferUtils.createFloatBuffer(v2a);
305                } else {
306                    multiData = BufferUtils.ensureLargeEnough(multiData, v2a.length * 2);
307                }
308
309                for (int i = 0; i < v2a.length; i++)
310                    BufferUtils.setInBuffer(v2a[i], multiData, i);
311
312                multiData.clear();
313                break;
314            case Vector3Array:
315                Vector3f[] v3a = (Vector3f[]) value;
316                if (multiData == null){
317                    multiData = BufferUtils.createFloatBuffer(v3a);
318                } else{
319                    multiData = BufferUtils.ensureLargeEnough(multiData, v3a.length * 3);
320                }
321
322                for (int i = 0; i < v3a.length; i++)
323                    BufferUtils.setInBuffer(v3a[i], multiData, i);
324
325                multiData.clear();
326                break;
327            case Vector4Array:
328                Quaternion[] v4a = (Quaternion[]) value;
329                if (multiData == null){
330                    multiData = BufferUtils.createFloatBuffer(v4a);
331                } else {
332                    multiData = BufferUtils.ensureLargeEnough(multiData, v4a.length * 4);
333                }
334
335                for (int i = 0; i < v4a.length; i++)
336                    BufferUtils.setInBuffer(v4a[i], multiData, i);
337
338                multiData.clear();
339                break;
340            case Matrix3Array:
341                Matrix3f[] m3a = (Matrix3f[]) value;
342
343                if (multiData == null)
344                    multiData = BufferUtils.createFloatBuffer(m3a.length * 9);
345                else{
346                    multiData = BufferUtils.ensureLargeEnough(multiData, m3a.length * 9);
347                }
348
349                for (int i = 0; i < m3a.length; i++)
350                    m3a[i].fillFloatBuffer(multiData, true);
351
352                multiData.clear();
353                break;
354            case Matrix4Array:
355                Matrix4f[] m4a = (Matrix4f[]) value;
356
357                if (multiData == null)
358                    multiData = BufferUtils.createFloatBuffer(m4a.length * 16);
359                else{
360                    multiData = BufferUtils.ensureLargeEnough(multiData, m4a.length * 16);
361                }
362
363                for (int i = 0; i < m4a.length; i++)
364                    m4a[i].fillFloatBuffer(multiData, true);
365
366                multiData.clear();
367                break;
368            // Only use check if equals optimization for primitive values
369            case Int:
370            case Float:
371            case Boolean:
372                if (this.value != null && this.value.equals(value))
373                    return;
374
375                this.value = value;
376                break;
377            default:
378                this.value = value;
379                break;
380        }
381
382        if (multiData != null)
383            this.value = multiData;
384
385        varType = type;
386        updateNeeded = true;
387    }
388
389    public void setVector4Length(int length){
390        if (location == -1)
391            return;
392
393        FloatBuffer fb = (FloatBuffer) value;
394        if (fb == null || fb.capacity() < length){
395            value = BufferUtils.createFloatBuffer(length * 4);
396        }
397
398        varType = VarType.Vector4Array;
399        updateNeeded = true;
400        setByCurrentMaterial = true;
401    }
402
403    public void setVector4InArray(float x, float y, float z, float w, int index){
404        if (location == -1)
405            return;
406
407        if (varType != null && varType != VarType.Vector4Array)
408            throw new IllegalArgumentException("Expected a "+varType.name()+" value!");
409
410        FloatBuffer fb = (FloatBuffer) value;
411        fb.position(index * 4);
412        fb.put(x).put(y).put(z).put(w);
413        fb.rewind();
414        updateNeeded = true;
415        setByCurrentMaterial = true;
416    }
417
418    public boolean isUpdateNeeded(){
419        return updateNeeded;
420    }
421
422    public void clearUpdateNeeded(){
423        updateNeeded = false;
424    }
425
426    public void reset(){
427        setByCurrentMaterial = false;
428        location = -2;
429        updateNeeded = true;
430    }
431
432}
433