package loon.opengl.d3d.shaders;
import loon.action.camera.BaseCamera;
import loon.geom.Matrix3;
import loon.geom.Matrix4;
import loon.geom.Vector3f;
import loon.opengl.GL20;
import loon.opengl.Mesh;
import loon.opengl.ShaderProgram;
import loon.opengl.VertexAttributes;
import loon.opengl.VertexAttributes.Usage;
import loon.opengl.d3d.RenderContext;
import loon.opengl.d3d.Renderable;
import loon.opengl.d3d.Shader;
import loon.opengl.d3d.materials.BlendingAttribute;
import loon.opengl.d3d.materials.ColorAttribute;
import loon.opengl.d3d.materials.FloatAttribute;
import loon.opengl.d3d.materials.IntAttribute;
import loon.opengl.d3d.materials.Material;
import loon.opengl.d3d.materials.TextureAttribute;
import loon.opengl.light.AmbientCubemap;
import loon.opengl.light.DirectionalLight;
import loon.opengl.light.Lights;
import loon.opengl.light.PointLight;
import loon.utils.TArray;
public class DefaultShader extends BaseShader {
public final static String getDefaultVertexShader() {
return null;
}
public final static String getDefaultFragmentShader() {
return null;
}
protected static long implementedFlags = BlendingAttribute.Type
| TextureAttribute.Diffuse | ColorAttribute.Diffuse
| ColorAttribute.Specular | FloatAttribute.Shininess;
public static boolean ignoreUnimplemented = true;
public static int defaultCullFace = GL20.GL_BACK;
public static int defaultDepthFunc = GL20.GL_LEQUAL;
// Global uniforms
protected final Input u_projTrans = register(new Input(GLOBAL_UNIFORM,
"u_projTrans"));
protected final Input u_cameraPosition = register(new Input(GLOBAL_UNIFORM,
"u_cameraPosition"));
protected final Input u_cameraDirection = register(new Input(
GLOBAL_UNIFORM, "u_cameraDirection"));
protected final Input u_cameraUp = register(new Input(GLOBAL_UNIFORM,
"u_cameraUp"));
protected final Input u_worldTrans = register(new Input(LOCAL_UNIFORM,
"u_worldTrans"));
protected final Input u_normalMatrix = register(new Input(LOCAL_UNIFORM,
"u_normalMatrix", 0, Usage.Normal));
protected final Input u_bones = register(new Input(LOCAL_UNIFORM, "u_bones"));
protected final Input u_shininess = register(new Input(LOCAL_UNIFORM,
"u_shininess", FloatAttribute.Shininess));
protected final Input u_opacity = register(new Input(LOCAL_UNIFORM,
"u_opacity", BlendingAttribute.Type));
protected final Input u_diffuseColor = register(new Input(LOCAL_UNIFORM,
"u_diffuseColor", ColorAttribute.Diffuse));
protected final Input u_diffuseTexture = register(new Input(LOCAL_UNIFORM,
"u_diffuseTexture", TextureAttribute.Diffuse));
protected final Input u_specularColor = register(new Input(LOCAL_UNIFORM,
"u_specularColor", ColorAttribute.Specular));
protected final Input u_specularTexture = register(new Input(LOCAL_UNIFORM,
"u_specularTexture", TextureAttribute.Specular));
protected final Input u_normalTexture = register(new Input(LOCAL_UNIFORM,
"u_normalTexture", TextureAttribute.Normal));
protected final Input u_alphaTest = register(new Input(LOCAL_UNIFORM,
"u_alphaTest", FloatAttribute.AlphaTest));
protected final Input u_ambientLight = register(new Input(LOCAL_UNIFORM,
"u_ambientLight"));
protected final Input u_ambientCubemap = register(new Input(LOCAL_UNIFORM,
"u_ambientCubemap"));
protected final Input u_dirLights0color = register(new Input(LOCAL_UNIFORM,
"u_dirLights[0].color"));
protected final Input u_dirLights0direction = register(new Input(
LOCAL_UNIFORM, "u_dirLights[0].direction"));
protected final Input u_dirLights1color = register(new Input(LOCAL_UNIFORM,
"u_dirLights[1].color"));
protected final Input u_pointLights0color = register(new Input(
LOCAL_UNIFORM, "u_pointLights[0].color"));
protected final Input u_pointLights0position = register(new Input(
LOCAL_UNIFORM, "u_pointLights[0].position"));
protected final Input u_pointLights0intensity = register(new Input(
LOCAL_UNIFORM, "u_pointLights[0].intensity"));
protected final Input u_pointLights1color = register(new Input(
LOCAL_UNIFORM, "u_pointLights[1].color"));
protected final Input u_fogColor = register(new Input(LOCAL_UNIFORM,
"u_fogColor"));
protected int dirLightsLoc;
protected int dirLightsColorOffset;
protected int dirLightsDirectionOffset;
protected int dirLightsSize;
protected int pointLightsLoc;
protected int pointLightsColorOffset;
protected int pointLightsPositionOffset;
protected int pointLightsIntensityOffset;
protected int pointLightsSize;
protected boolean lighting;
protected boolean fog;
protected final AmbientCubemap ambientCubemap = new AmbientCubemap();
protected final DirectionalLight[] directionalLights;
protected final PointLight[] pointLights;
protected final float[] bones;
protected long materialMask;
protected long vertexMask;
public DefaultShader(final Material material,
final VertexAttributes attributes, boolean lighting, boolean fog,
int numDirectional, int numPoint, int numSpot, int numBones) {
this(getDefaultVertexShader(), getDefaultFragmentShader(), material,
attributes, lighting, fog, numDirectional, numPoint, numSpot,
numBones);
}
public DefaultShader(final long materialMask, final long vertexMask,
boolean lighting, boolean fog, int numDirectional, int numPoint,
int numSpot, int numBones) {
this(getDefaultVertexShader(), getDefaultFragmentShader(),
materialMask, vertexMask, lighting, fog, numDirectional,
numPoint, numSpot, numBones);
}
public DefaultShader(final String vertexShader,
final String fragmentShader, final Material material,
final VertexAttributes attributes, boolean lighting, boolean fog,
int numDirectional, int numPoint, int numSpot, int numBones) {
this(vertexShader, fragmentShader, material.getMask(),
getAttributesMask(attributes), lighting, fog, numDirectional,
numPoint, numSpot, numBones);
}
public DefaultShader(final String vertexShader,
final String fragmentShader, final long materialMask,
final long vertexMask, boolean lighting, boolean fog,
int numDirectional, int numPoint, int numSpot, int numBones) {
this(createPrefix(materialMask, vertexMask, lighting, fog,
numDirectional, numPoint, numSpot, numBones), vertexShader,
fragmentShader, materialMask, vertexMask, lighting, fog,
numDirectional, numPoint, numSpot, numBones);
}
public DefaultShader(final String prefix, final String vertexShader,
final String fragmentShader, final long materialMask,
final long vertexMask, boolean lighting, boolean fog,
int numDirectional, int numPoint, int numSpot, int numBones) {
this(new ShaderProgram(prefix + vertexShader, prefix + fragmentShader),
materialMask, vertexMask, lighting, fog, numDirectional,
numPoint, numSpot, numBones);
}
public DefaultShader(final ShaderProgram shaderProgram,
final long materialMask, final long vertexMask, boolean lighting,
boolean fog, int numDirectional, int numPoint, int numSpot,
int numBones) {
this.program = shaderProgram;
this.lighting = lighting;
this.fog = fog;
this.materialMask = materialMask;
this.vertexMask = vertexMask;
this.directionalLights = new DirectionalLight[lighting
&& numDirectional > 0 ? numDirectional : 0];
for (int i = 0; i < directionalLights.length; i++)
directionalLights[i] = new DirectionalLight();
this.pointLights = new PointLight[lighting && numPoint > 0 ? numPoint
: 0];
for (int i = 0; i < pointLights.length; i++)
pointLights[i] = new PointLight();
bones = new float[numBones > 0 ? numBones * 16 : 0];
if (!ignoreUnimplemented
&& (implementedFlags & materialMask) != materialMask)
throw new RuntimeException("Some attributes not implemented yet ("
+ materialMask + ")");
}
@Override
public void init() {
final ShaderProgram program = this.program;
this.program = null;
init(program, materialMask, vertexMask, 0);
dirLightsLoc = u_dirLights0color.location;
dirLightsColorOffset = u_dirLights0color.location - dirLightsLoc;
dirLightsDirectionOffset = u_dirLights0direction.location
- dirLightsLoc;
dirLightsSize = u_dirLights1color.location - dirLightsLoc;
pointLightsLoc = u_pointLights0color.location;
pointLightsColorOffset = u_pointLights0color.location - pointLightsLoc;
pointLightsPositionOffset = u_pointLights0position.location
- pointLightsLoc;
pointLightsIntensityOffset = u_pointLights0intensity.location
- pointLightsLoc;
pointLightsSize = u_pointLights1color.location - pointLightsLoc;
}
protected final static long tangentAttribute = Usage.Generic << 1;
protected final static long binormalAttribute = Usage.Generic << 2;
protected final static long blendAttributes[] = { Usage.Generic << 3,
Usage.Generic << 4, Usage.Generic << 5, Usage.Generic << 6,
Usage.Generic << 7, Usage.Generic << 8, Usage.Generic << 9,
Usage.Generic << 10 };
protected static long getAttributesMask(final VertexAttributes attributes) {
long result = 0;
int currentBone = 0;
final int n = attributes.size();
for (int i = 0; i < n; i++) {
long a = (long) attributes.get(i).usage;
if (a == Usage.BoneWeight)
a = blendAttributes[attributes.get(i).unit];
else if (a == Usage.Tangent)
a = tangentAttribute;
else if (a == Usage.BiNormal)
a = binormalAttribute;
result |= a;
}
return result;
}
private static String createPrefix(final long mask, final long attributes,
boolean lighting, boolean fog, int numDirectional, int numPoint,
int numSpot, int numBones) {
String prefix = "";
if (((attributes & Usage.Color) == Usage.Color)
|| ((attributes & Usage.ColorPacked) == Usage.ColorPacked))
prefix += "#define colorFlag\n";
if ((attributes & Usage.Normal) == Usage.Normal) {
prefix += "#define normalFlag\n";
if (lighting) {
prefix += "#define lightingFlag\n";
prefix += "#define ambientCubemapFlag\n";
prefix += "#define numDirectionalLights " + numDirectional
+ "\n";
prefix += "#define numPointLights " + numPoint + "\n";
if (fog) {
prefix += "#define fogFlag\n";
}
}
}
for (int i = 0; i < blendAttributes.length; i++) {
if ((attributes & blendAttributes[i]) == blendAttributes[i])
prefix += "#define boneWeight" + i + "Flag\n";
}
if ((attributes & tangentAttribute) == tangentAttribute)
prefix += "#define tangentFlag\n";
if ((attributes & binormalAttribute) == binormalAttribute)
prefix += "#define binormalFlag\n";
if ((mask & BlendingAttribute.Type) == BlendingAttribute.Type)
prefix += "#define " + BlendingAttribute.Alias + "Flag\n";
if ((mask & TextureAttribute.Diffuse) == TextureAttribute.Diffuse)
prefix += "#define " + TextureAttribute.DiffuseAlias + "Flag\n";
if ((mask & TextureAttribute.Normal) == TextureAttribute.Normal)
prefix += "#define " + TextureAttribute.NormalAlias + "Flag\n";
if ((mask & ColorAttribute.Diffuse) == ColorAttribute.Diffuse)
prefix += "#define " + ColorAttribute.DiffuseAlias + "Flag\n";
if ((mask & ColorAttribute.Specular) == ColorAttribute.Specular)
prefix += "#define " + ColorAttribute.SpecularAlias + "Flag\n";
if ((mask & FloatAttribute.Shininess) == FloatAttribute.Shininess)
prefix += "#define " + FloatAttribute.ShininessAlias + "Flag\n";
if ((mask & FloatAttribute.AlphaTest) == FloatAttribute.AlphaTest)
prefix += "#define " + FloatAttribute.AlphaTestAlias + "Flag\n";
if (numBones > 0)
prefix += "#define numBones " + numBones + "\n";
return prefix;
}
@Override
public boolean canRender(final Renderable renderable) {
return materialMask == renderable.material.getMask()
&& vertexMask == getAttributesMask(renderable.mesh
.getVertexAttributes())
&& (renderable.lights != null) == lighting
&& ((renderable.lights != null && renderable.lights.fog != null) == fog);
}
private final boolean can(final long flag) {
return (materialMask & flag) == flag;
}
@Override
public int compareTo(Shader other) {
if (other == null)
return -1;
if (other == this)
return 0;
return 0;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof DefaultShader) ? equals((DefaultShader) obj)
: false;
}
public boolean equals(DefaultShader obj) {
return (obj == this);
}
private Mesh currentMesh;
private Matrix3 normalMatrix = new Matrix3();
private BaseCamera camera;
private final static Matrix4 idtMatrix = new Matrix4();
@Override
public void begin(final BaseCamera camera, final RenderContext context) {
super.begin(camera, context);
if (defaultDepthFunc == 0)
context.setDepthTest(false, GL20.GL_LEQUAL);
else
context.setDepthTest(true, defaultDepthFunc);
/*
float fogDist = 1.09f / camera.far;
fogDist *= fogDist;
set(u_projTrans, camera.combined);
set(u_cameraPosition, camera.position.x, camera.position.y,
camera.position.z, fogDist);
set(u_cameraDirection, camera.direction);
set(u_cameraUp, camera.up);*/
for (final DirectionalLight dirLight : directionalLights)
dirLight.set(0, 0, 0, 0, -1, 0);
for (final PointLight pointLight : pointLights)
pointLight.set(0, 0, 0, 0, 0, 0, 0);
for (int i = 0; i < bones.length; i++)
bones[i] = idtMatrix.val[i % 16];
}
private void setWorldTransform(final Matrix4 value) {
set(u_worldTrans, value);
set(u_normalMatrix, normalMatrix.set(value));
}
@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);
setWorldTransform(renderable.worldTransform);
bindMaterial(renderable);
if (lighting)
bindLights(renderable);
if (currentMesh != renderable.mesh) {
if (currentMesh != null)
currentMesh.unbind(program);
renderable.mesh.setAutoBind(false);
(currentMesh = renderable.mesh).bind(program);
}
if (has(u_bones)) {
for (int i = 0; i < bones.length; i++) {
final int idx = i / 16;
bones[i] = (renderable.bones == null
|| idx >= renderable.bones.length || renderable.bones[idx] == null) ? idtMatrix.val[i % 16]
: renderable.bones[idx].val[i % 16];
}
program.setUniformMatrix4fv(u_bones.location, bones, 0,
bones.length);
}
super.render(renderable);
}
@Override
public void end() {
if (currentMesh != null) {
currentMesh.unbind(program);
currentMesh = null;
}
currentTextureAttribute = null;
currentMaterial = null;
super.end();
}
Material currentMaterial;
private final void bindMaterial(final Renderable renderable) {
if (currentMaterial == renderable.material)
return;
int cullFace = defaultCullFace;
currentMaterial = renderable.material;
for (final Material.Attribute attr : currentMaterial) {
final long t = attr.type;
if (BlendingAttribute.is(t)) {
context.setBlending(true,
((BlendingAttribute) attr).sourceFunction,
((BlendingAttribute) attr).destFunction);
set(u_opacity, ((BlendingAttribute) attr).opacity);
} else if (ColorAttribute.is(t)) {
ColorAttribute col = (ColorAttribute) attr;
if ((t & ColorAttribute.Diffuse) == ColorAttribute.Diffuse)
set(u_diffuseColor, col.color);
else if ((t & ColorAttribute.Specular) == ColorAttribute.Specular)
set(u_specularColor, col.color);
} else if (TextureAttribute.is(t)) {
final TextureAttribute tex = (TextureAttribute) attr;
if ((t & TextureAttribute.Diffuse) == TextureAttribute.Diffuse
&& has(u_diffuseTexture))
bindTextureAttribute(u_diffuseTexture.location, tex);
if ((t & TextureAttribute.Normal) == TextureAttribute.Normal
&& has(u_normalTexture))
bindTextureAttribute(u_normalTexture.location, tex);
// TODO else if (..)
} else if ((t & FloatAttribute.Shininess) == FloatAttribute.Shininess)
set(u_shininess, ((FloatAttribute) attr).value);
else if ((t & IntAttribute.CullFace) == IntAttribute.CullFace)
cullFace = ((IntAttribute) attr).value;
else if ((t & FloatAttribute.AlphaTest) == FloatAttribute.AlphaTest)
set(u_alphaTest, ((FloatAttribute) attr).value);
else if (!ignoreUnimplemented)
throw new RuntimeException("Unknown material attribute: "
+ attr.toString());
}
context.setCullFace(cullFace);
}
TextureAttribute currentTextureAttribute;
private final void bindTextureAttribute(final int uniform,
final TextureAttribute attribute) {
final int unit = context.textureBinder
.bind(attribute.textureDescription);
program.setUniformi(uniform, unit);
currentTextureAttribute = attribute;
}
private final Vector3f tmpV1 = new Vector3f();
private final void bindLights(final Renderable renderable) {
final Lights lights = renderable.lights;
final TArray<DirectionalLight> dirs = lights.directionalLights;
final TArray<PointLight> points = lights.pointLights;
if (has(u_ambientCubemap)) {
renderable.worldTransform.getTranslation(tmpV1);
ambientCubemap.set(lights.ambientLight);
for (int i = directionalLights.length; i < dirs.size; i++)
ambientCubemap.add(dirs.get(i).color, dirs.get(i).direction);
for (int i = pointLights.length; i < points.size; i++)
ambientCubemap.add(points.get(i).color, points.get(i).position,
tmpV1, points.get(i).intensity);
ambientCubemap.clamp();
program.setUniform3fv(u_ambientCubemap.location,
ambientCubemap.data, 0, ambientCubemap.data.length);
}
if (dirLightsLoc >= 0) {
for (int i = 0; i < directionalLights.length; i++) {
if (dirs == null || i >= dirs.size) {
if (directionalLights[i].color.r == 0f
&& directionalLights[i].color.g == 0f
&& directionalLights[i].color.b == 0f)
continue;
directionalLights[i].color.setColor(0f, 0f, 0f, 1f);
} else if (directionalLights[i].equals(dirs.get(i)))
continue;
else
directionalLights[i].set(dirs.get(i));
int idx = dirLightsLoc + i * dirLightsSize;
program.setUniformf(idx + dirLightsColorOffset,
directionalLights[i].color.r,
directionalLights[i].color.g,
directionalLights[i].color.b);
program.setUniformf(idx + dirLightsDirectionOffset,
directionalLights[i].direction);
}
}
if (pointLightsLoc >= 0) {
for (int i = 0; i < pointLights.length; i++) {
if (points == null || i >= points.size) {
if (pointLights[i].intensity == 0f)
continue;
pointLights[i].intensity = 0f;
} else if (pointLights[i].equals(points.get(i)))
continue;
else
pointLights[i].set(points.get(i));
int idx = pointLightsLoc + i * pointLightsSize;
program.setUniformf(idx + pointLightsColorOffset,
pointLights[i].color.r, pointLights[i].color.g,
pointLights[i].color.b);
program.setUniformf(idx + pointLightsPositionOffset,
pointLights[i].position);
if (pointLightsIntensityOffset >= 0)
program.setUniformf(idx + pointLightsIntensityOffset,
pointLights[i].intensity);
}
}
if (lights.fog != null) {
program.setUniformf(u_fogColor.location, lights.fog);
}
}
@Override
public void close() {
program.close();
}
}