/*******************************************************************************
* Copyright 2012 bmanuel
*
* 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.bitfire.postprocessing.filters;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix3;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.bitfire.postprocessing.utils.FullscreenQuad;
/** The base class for any single-pass filter. */
@SuppressWarnings("unchecked")
public abstract class Filter<T> {
public interface Parameter {
String mnemonic();
int arrayElementSize();
}
protected static final FullscreenQuad quad = new FullscreenQuad();
protected static final int u_texture0 = 0;
protected static final int u_texture1 = 1;
protected static final int u_texture2 = 2;
protected static final int u_texture3 = 3;
protected Texture inputTexture = null;
protected FrameBuffer outputBuffer = null;
protected ShaderProgram program = null;
private boolean programBegan = false;
public Filter (ShaderProgram program) {
this.program = program;
}
public T setInput (Texture input) {
this.inputTexture = input;
return (T)this; // assumes T extends Filter
}
public T setInput (FrameBuffer input) {
return setInput(input.getColorBufferTexture());
}
public T setOutput (FrameBuffer output) {
this.outputBuffer = output;
return (T)this;
}
public void dispose () {
program.dispose();
}
public abstract void rebind ();
/*
* Sets the parameter to the specified value for this filter. This is for one-off operations since the shader is being bound
* and unbound once per call: for a batch-ready version of this fuction see and use setParams instead.
*/
// int
protected void setParam (Parameter param, int value) {
program.begin();
program.setUniformi(param.mnemonic(), value);
program.end();
}
// float
protected void setParam (Parameter param, float value) {
program.begin();
program.setUniformf(param.mnemonic(), value);
program.end();
}
// vec2
protected void setParam (Parameter param, Vector2 value) {
program.begin();
program.setUniformf(param.mnemonic(), value);
program.end();
}
// vec3
protected void setParam (Parameter param, Vector3 value) {
program.begin();
program.setUniformf(param.mnemonic(), value);
program.end();
}
// mat3
protected T setParam (Parameter param, Matrix3 value) {
program.begin();
program.setUniformMatrix(param.mnemonic(), value);
program.end();
return (T)this;
}
// mat4
protected T setParam (Parameter param, Matrix4 value) {
program.begin();
program.setUniformMatrix(param.mnemonic(), value);
program.end();
return (T)this;
}
// float[], vec2[], vec3[], vec4[]
protected T setParamv (Parameter param, float[] values, int offset, int length) {
program.begin();
switch (param.arrayElementSize()) {
case 4:
program.setUniform4fv(param.mnemonic(), values, offset, length);
break;
case 3:
program.setUniform3fv(param.mnemonic(), values, offset, length);
break;
case 2:
program.setUniform2fv(param.mnemonic(), values, offset, length);
break;
default:
case 1:
program.setUniform1fv(param.mnemonic(), values, offset, length);
break;
}
program.end();
return (T)this;
}
/** Sets the parameter to the specified value for this filter. When you are finished building the batch you shall signal it by
* invoking endParams(). */
// float
protected T setParams (Parameter param, float value) {
if (!programBegan) {
programBegan = true;
program.begin();
}
program.setUniformf(param.mnemonic(), value);
return (T)this;
}
// int version
protected T setParams (Parameter param, int value) {
if (!programBegan) {
programBegan = true;
program.begin();
}
program.setUniformi(param.mnemonic(), value);
return (T)this;
}
// vec2 version
protected T setParams (Parameter param, Vector2 value) {
if (!programBegan) {
programBegan = true;
program.begin();
}
program.setUniformf(param.mnemonic(), value);
return (T)this;
}
// vec3 version
protected T setParams (Parameter param, Vector3 value) {
if (!programBegan) {
programBegan = true;
program.begin();
}
program.setUniformf(param.mnemonic(), value);
return (T)this;
}
// mat3
protected T setParams (Parameter param, Matrix3 value) {
if (!programBegan) {
programBegan = true;
program.begin();
}
program.setUniformMatrix(param.mnemonic(), value);
return (T)this;
}
// mat4
protected T setParams (Parameter param, Matrix4 value) {
if (!programBegan) {
programBegan = true;
program.begin();
}
program.setUniformMatrix(param.mnemonic(), value);
return (T)this;
}
// float[], vec2[], vec3[], vec4[]
protected T setParamsv (Parameter param, float[] values, int offset, int length) {
if (!programBegan) {
programBegan = true;
program.begin();
}
switch (param.arrayElementSize()) {
case 4:
program.setUniform4fv(param.mnemonic(), values, offset, length);
break;
case 3:
program.setUniform3fv(param.mnemonic(), values, offset, length);
break;
case 2:
program.setUniform2fv(param.mnemonic(), values, offset, length);
break;
default:
case 1:
program.setUniform1fv(param.mnemonic(), values, offset, length);
break;
}
return (T)this;
}
/** Should be called after any one or more setParams method calls. */
protected void endParams () {
if (programBegan) {
program.end();
programBegan = false;
}
}
/** This method will get called just before a rendering operation occurs. */
protected abstract void onBeforeRender ();
public final void render () {
if (outputBuffer != null) {
outputBuffer.begin();
realRender();
outputBuffer.end();
} else {
realRender();
}
}
private void realRender () {
// gives a chance to filters to perform needed operations just before the rendering operation take place.
onBeforeRender();
program.begin();
quad.render(program);
program.end();
}
}