• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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