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 33 package com.jme3.shader; 34 35 import com.jme3.export.InputCapsule; 36 import com.jme3.export.JmeExporter; 37 import com.jme3.export.JmeImporter; 38 import com.jme3.export.OutputCapsule; 39 import com.jme3.math.*; 40 import com.jme3.util.BufferUtils; 41 import java.io.IOException; 42 import java.nio.FloatBuffer; 43 44 public 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