All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.badlogic.gdx.graphics.g3d.particles.ParticleShader Maven / Gradle / Ivy

There is a newer version: 1.12.1
Show newest version
/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.graphics.g3d.particles;

import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g3d.Attribute;
import com.badlogic.gdx.graphics.g3d.Attributes;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Renderable;
import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.graphics.g3d.attributes.BlendingAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.DepthTestAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.IntAttribute;
import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
import com.badlogic.gdx.graphics.g3d.shaders.BaseShader;
import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader;
import com.badlogic.gdx.graphics.g3d.utils.RenderContext;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.GdxRuntimeException;

/** This is a custom shader to render the particles. Usually is not required, because the {@link DefaultShader} will be used
 * instead. This shader will be used when dealing with billboards using GPU mode or point sprites.
 * @author inferno */
public class ParticleShader extends BaseShader {
	public enum ParticleType {
		Billboard, Point
	}

	public static enum AlignMode {
		Screen, ViewPoint// , ParticleDirection
	}

	public static class Config {
		/** The uber vertex shader to use, null to use the default vertex shader. */
		public String vertexShader = null;
		/** The uber fragment shader to use, null to use the default fragment shader. */
		public String fragmentShader = null;
		public boolean ignoreUnimplemented = true;
		/** Set to 0 to disable culling */
		public int defaultCullFace = -1;
		/** Set to 0 to disable depth test */
		public int defaultDepthFunc = -1;
		public AlignMode align = AlignMode.Screen;
		public ParticleType type = ParticleType.Billboard;

		public Config () {
		}

		public Config (AlignMode align, ParticleType type) {
			this.align = align;
			this.type = type;
		}

		public Config (AlignMode align) {
			this.align = align;
		}

		public Config (ParticleType type) {
			this.type = type;
		}

		public Config (final String vertexShader, final String fragmentShader) {
			this.vertexShader = vertexShader;
			this.fragmentShader = fragmentShader;
		}
	}

	private static String defaultVertexShader = null;

	public static String getDefaultVertexShader () {
		if (defaultVertexShader == null)
			defaultVertexShader = Gdx.files.classpath("com/badlogic/gdx/graphics/g3d/particles/particles.vertex.glsl").readString();
		return defaultVertexShader;
	}

	private static String defaultFragmentShader = null;

	public static String getDefaultFragmentShader () {
		if (defaultFragmentShader == null) defaultFragmentShader = Gdx.files
			.classpath("com/badlogic/gdx/graphics/g3d/particles/particles.fragment.glsl").readString();
		return defaultFragmentShader;
	}

	protected static long implementedFlags = BlendingAttribute.Type | TextureAttribute.Diffuse;

	static final Vector3 TMP_VECTOR3 = new Vector3();

	public static class Inputs {
		public final static Uniform cameraRight = new Uniform("u_cameraRight");
		public final static Uniform cameraInvDirection = new Uniform("u_cameraInvDirection");
		public final static Uniform screenWidth = new Uniform("u_screenWidth");
		public final static Uniform regionSize = new Uniform("u_regionSize");
	}

	public static class Setters {
		public final static Setter cameraRight = new Setter() {
			@Override
			public boolean isGlobal (BaseShader shader, int inputID) {
				return true;
			}

			@Override
			public void set (BaseShader shader, int inputID, Renderable renderable, Attributes combinedAttributes) {
				shader.set(inputID, TMP_VECTOR3.set(shader.camera.direction).crs(shader.camera.up).nor());
			}
		};

		public final static Setter cameraUp = new Setter() {
			@Override
			public boolean isGlobal (BaseShader shader, int inputID) {
				return true;
			}

			@Override
			public void set (BaseShader shader, int inputID, Renderable renderable, Attributes combinedAttributes) {
				shader.set(inputID, TMP_VECTOR3.set(shader.camera.up).nor());
			}
		};

