package loon.opengl.d3d.shaders;
import loon.action.camera.BaseCamera;
import loon.canvas.LColor;
import loon.geom.Matrix3;
import loon.geom.Matrix4;
import loon.geom.Vector2f;
import loon.geom.Vector3f;
import loon.opengl.ShaderProgram;
import loon.opengl.d3d.RenderContext;
import loon.opengl.d3d.Renderable;
import loon.opengl.d3d.Shader;
import loon.utils.TArray;
public abstract class BaseShader implements Shader {
public final static int VERTEX_ATTRIBUTE = 1;
public final static int GLOBAL_UNIFORM = 2;
public final static int LOCAL_UNIFORM = 3;
public static class Input {
public interface Setter {
void set(BaseShader shader, ShaderProgram program, Input input, BaseCamera camera, RenderContext context, Renderable renderable);
}
public final int scope;
public final String name;
public final long materialFlags;
public final long vertexFlags;
public final long userFlags;
public final Setter setter;
public int location = -1;
public boolean compare(final long materialMask, final long vertexMask, final long userMask) {
return (((materialMask & this.materialFlags) == this.materialFlags) &&
((vertexMask & this.vertexFlags) == this.vertexFlags) &&
((userMask & this.userFlags) == this.userFlags));
}
public Input(final int scope, final String name, final long materialFlags, final long vertexFlags, final long userFlags, final Setter setter) {
this.scope = scope;
this.name = name;
this.materialFlags = materialFlags;
this.vertexFlags = vertexFlags;
this.userFlags = userFlags;
this.setter = setter;
}
public Input(final int scope, final String name, final long materialFlags, final long vertexFlags, final long userFlags) {
this(scope, name, materialFlags, vertexFlags, userFlags, null);
}
public Input(final int scope, final String name, final long materialFlags, final long vertexFlags, final Setter setter) {
this(scope, name, materialFlags, vertexFlags, 0, setter);
}
public Input(final int scope, final String name, final long materialFlags, final long vertexFlags) {
this(scope, name, materialFlags, vertexFlags, 0);
}
public Input(final int scope, final String name, final long materialFlags, final Setter setter) {
this(scope, name, materialFlags, 0, 0, setter);
}
public Input(final int scope, final String name, final long materialFlags) {
this(scope, name, materialFlags, 0, 0);
}
public Input(final int scope, final String name, final Setter setter) {
this(scope, name, 0, 0, 0, setter);
}
public Input(final int scope, final String name) {
this(scope, name, 0, 0, 0);
}
}
private final TArray<Input> inputs = new TArray<Input>();
public final TArray<Input> vertexAttributes = new TArray<Input>();
public final TArray<Input> globalUniforms = new TArray<Input>();
public final TArray<Input> localUniforms = new TArray<Input>();
public ShaderProgram program;
public RenderContext context;
public BaseCamera camera;
public Input register(final Input input) {
if (program != null)
throw new RuntimeException("Cannot register input after initialization");
final Input existing = getInput(input.name);
if (existing != null) {
if (existing.scope != input.scope)
throw new RuntimeException(input.name+": An input with the same name but different scope is already registered.");
return existing;
}
inputs.add(input);
return input;
}
public Iterable<Input> getInputs() {
return inputs;
}
public Input getInput(final String alias) {
for (final Input input : inputs)
if (alias.equals(input.name))
return input;
return null;
}
public void init(final ShaderProgram program, final long materialMask, final long vertexMask, final long userMask) {
if (this.program != null)
throw new RuntimeException("Already initialized");
if (!program.isCompiled())
throw new RuntimeException(program.getLog());
this.program = program;
for (Input input : inputs) {
if (input.compare(materialMask, vertexMask, userMask)) {
if (input.scope == GLOBAL_UNIFORM) {
input.location = program.fetchUniformLocation(input.name, false);
if (input.location >= 0 && input.setter != null)
globalUniforms.add(input);
} else if (input.scope == LOCAL_UNIFORM) {
input.location = program.fetchUniformLocation(input.name, false);
if (input.location >= 0 && input.setter != null)
localUniforms.add(input);
} else if (input.scope == VERTEX_ATTRIBUTE) {
input.location = program.getAttributeLocation(input.name);
if (input.location >= 0)
vertexAttributes.add(input);
} else
input.location = -1;
} else
input.location = -1;
}
}
@Override
public void begin (BaseCamera camera, RenderContext context) {
this.camera = camera;
this.context = context;
program.begin();
for (final Input input : globalUniforms){
input.setter.set(this, program, input, camera, context, null);
}
}
@Override
public void render (Renderable renderable) {
for (final Input input : localUniforms){
input.setter.set(this, program, input, camera, context, renderable);
}
renderable.mesh.render(program, renderable.primitiveType, renderable.meshPartOffset, renderable.meshPartSize);
}
@Override
public void end () {
program.end();
}
@Override
public void close () {
program = null;
inputs.clear();
vertexAttributes.clear();
localUniforms.clear();
globalUniforms.clear();
}
public final boolean has(final Input input) {
return input.location >= 0;
}
public final boolean set(final Input uniform, final Matrix4 value) {
if (uniform.location < 0)
return false;
program.setUniformMatrix(uniform.location, value);
return true;
}
public final boolean set(final Input uniform, final Matrix3 value) {
if (uniform.location < 0)
return false;
program.setUniformMatrix(uniform.location, value);
return true;
}
public final boolean set(final Input uniform, final Vector3f value) {
if (uniform.location < 0)
return false;
program.setUniformf(uniform.location, value);
return true;
}
public final boolean set(final Input uniform, final Vector2f value) {
if (uniform.location < 0)
return false;
program.setUniformf(uniform.location, value);
return true;
}
public final boolean set(final Input uniform, final LColor value) {
if (uniform.location < 0)
return false;
program.setUniformf(uniform.location, value);
return true;
}
public final boolean set(final Input uniform, final float value) {
if (uniform.location < 0)
return false;
program.setUniformf(uniform.location, value);
return true;
}
public final boolean set(final Input uniform, final float v1, final float v2) {
if (uniform.location < 0)
return false;
program.setUniformf(uniform.location, v1, v2);
return true;
}
public final boolean set(final Input uniform, final float v1, final float v2, final float v3) {
if (uniform.location < 0)
return false;
program.setUniformf(uniform.location, v1, v2, v3);
return true;
}
public final boolean set(final Input uniform, final float v1, final float v2, final float v3, final float v4) {
if (uniform.location < 0)
return false;
program.setUniformf(uniform.location, v1, v2, v3, v4);
return true;
}
public final boolean set(final Input uniform, final int value) {
if (uniform.location < 0)
return false;
program.setUniformi(uniform.location, value);
return true;
}
public final boolean set(final Input uniform, final int v1, final int v2) {
if (uniform.location < 0)
return false;
program.setUniformi(uniform.location, v1, v2);
return true;
}
public final boolean set(final Input uniform, final int v1, final int v2, final int v3) {
if (uniform.location < 0)
return false;
program.setUniformi(uniform.location, v1, v2, v3);
return true;
}
public final boolean set(final Input uniform, final int v1, final int v2, final int v3, final int v4) {
if (uniform.location < 0)
return false;
program.setUniformi(uniform.location, v1, v2, v3, v4);
return true;
}
}