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.*; 36 import com.jme3.renderer.Renderer; 37 import com.jme3.scene.VertexBuffer; 38 import com.jme3.util.IntMap; 39 import com.jme3.util.IntMap.Entry; 40 import com.jme3.util.ListMap; 41 import com.jme3.util.NativeObject; 42 import java.io.IOException; 43 import java.util.ArrayList; 44 import java.util.Collection; 45 import java.util.HashMap; 46 47 public final class Shader extends NativeObject implements Savable { 48 49 private String language; 50 51 /** 52 * True if the shader is fully compiled & linked. 53 * (e.g no GL error will be invoked if used). 54 */ 55 private boolean usable = false; 56 57 /** 58 * A list of all shaders currently attached. 59 */ 60 private ArrayList<ShaderSource> shaderList; 61 62 /** 63 * Maps uniform name to the uniform variable. 64 */ 65 // private HashMap<String, Uniform> uniforms; 66 private ListMap<String, Uniform> uniforms; 67 68 /** 69 * Maps attribute name to the location of the attribute in the shader. 70 */ 71 private IntMap<Attribute> attribs; 72 73 /** 74 * Type of shader. The shader will control the pipeline of it's type. 75 */ 76 public static enum ShaderType { 77 /** 78 * Control fragment rasterization. (e.g color of pixel). 79 */ 80 Fragment, 81 82 /** 83 * Control vertex processing. (e.g transform of model to clip space) 84 */ 85 Vertex, 86 87 /** 88 * Control geometry assembly. (e.g compile a triangle list from input data) 89 */ 90 Geometry; 91 } 92 93 /** 94 * Shader source describes a shader object in OpenGL. Each shader source 95 * is assigned a certain pipeline which it controls (described by it's type). 96 */ 97 public static class ShaderSource extends NativeObject implements Savable { 98 99 ShaderType shaderType; 100 101 boolean usable = false; 102 String name = null; 103 String source = null; 104 String defines = null; 105 ShaderSource(ShaderType type)106 public ShaderSource(ShaderType type){ 107 super(ShaderSource.class); 108 this.shaderType = type; 109 if (type == null) 110 throw new NullPointerException("The shader type must be specified"); 111 } 112 ShaderSource(ShaderSource ss)113 protected ShaderSource(ShaderSource ss){ 114 super(ShaderSource.class, ss.id); 115 this.shaderType = ss.shaderType; 116 usable = false; 117 name = ss.name; 118 // forget source & defines 119 } 120 ShaderSource()121 public ShaderSource(){ 122 super(ShaderSource.class); 123 } 124 write(JmeExporter ex)125 public void write(JmeExporter ex) throws IOException{ 126 OutputCapsule oc = ex.getCapsule(this); 127 oc.write(shaderType, "shaderType", null); 128 oc.write(name, "name", null); 129 oc.write(source, "source", null); 130 oc.write(defines, "defines", null); 131 } 132 read(JmeImporter im)133 public void read(JmeImporter im) throws IOException{ 134 InputCapsule ic = im.getCapsule(this); 135 shaderType = ic.readEnum("shaderType", ShaderType.class, null); 136 name = ic.readString("name", null); 137 source = ic.readString("source", null); 138 defines = ic.readString("defines", null); 139 } 140 setName(String name)141 public void setName(String name){ 142 this.name = name; 143 } 144 getName()145 public String getName(){ 146 return name; 147 } 148 getType()149 public ShaderType getType() { 150 return shaderType; 151 } 152 setSource(String source)153 public void setSource(String source){ 154 if (source == null) 155 throw new NullPointerException("Shader source cannot be null"); 156 157 this.source = source; 158 setUpdateNeeded(); 159 } 160 setDefines(String defines)161 public void setDefines(String defines){ 162 if (defines == null) 163 throw new NullPointerException("Shader defines cannot be null"); 164 165 this.defines = defines; 166 setUpdateNeeded(); 167 } 168 getSource()169 public String getSource(){ 170 return source; 171 } 172 getDefines()173 public String getDefines(){ 174 return defines; 175 } 176 isUsable()177 public boolean isUsable(){ 178 return usable; 179 } 180 setUsable(boolean usable)181 public void setUsable(boolean usable){ 182 this.usable = usable; 183 } 184 185 @Override toString()186 public String toString(){ 187 String nameTxt = ""; 188 if (name != null) 189 nameTxt = "name="+name+", "; 190 if (defines != null) 191 nameTxt += "defines, "; 192 193 194 return getClass().getSimpleName() + "["+nameTxt+"type=" 195 + shaderType.name()+"]"; 196 } 197 resetObject()198 public void resetObject(){ 199 id = -1; 200 usable = false; 201 setUpdateNeeded(); 202 } 203 deleteObject(Object rendererObject)204 public void deleteObject(Object rendererObject){ 205 ((Renderer)rendererObject).deleteShaderSource(ShaderSource.this); 206 } 207 createDestructableClone()208 public NativeObject createDestructableClone(){ 209 return new ShaderSource(ShaderSource.this); 210 } 211 } 212 213 /** 214 * Create an empty shader. 215 */ Shader(String language)216 public Shader(String language){ 217 super(Shader.class); 218 this.language = language; 219 shaderList = new ArrayList<ShaderSource>(); 220 // uniforms = new HashMap<String, Uniform>(); 221 uniforms = new ListMap<String, Uniform>(); 222 attribs = new IntMap<Attribute>(); 223 } 224 225 /** 226 * Do not use this constructor. Serialization purposes only. 227 */ Shader()228 public Shader(){ 229 super(Shader.class); 230 } 231 Shader(Shader s)232 protected Shader(Shader s){ 233 super(Shader.class, s.id); 234 shaderList = new ArrayList<ShaderSource>(); 235 //uniforms = new ListMap<String, Uniform>(); 236 //attribs = new IntMap<Attribute>(); 237 238 // NOTE: Because ShaderSources are registered separately with 239 // the GLObjectManager 240 for (ShaderSource source : s.shaderList){ 241 shaderList.add( (ShaderSource)source.createDestructableClone() ); 242 } 243 } 244 write(JmeExporter ex)245 public void write(JmeExporter ex) throws IOException{ 246 OutputCapsule oc = ex.getCapsule(this); 247 oc.write(language, "language", null); 248 oc.writeSavableArrayList(shaderList, "shaderList", null); 249 oc.writeIntSavableMap(attribs, "attribs", null); 250 oc.writeStringSavableMap(uniforms, "uniforms", null); 251 } 252 read(JmeImporter im)253 public void read(JmeImporter im) throws IOException{ 254 InputCapsule ic = im.getCapsule(this); 255 language = ic.readString("language", null); 256 shaderList = ic.readSavableArrayList("shaderList", null); 257 attribs = (IntMap<Attribute>) ic.readIntSavableMap("attribs", null); 258 259 HashMap<String, Uniform> uniMap = (HashMap<String, Uniform>) ic.readStringSavableMap("uniforms", null); 260 uniforms = new ListMap<String, Uniform>(uniMap); 261 } 262 263 /** 264 * Creates a deep clone of the shader, where the sources are available 265 * but have not been compiled yet. Does not copy the uniforms or attribs. 266 * @return 267 */ 268 // public Shader createDeepClone(String defines){ 269 // Shader newShader = new Shader(language); 270 // for (ShaderSource source : shaderList){ 271 // if (!source.getDefines().equals(defines)){ 272 // // need to clone the shadersource so 273 // // the correct defines can be placed 274 // ShaderSource newSource = new ShaderSource(source.getType()); 275 // newSource.setSource(source.getSource()); 276 // newSource.setDefines(defines); 277 // newShader.addSource(newSource); 278 // }else{ 279 // // no need to clone source, also saves 280 // // having to compile the shadersource 281 // newShader.addSource(source); 282 // } 283 // } 284 // return newShader; 285 // } 286 287 /** 288 * Adds source code to a certain pipeline. 289 * 290 * @param type The pipeline to control 291 * @param source The shader source code (in GLSL). 292 */ addSource(ShaderType type, String name, String source, String defines)293 public void addSource(ShaderType type, String name, String source, String defines){ 294 ShaderSource shader = new ShaderSource(type); 295 shader.setSource(source); 296 shader.setName(name); 297 if (defines != null) 298 shader.setDefines(defines); 299 300 shaderList.add(shader); 301 setUpdateNeeded(); 302 } 303 addSource(ShaderType type, String source, String defines)304 public void addSource(ShaderType type, String source, String defines){ 305 addSource(type, null, source, defines); 306 } 307 addSource(ShaderType type, String source)308 public void addSource(ShaderType type, String source){ 309 addSource(type, source, null); 310 } 311 312 /** 313 * Adds an existing shader source to this shader. 314 * @param source 315 */ addSource(ShaderSource source)316 private void addSource(ShaderSource source){ 317 shaderList.add(source); 318 setUpdateNeeded(); 319 } 320 getUniform(String name)321 public Uniform getUniform(String name){ 322 Uniform uniform = uniforms.get(name); 323 if (uniform == null){ 324 uniform = new Uniform(); 325 uniform.name = name; 326 uniforms.put(name, uniform); 327 } 328 return uniform; 329 } 330 removeUniform(String name)331 public void removeUniform(String name){ 332 uniforms.remove(name); 333 } 334 getAttribute(VertexBuffer.Type attribType)335 public Attribute getAttribute(VertexBuffer.Type attribType){ 336 int ordinal = attribType.ordinal(); 337 Attribute attrib = attribs.get(ordinal); 338 if (attrib == null){ 339 attrib = new Attribute(); 340 attrib.name = attribType.name(); 341 attribs.put(ordinal, attrib); 342 } 343 return attrib; 344 } 345 346 // public Collection<Uniform> getUniforms(){ 347 // return uniforms.values(); 348 // } 349 getUniformMap()350 public ListMap<String, Uniform> getUniformMap(){ 351 return uniforms; 352 } 353 354 // public Collection<Attribute> getAttributes() { 355 // return attribs. 356 // } 357 getSources()358 public Collection<ShaderSource> getSources(){ 359 return shaderList; 360 } 361 getLanguage()362 public String getLanguage(){ 363 return language; 364 } 365 366 @Override toString()367 public String toString(){ 368 return getClass().getSimpleName() + "[language="+language 369 + ", numSources="+shaderList.size() 370 + ", numUniforms="+uniforms.size() 371 + ", shaderSources="+getSources()+"]"; 372 } 373 374 /** 375 * Clears all sources. Assuming that they have already been detached and 376 * removed on the GL side. 377 */ resetSources()378 public void resetSources(){ 379 shaderList.clear(); 380 } 381 382 /** 383 * Returns true if this program and all it's shaders have been compiled, 384 * linked and validated successfuly. 385 */ isUsable()386 public boolean isUsable(){ 387 return usable; 388 } 389 390 /** 391 * Sets if the program can be used. Should only be called by the Renderer. 392 * @param usable 393 */ setUsable(boolean usable)394 public void setUsable(boolean usable){ 395 this.usable = usable; 396 } 397 398 /** 399 * Usually called when the shader itself changes or during any 400 * time when the var locations need to be refreshed. 401 */ resetLocations()402 public void resetLocations(){ 403 // NOTE: Shader sources will be reset seperately from the shader itself. 404 for (Uniform uniform : uniforms.values()){ 405 uniform.reset(); // fixes issue with re-initialization 406 } 407 for (Entry<Attribute> entry : attribs){ 408 entry.getValue().location = -2; 409 } 410 } 411 412 @Override setUpdateNeeded()413 public void setUpdateNeeded(){ 414 super.setUpdateNeeded(); 415 resetLocations(); 416 } 417 418 /** 419 * Called by the object manager to reset all object IDs. This causes 420 * the shader to be reuploaded to the GPU incase the display was restarted. 421 */ 422 @Override resetObject()423 public void resetObject() { 424 this.id = -1; 425 this.usable = false; 426 427 for (ShaderSource source : shaderList){ 428 source.resetObject(); 429 } 430 431 setUpdateNeeded(); 432 } 433 434 @Override deleteObject(Object rendererObject)435 public void deleteObject(Object rendererObject) { 436 ((Renderer)rendererObject).deleteShader(this); 437 } 438 createDestructableClone()439 public NativeObject createDestructableClone(){ 440 return new Shader(this); 441 } 442 443 } 444