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. */ validate(final BaseShader shader, final int inputID, final Renderable renderable)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. */ isGlobal(final BaseShader shader, final int inputID)50 boolean isGlobal (final BaseShader shader, final int inputID); 51 set(final BaseShader shader, final int inputID, final Renderable renderable, final Attributes combinedAttributes)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 isGlobal(final BaseShader shader, final int inputID)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 isGlobal(final BaseShader shader, final int inputID)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 Uniform(final String alias, final long materialMask, final long environmentMask, final long overallMask)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 Uniform(final String alias, final long materialMask, final long environmentMask)82 public Uniform (final String alias, final long materialMask, final long environmentMask) { 83 this(alias, materialMask, environmentMask, 0); 84 } 85 Uniform(final String alias, final long overallMask)86 public Uniform (final String alias, final long overallMask) { 87 this(alias, 0, 0, overallMask); 88 } 89 Uniform(final String alias)90 public Uniform (final String alias) { 91 this(alias, 0, 0); 92 } 93 validate(final BaseShader shader, final int inputID, final Renderable renderable)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. */ register(final String alias, final Validator validator, final Setter setter)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 register(final String alias, final Validator validator)131 public int register (final String alias, final Validator validator) { 132 return register(alias, validator, null); 133 } 134 register(final String alias, final Setter setter)135 public int register (final String alias, final Setter setter) { 136 return register(alias, null, setter); 137 } 138 register(final String alias)139 public int register (final String alias) { 140 return register(alias, null, null); 141 } 142 register(final Uniform uniform, final Setter setter)143 public int register (final Uniform uniform, final Setter setter) { 144 return register(uniform.alias, uniform, setter); 145 } 146 register(final Uniform uniform)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. */ getUniformID(final String alias)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. */ getUniformAlias(final int 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. */ init(final ShaderProgram program, final Renderable renderable)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 begin(Camera camera, RenderContext context)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 getAttributeLocations(final VertexAttributes attrs)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 render(Renderable renderable)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 render(Renderable renderable, final Attributes combinedAttributes)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 end()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 dispose()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(). */ has(final int inputID)267 public final boolean has (final int inputID) { 268 return inputID >= 0 && inputID < locations.length && locations[inputID] >= 0; 269 } 270 loc(final int inputID)271 public final int loc (final int inputID) { 272 return (inputID >= 0 && inputID < locations.length) ? locations[inputID] : -1; 273 } 274 set(final int uniform, final Matrix4 value)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 set(final int uniform, final Matrix3 value)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 set(final int uniform, final Vector3 value)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 set(final int uniform, final Vector2 value)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 set(final int uniform, final Color value)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 set(final int uniform, final float value)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 set(final int uniform, final float v1, final float v2)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 set(final int uniform, final float v1, final float v2, final float v3)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 set(final int uniform, final float v1, final float v2, final float v3, final float v4)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 set(final int uniform, final int value)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 set(final int uniform, final int v1, final int v2)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 set(final int uniform, final int v1, final int v2, final int v3)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 set(final int uniform, final int v1, final int v2, final int v3, final int v4)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 set(final int uniform, final TextureDescriptor textureDesc)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 set(final int uniform, final GLTexture texture)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