1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.graphics.g3d.shaders; 18 19 import com.badlogic.gdx.graphics.Camera; 20 import com.badlogic.gdx.graphics.Color; 21 import com.badlogic.gdx.graphics.GLTexture; 22 import com.badlogic.gdx.graphics.Mesh; 23 import com.badlogic.gdx.graphics.VertexAttribute; 24 import com.badlogic.gdx.graphics.VertexAttributes; 25 import com.badlogic.gdx.graphics.g3d.Attributes; 26 import com.badlogic.gdx.graphics.g3d.Renderable; 27 import com.badlogic.gdx.graphics.g3d.Shader; 28 import com.badlogic.gdx.graphics.g3d.utils.RenderContext; 29 import com.badlogic.gdx.graphics.g3d.utils.TextureDescriptor; 30 import com.badlogic.gdx.graphics.glutils.ShaderProgram; 31 import com.badlogic.gdx.math.Matrix3; 32 import com.badlogic.gdx.math.Matrix4; 33 import com.badlogic.gdx.math.Vector2; 34 import com.badlogic.gdx.math.Vector3; 35 import com.badlogic.gdx.utils.Array; 36 import com.badlogic.gdx.utils.GdxRuntimeException; 37 import com.badlogic.gdx.utils.IntArray; 38 import com.badlogic.gdx.utils.IntIntMap; 39 40 /** @author Xoppa A BaseShader is a wrapper around a ShaderProgram that keeps track of the uniform and attribute locations. It does 41 * not manage the ShaderPogram, you are still responsible for disposing the ShaderProgram. */ 42 public abstract class BaseShader implements Shader { 43 public interface Validator { 44 /** @return True if the input is valid for the renderable, false otherwise. */ 45 boolean validate (final BaseShader shader, final int inputID, final Renderable renderable); 46 } 47 48 public interface Setter { 49 /** @return True if the uniform only has to be set once per render call, false if the uniform must be set for each renderable. */ 50 boolean isGlobal (final BaseShader shader, final int inputID); 51 52 void set (final BaseShader shader, final int inputID, final Renderable renderable, final Attributes combinedAttributes); 53 } 54 55 public abstract static class GlobalSetter implements Setter { 56 @Override 57 public boolean isGlobal (final BaseShader shader, final int inputID) { 58 return true; 59 } 60 } 61 62 public abstract static class LocalSetter implements Setter { 63 @Override 64 public boolean isGlobal (final BaseShader shader, final int inputID) { 65 return false; 66 } 67 } 68 69 public static class Uniform implements Validator { 70 public final String alias; 71 public final long materialMask; 72 public final long environmentMask; 73 public final long overallMask; 74 75 public Uniform (final String alias, final long materialMask, final long environmentMask, final long overallMask) { 76 this.alias = alias; 77 this.materialMask = materialMask; 78 this.environmentMask = environmentMask; 79 this.overallMask = overallMask; 80 } 81 82 public Uniform (final String alias, final long materialMask, final long environmentMask) { 83 this(alias, materialMask, environmentMask, 0); 84 } 85 86 public Uniform (final String alias, final long overallMask) { 87 this(alias, 0, 0, overallMask); 88 } 89 90 public Uniform (final String alias) { 91 this(alias, 0, 0); 92 } 93 94 public boolean validate (final BaseShader shader, final int inputID, final Renderable renderable) { 95 final long matFlags = (renderable != null && renderable.material != null) ? renderable.material.getMask() : 0; 96 final long envFlags = (renderable != null && renderable.environment != null) ? renderable.environment.getMask() : 0; 97 return ((matFlags & materialMask) == materialMask) && ((envFlags & environmentMask) == environmentMask) 98 && (((matFlags | envFlags) & overallMask) == overallMask); 99 } 100 } 101 102 private final Array<String> uniforms = new Array<String>(); 103 private final Array<Validator> validators = new Array<Validator>(); 104 private final Array<Setter> setters = new Array<Setter>(); 105 private int locations[]; 106 private final IntArray globalUniforms = new IntArray(); 107 private final IntArray localUniforms = new IntArray(); 108 private final IntIntMap attributes = new IntIntMap(); 109 110 public ShaderProgram program; 111 public RenderContext context; 112 public Camera camera; 113 private Mesh currentMesh; 114 115 /** Register an uniform which might be used by this shader. Only possible prior to the call to init(). 116 * @return The ID of the uniform to use in this shader. */ 117 public int register (final String alias, final Validator validator, final Setter setter) { 118 if (locations != null) throw new GdxRuntimeException("Cannot register an uniform after initialization"); 119 final int existing = getUniformID(alias); 120 if (existing >= 0) { 121 validators.set(existing, validator); 122 setters.set(existing, setter); 123 return existing; 124 } 125 uniforms.add(alias); 126 validators.add(validator); 127 setters.add(setter); 128 return uniforms.size - 1; 129 } 130 131 public int register (final String alias, final Validator validator) { 132 return register(alias, validator, null); 133 } 134 135 public int register (final String alias, final Setter setter) { 136 return register(alias, null, setter); 137 } 138 139 public int register (final String alias) { 140 return register(alias, null, null); 141 } 142 143 public int register (final Uniform uniform, final Setter setter) { 144 return register(uniform.alias, uniform, setter); 145 } 146 147 public int register (final Uniform uniform) { 148 return register(uniform, null); 149 } 150 151 /** @return the ID of the input or negative if not available. */ 152 public int getUniformID (final String alias) { 153 final int n = uniforms.size; 154 for (int i = 0; i < n; i++) 155 if (uniforms.get(i).equals(alias)) return i; 156 return -1; 157 } 158 159 /** @return The input at the specified id. */ 160 public String getUniformAlias (final int id) { 161 return uniforms.get(id); 162 } 163 164 /** Initialize this shader, causing all registered uniforms/attributes to be fetched. */ 165 public void init (final ShaderProgram program, final Renderable renderable) { 166 if (locations != null) throw new GdxRuntimeException("Already initialized"); 167 if (!program.isCompiled()) throw new GdxRuntimeException(program.getLog()); 168 this.program = program; 169 170 final int n = uniforms.size; 171 locations = new int[n]; 172 for (int i = 0; i < n; i++) { 173 final String input = uniforms.get(i); 174 final Validator validator = validators.get(i); 175 final Setter setter = setters.get(i); 176 if (validator != null && !validator.validate(this, i, renderable)) 177 locations[i] = -1; 178 else { 179 locations[i] = program.fetchUniformLocation(input, false); 180 if (locations[i] >= 0 && setter != null) { 181 if (setter.isGlobal(this, i)) 182 globalUniforms.add(i); 183 else 184 localUniforms.add(i); 185 } 186 } 187 if (locations[i] < 0) { 188 validators.set(i, null); 189 setters.set(i, null); 190 } 191 } 192 if (renderable != null) { 193 final VertexAttributes attrs = renderable.meshPart.mesh.getVertexAttributes(); 194 final int c = attrs.size(); 195 for (int i = 0; i < c; i++) { 196 final VertexAttribute attr = attrs.get(i); 197 final int location = program.getAttributeLocation(attr.alias); 198 if (location >= 0) attributes.put(attr.getKey(), location); 199 } 200 } 201 } 202 203 @Override 204 public void begin (Camera camera, RenderContext context) { 205 this.camera = camera; 206 this.context = context; 207 program.begin(); 208 currentMesh = null; 209 for (int u, i = 0; i < globalUniforms.size; ++i) 210 if (setters.get(u = globalUniforms.get(i)) != null) setters.get(u).set(this, u, null, null); 211 } 212 213 private final IntArray tempArray = new IntArray(); 214 215 private final int[] getAttributeLocations (final VertexAttributes attrs) { 216 tempArray.clear(); 217 final int n = attrs.size(); 218 for (int i = 0; i < n; i++) { 219 tempArray.add(attributes.get(attrs.get(i).getKey(), -1)); 220 } 221 return tempArray.items; 222 } 223 224 private Attributes combinedAttributes = new Attributes(); 225 226 @Override 227 public void render (Renderable renderable) { 228 if (renderable.worldTransform.det3x3() == 0) return; 229 combinedAttributes.clear(); 230 if (renderable.environment != null) combinedAttributes.set(renderable.environment); 231 if (renderable.material != null) combinedAttributes.set(renderable.material); 232 render(renderable, combinedAttributes); 233 } 234 235 public void render (Renderable renderable, final Attributes combinedAttributes) { 236 for (int u, i = 0; i < localUniforms.size; ++i) 237 if (setters.get(u = localUniforms.get(i)) != null) setters.get(u).set(this, u, renderable, combinedAttributes); 238 if (currentMesh != renderable.meshPart.mesh) { 239 if (currentMesh != null) currentMesh.unbind(program, tempArray.items); 240 currentMesh = renderable.meshPart.mesh; 241 currentMesh.bind(program, getAttributeLocations(renderable.meshPart.mesh.getVertexAttributes())); 242 } 243 renderable.meshPart.render(program, false); 244 } 245 246 @Override 247 public void end () { 248 if (currentMesh != null) { 249 currentMesh.unbind(program, tempArray.items); 250 currentMesh = null; 251 } 252 program.end(); 253 } 254 255 @Override 256 public void dispose () { 257 program = null; 258 uniforms.clear(); 259 validators.clear(); 260 setters.clear(); 261 localUniforms.clear(); 262 globalUniforms.clear(); 263 locations = null; 264 } 265 266 /** Whether this Shader instance implements the specified uniform, only valid after a call to init(). */ 267 public final boolean has (final int inputID) { 268 return inputID >= 0 && inputID < locations.length && locations[inputID] >= 0; 269 } 270 271 public final int loc (final int inputID) { 272 return (inputID >= 0 && inputID < locations.length) ? locations[inputID] : -1; 273 } 274 275 public final boolean set (final int uniform, final Matrix4 value) { 276 if (locations[uniform] < 0) return false; 277 program.setUniformMatrix(locations[uniform], value); 278 return true; 279 } 280 281 public final boolean set (final int uniform, final Matrix3 value) { 282 if (locations[uniform] < 0) return false; 283 program.setUniformMatrix(locations[uniform], value); 284 return true; 285 } 286 287 public final boolean set (final int uniform, final Vector3 value) { 288 if (locations[uniform] < 0) return false; 289 program.setUniformf(locations[uniform], value); 290 return true; 291 } 292 293 public final boolean set (final int uniform, final Vector2 value) { 294 if (locations[uniform] < 0) return false; 295 program.setUniformf(locations[uniform], value); 296 return true; 297 } 298 299 public final boolean set (final int uniform, final Color value) { 300 if (locations[uniform] < 0) return false; 301 program.setUniformf(locations[uniform], value); 302 return true; 303 } 304 305 public final boolean set (final int uniform, final float value) { 306 if (locations[uniform] < 0) return false; 307 program.setUniformf(locations[uniform], value); 308 return true; 309 } 310 311 public final boolean set (final int uniform, final float v1, final float v2) { 312 if (locations[uniform] < 0) return false; 313 program.setUniformf(locations[uniform], v1, v2); 314 return true; 315 } 316 317 public final boolean set (final int uniform, final float v1, final float v2, final float v3) { 318 if (locations[uniform] < 0) return false; 319 program.setUniformf(locations[uniform], v1, v2, v3); 320 return true; 321 } 322 323 public final boolean set (final int uniform, final float v1, final float v2, final float v3, final float v4) { 324 if (locations[uniform] < 0) return false; 325 program.setUniformf(locations[uniform], v1, v2, v3, v4); 326 return true; 327 } 328 329 public final boolean set (final int uniform, final int value) { 330 if (locations[uniform] < 0) return false; 331 program.setUniformi(locations[uniform], value); 332 return true; 333 } 334 335 public final boolean set (final int uniform, final int v1, final int v2) { 336 if (locations[uniform] < 0) return false; 337 program.setUniformi(locations[uniform], v1, v2); 338 return true; 339 } 340 341 public final boolean set (final int uniform, final int v1, final int v2, final int v3) { 342 if (locations[uniform] < 0) return false; 343 program.setUniformi(locations[uniform], v1, v2, v3); 344 return true; 345 } 346 347 public final boolean set (final int uniform, final int v1, final int v2, final int v3, final int v4) { 348 if (locations[uniform] < 0) return false; 349 program.setUniformi(locations[uniform], v1, v2, v3, v4); 350 return true; 351 } 352 353 public final boolean set (final int uniform, final TextureDescriptor textureDesc) { 354 if (locations[uniform] < 0) return false; 355 program.setUniformi(locations[uniform], context.textureBinder.bind(textureDesc)); 356 return true; 357 } 358 359 public final boolean set (final int uniform, final GLTexture texture) { 360 if (locations[uniform] < 0) return false; 361 program.setUniformi(locations[uniform], context.textureBinder.bind(texture)); 362 return true; 363 } 364 } 365