		public final static Setter cameraInvDirection = new Setter() {
			@Override
			public boolean isGlobal (BaseShader shader, int inputID) {
				return true;
			}

			@Override
			public void set (BaseShader shader, int inputID, Renderable renderable, Attributes combinedAttributes) {
				shader.set(inputID,
					TMP_VECTOR3.set(-shader.camera.direction.x, -shader.camera.direction.y, -shader.camera.direction.z).nor());
			}
		};
		public final static Setter cameraPosition = new Setter() {
			@Override
			public boolean isGlobal (BaseShader shader, int inputID) {
				return true;
			}

			@Override
			public void set (BaseShader shader, int inputID, Renderable renderable, Attributes combinedAttributes) {
				shader.set(inputID, shader.camera.position);
			}
		};
		public final static Setter screenWidth = new Setter() {
			@Override
			public boolean isGlobal (BaseShader shader, int inputID) {
				return true;
			}

			@Override
			public void set (BaseShader shader, int inputID, Renderable renderable, Attributes combinedAttributes) {
				shader.set(inputID, (float)Gdx.graphics.getWidth());
			}
		};
		public final static Setter worldViewTrans = new Setter() {
			final Matrix4 temp = new Matrix4();

			@Override
			public boolean isGlobal (BaseShader shader, int inputID) {
				return false;
			}

			@Override
			public void set (BaseShader shader, int inputID, Renderable renderable, Attributes combinedAttributes) {
				shader.set(inputID, temp.set(shader.camera.view).mul(renderable.worldTransform));
			}
		};
	}

	/** The renderable used to create this shader, invalid after the call to init */
	private Renderable renderable;
	private long materialMask;
	private long vertexMask;
	protected final Config config;
	/** Material attributes which are not required but always supported. */
	private final static long optionalAttributes = IntAttribute.CullFace | DepthTestAttribute.Type;

	public ParticleShader (final Renderable renderable) {
		this(renderable, new Config());
	}

	public ParticleShader (final Renderable renderable, final Config config) {
		this(renderable, config, createPrefix(renderable, config));
	}

	public ParticleShader (final Renderable renderable, final Config config, final String prefix) {
		this(renderable, config, prefix, config.vertexShader != null ? config.vertexShader : getDefaultVertexShader(),
			config.fragmentShader != null ? config.fragmentShader : getDefaultFragmentShader());
	}

	public ParticleShader (final Renderable renderable, final Config config, final String prefix, final String vertexShader,
		final String fragmentShader) {
		this(renderable, config, new ShaderProgram(prefix + vertexShader, prefix + fragmentShader));
	}

	public ParticleShader (final Renderable renderable, final Config config, final ShaderProgram shaderProgram) {
		this.config = config;
		this.program = shaderProgram;
		this.renderable = renderable;
		materialMask = renderable.material.getMask() | optionalAttributes;
		vertexMask = renderable.meshPart.mesh.getVertexAttributes().getMask();

		if (!config.ignoreUnimplemented && (implementedFlags & materialMask) != materialMask)
			throw new GdxRuntimeException("Some attributes not implemented yet (" + materialMask + ")");

		// Global uniforms
		register(DefaultShader.Inputs.viewTrans, DefaultShader.Setters.viewTrans);
		register(DefaultShader.Inputs.projViewTrans, DefaultShader.Setters.projViewTrans);
		register(DefaultShader.Inputs.projTrans, DefaultShader.Setters.projTrans);
		register(Inputs.screenWidth, Setters.screenWidth);
		register(DefaultShader.Inputs.cameraUp, Setters.cameraUp);
		register(Inputs.cameraRight, Setters.cameraRight);
		register(Inputs.cameraInvDirection, Setters.cameraInvDirection);
		register(DefaultShader.Inputs.cameraPosition, Setters.cameraPosition);

		// Object uniforms
		register(DefaultShader.Inputs.diffuseTexture, DefaultShader.Setters.diffuseTexture);
	}

