Home | History | Annotate | Download | only in shaders
      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