com.badlogic.gdx.graphics.g3d.particles.ParticleShader Maven / Gradle / Ivy
The 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 - 2025 Weber Informatics LLC | Privacy Policy