	@Override
	public void init () {
		final ShaderProgram program = this.program;
		this.program = null;
		init(program, renderable);
		renderable = null;
	}

	public static String createPrefix (final Renderable renderable, final Config config) {
		String prefix = "";
		if (Gdx.app.getType() == ApplicationType.Desktop)
			prefix += "#version 120\n";
		else
			prefix += "#version 100\n";
		if (config.type == ParticleType.Billboard) {
			prefix += "#define billboard\n";
			if (config.align == AlignMode.Screen)
				prefix += "#define screenFacing\n";
			else if (config.align == AlignMode.ViewPoint) prefix += "#define viewPointFacing\n";
			// else if(config.align == AlignMode.ParticleDirection)
			// prefix += "#define paticleDirectionFacing\n";
		}
		return prefix;
	}

	@Override
	public boolean canRender (final Renderable renderable) {
		return (materialMask == (renderable.material.getMask() | optionalAttributes))
			&& (vertexMask == renderable.meshPart.mesh.getVertexAttributes().getMask());
	}

	@Override
	public int compareTo (Shader other) {
		if (other == null) return -1;
		if (other == this) return 0;
		return 0; // FIXME compare shaders on their impact on performance
	}

	@Override
	public boolean equals (Object obj) {
		return (obj instanceof ParticleShader) && equals((ParticleShader)obj);
	}

	public boolean equals (ParticleShader obj) {
		return (obj == this);
	}

	@Override
	public void begin (final Camera camera, final RenderContext context) {
		super.begin(camera, context);
	}

	@Override
	public void render (final Renderable renderable) {
		if (!renderable.material.has(BlendingAttribute.Type))
			context.setBlending(false, GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
		bindMaterial(renderable);
		super.render(renderable);
	}

	@Override
	public void end () {
		currentMaterial = null;
		super.end();
	}

	Material currentMaterial;

	protected void bindMaterial (final Renderable renderable) {
		if (currentMaterial == renderable.material) return;

		int cullFace = config.defaultCullFace == -1 ? GL20.GL_BACK : config.defaultCullFace;
		int depthFunc = config.defaultDepthFunc == -1 ? GL20.GL_LEQUAL : config.defaultDepthFunc;
		float depthRangeNear = 0f;
		float depthRangeFar = 1f;
		boolean depthMask = true;

		currentMaterial = renderable.material;
		for (final Attribute attr : currentMaterial) {
			final long t = attr.type;
			if (BlendingAttribute.is(t)) {
				context.setBlending(true, ((BlendingAttribute)attr).sourceFunction, ((BlendingAttribute)attr).destFunction);
			} else if ((t & DepthTestAttribute.Type) == DepthTestAttribute.Type) {
				DepthTestAttribute dta = (DepthTestAttribute)attr;
				depthFunc = dta.depthFunc;
				depthRangeNear = dta.depthRangeNear;
				depthRangeFar = dta.depthRangeFar;
				depthMask = dta.depthMask;
			} else if (!config.ignoreUnimplemented) throw new GdxRuntimeException("Unknown material attribute: " + attr.toString());
		}

		context.setCullFace(cullFace);
		context.setDepthTest(depthFunc, depthRangeNear, depthRangeFar);
		context.setDepthMask(depthMask);
	}

	@Override
	public void dispose () {
		program.dispose();
		super.dispose();
	}

	public int getDefaultCullFace () {
		return config.defaultCullFace == -1 ? GL20.GL_BACK : config.defaultCullFace;
	}

	public void setDefaultCullFace (int cullFace) {
		config.defaultCullFace = cullFace;
	}

	public int getDefaultDepthFunc () {
		return config.defaultDepthFunc == -1 ? GL20.GL_LEQUAL : config.defaultDepthFunc;
	}

	public void setDefaultDepthFunc (int depthFunc) {
		config.defaultDepthFunc = depthFunc;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy