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.AssetManager; 35 import com.jme3.export.*; 36 import com.jme3.shader.*; 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Collection; 40 import java.util.List; 41 import java.util.logging.Logger; 42 43 /** 44 * Represents a technique instance. 45 */ 46 public class Technique implements Savable { 47 48 private static final Logger logger = Logger.getLogger(Technique.class.getName()); 49 private TechniqueDef def; 50 private Material owner; 51 private ArrayList<Uniform> worldBindUniforms; 52 private DefineList defines; 53 private Shader shader; 54 private boolean needReload = true; 55 56 /** 57 * Creates a new technique instance that implements the given 58 * technique definition. 59 * 60 * @param owner The material that will own this technique 61 * @param def The technique definition being implemented. 62 */ 63 public Technique(Material owner, TechniqueDef def) { 64 this.owner = owner; 65 this.def = def; 66 if (def.isUsingShaders()) { 67 this.worldBindUniforms = new ArrayList<Uniform>(); 68 this.defines = new DefineList(); 69 } 70 } 71 72 /** 73 * Serialization only. Do not use. 74 */ 75 public Technique() { 76 } 77 78 /** 79 * Returns the technique definition that is implemented by this technique 80 * instance. 81 * 82 * @return the technique definition that is implemented by this technique 83 * instance. 84 */ 85 public TechniqueDef getDef() { 86 return def; 87 } 88 89 /** 90 * Returns the shader currently used by this technique instance. 91 * <p> 92 * Shaders are typically loaded dynamically when the technique is first 93 * used, therefore, this variable will most likely be null most of the time. 94 * 95 * @return the shader currently used by this technique instance. 96 */ 97 public Shader getShader() { 98 return shader; 99 } 100 101 /** 102 * Returns a list of uniforms that implements the world parameters 103 * that were requested by the material definition. 104 * 105 * @return a list of uniforms implementing the world parameters. 106 */ 107 public List<Uniform> getWorldBindUniforms() { 108 return worldBindUniforms; 109 } 110 111 /** 112 * Called by the material to tell the technique a parameter was modified 113 */ 114 void notifySetParam(String paramName, VarType type, Object value) { 115 String defineName = def.getShaderParamDefine(paramName); 116 if (defineName != null) { 117 needReload = defines.set(defineName, type, value); 118 } 119 if (shader != null) { 120 updateUniformParam(paramName, type, value); 121 } 122 } 123 124 /** 125 * Called by the material to tell the technique a parameter was cleared 126 */ 127 void notifyClearParam(String paramName) { 128 String defineName = def.getShaderParamDefine(paramName); 129 if (defineName != null) { 130 needReload = defines.remove(defineName); 131 } 132 if (shader != null) { 133 if (!paramName.startsWith("m_")) { 134 paramName = "m_" + paramName; 135 } 136 shader.removeUniform(paramName); 137 } 138 } 139 140 void updateUniformParam(String paramName, VarType type, Object value, boolean ifNotOwner) { 141 Uniform u = shader.getUniform(paramName); 142 143 // if (ifNotOwner && u.getLastChanger() == owner) 144 // return; 145 146 switch (type) { 147 case Texture2D: // fall intentional 148 case Texture3D: 149 case TextureArray: 150 case TextureCubeMap: 151 case Int: 152 u.setValue(VarType.Int, value); 153 break; 154 default: 155 u.setValue(type, value); 156 break; 157 } 158 // u.setLastChanger(owner); 159 } 160 161 void updateUniformParam(String paramName, VarType type, Object value) { 162 updateUniformParam(paramName, type, value, false); 163 } 164 165 /** 166 * Returns true if the technique must be reloaded. 167 * <p> 168 * If a technique needs to reload, then the {@link Material} should 169 * call {@link #makeCurrent(com.jme3.asset.AssetManager) } on this 170 * technique. 171 * 172 * @return true if the technique must be reloaded. 173 */ 174 public boolean isNeedReload() { 175 return needReload; 176 } 177 178 /** 179 * Prepares the technique for use by loading the shader and setting 180 * the proper defines based on material parameters. 181 * 182 * @param assetManager The asset manager to use for loading shaders. 183 */ 184 public void makeCurrent(AssetManager assetManager) { 185 // check if reload is needed.. 186 if (def.isUsingShaders()) { 187 DefineList newDefines = new DefineList(); 188 Collection<MatParam> params = owner.getParams(); 189 for (MatParam param : params) { 190 String defineName = def.getShaderParamDefine(param.getName()); 191 if (defineName != null) { 192 newDefines.set(defineName, param.getVarType(), param.getValue()); 193 } 194 } 195 196 if (!needReload && defines.getCompiled().equals(newDefines.getCompiled())) { 197 newDefines = null; 198 // defines have not been changed.. 199 } else { 200 defines.clear(); 201 defines.addFrom(newDefines); 202 // defines changed, recompile needed 203 loadShader(assetManager); 204 } 205 } 206 } 207 208 private void loadShader(AssetManager manager) { 209 // recompute define list 210 DefineList allDefines = new DefineList(); 211 allDefines.addFrom(def.getShaderPresetDefines()); 212 allDefines.addFrom(defines); 213 214 ShaderKey key = new ShaderKey(def.getVertexShaderName(), 215 def.getFragmentShaderName(), 216 allDefines, 217 def.getShaderLanguage()); 218 shader = manager.loadShader(key); 219 if (shader == null) { 220 logger.warning("Failed to reload shader!"); 221 return; 222 } 223 224 // refresh the uniform links 225 //owner.updateUniformLinks(); 226 227 // register the world bound uniforms 228 worldBindUniforms.clear(); 229 if (def.getWorldBindings() != null) { 230 for (UniformBinding binding : def.getWorldBindings()) { 231 Uniform uniform = shader.getUniform("g_" + binding.name()); 232 uniform.setBinding(binding); 233 if (uniform != null) { 234 worldBindUniforms.add(uniform); 235 } 236 } 237 } 238 239 needReload = false; 240 } 241 242 public void write(JmeExporter ex) throws IOException { 243 OutputCapsule oc = ex.getCapsule(this); 244 oc.write(def, "def", null); 245 // TODO: 246 // oc.write(owner, "owner", null); 247 oc.writeSavableArrayList(worldBindUniforms, "worldBindUniforms", null); 248 oc.write(defines, "defines", null); 249 oc.write(shader, "shader", null); 250 } 251 252 public void read(JmeImporter im) throws IOException { 253 InputCapsule ic = im.getCapsule(this); 254 def = (TechniqueDef) ic.readSavable("def", null); 255 worldBindUniforms = ic.readSavableArrayList("worldBindUniforms", null); 256 defines = (DefineList) ic.readSavable("defines", null); 257 shader = (Shader) ic.readSavable("shader", null); 258 //if (shader != null) 259 // owner.updateUniformLinks(); 260 } 261 } 262