/*
* This file is part of the Haven & Hearth game client.
* Copyright (C) 2009 Fredrik Tolf <fredrik@dolda2000.com>, and
* Björn Johannessen <johannessen.bjorn@gmail.com>
*
* Redistribution and/or modification of this file is subject to the
* terms of the GNU Lesser General Public License, version 3, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Other parts of this source tree adhere to other copying
* rights. Please see the file `COPYING' in the root directory of the
* source tree for details.
*
* A copy the GNU Lesser General Public License is distributed along
* with the source tree of which this file is a part in the file
* `doc/LPGL-3'. If it is missing for any reason, please see the Free
* Software Foundation's website at <http://www.fsf.org/>, or write
* to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA
*/
package haven;
import java.util.*;
import javax.media.opengl.*;
import haven.glsl.*;
import static haven.glsl.Cons.*;
import static haven.glsl.Function.PDir.*;
import static haven.glsl.Type.*;
public class ShadowMap extends GLState implements GLState.GlobalState, GLState.Global {
public final static Slot<ShadowMap> smap = new Slot<ShadowMap>(Slot.Type.DRAW, ShadowMap.class, Light.lighting);
public DirLight light;
public final TexE lbuf;
private final Projection lproj;
private final DirCam lcam;
private final FBView tgt;
private final static Matrix4f texbias = new Matrix4f(0.5f, 0.0f, 0.0f, 0.5f,
0.0f, 0.5f, 0.0f, 0.5f,
0.0f, 0.0f, 0.5f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f);
private final List<RenderList.Slot> parts = new ArrayList<RenderList.Slot>();
private int slidx;
private Matrix4f txf;
public ShadowMap(Coord res, float size, float depth, float dthr) {
lbuf = new TexE(res, GL2.GL_DEPTH_COMPONENT, GL2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT);
lbuf.magfilter = GL.GL_LINEAR;
lbuf.wrapmode = GL2.GL_CLAMP;
shader = new Shader(1.0 / res.x, 1.0 / res.y, 4, dthr / depth);
shaders = new ShaderMacro[] {shader};
lproj = Projection.ortho(-size, size, -size, size, 1, depth);
lcam = new DirCam();
tgt = new FBView(new GLFrameBuffer((TexGL)null, lbuf), GLState.compose(lproj, lcam));
}
private final Rendered scene = new Rendered() {
public void draw(GOut g) {}
public boolean setup(RenderList rl) {
GLState.Buffer buf = new GLState.Buffer(rl.cfg);
for(RenderList.Slot s : parts) {
rl.state().copy(buf);
s.os.copy(buf, GLState.Slot.Type.GEOM);
rl.add2(s.r, buf);
}
return(false);
}
};
public void setpos(Coord3f base, Coord3f dir) {
lcam.base = base;
lcam.dir = dir;
}
public void dispose() {
lbuf.dispose();
tgt.dispose();
}
public void prerender(RenderList rl, GOut g) {
parts.clear();
Light.LightList ll = null;
Camera cam = null;
for(RenderList.Slot s : rl.slots()) {
if(!s.d)
continue;
if((s.os.get(smap) != this) || (s.os.get(Light.lighting) == null))
continue;
if(ll == null) {
PView.RenderState rs = s.os.get(PView.wnd);
cam = s.os.get(PView.cam);
ll = s.os.get(Light.lights);
}
parts.add(s);
}
slidx = -1;
for(int i = 0; i < ll.ll.size(); i++) {
if(ll.ll.get(i) == light) {
slidx = i;
break;
}
}
Matrix4f cm = Transform.rxinvert(cam.fin(Matrix4f.id));
/*
txf = cm;
barda(txf);
txf = lcam.fin(Matrix4f.id).mul(txf);
barda(txf);
txf = lproj.fin(Matrix4f.id).mul(txf);
barda(txf);
txf = texbias.mul(txf);
barda(txf);
*/
txf = texbias
.mul(lproj.fin(Matrix4f.id))
.mul(lcam.fin(Matrix4f.id))
.mul(cm);
tgt.render(scene, g);
}
/*
static void barda(Matrix4f m) {
float[] a = m.mul4(new float[] {0, 0, 0, 1});
System.err.println(String.format("(%f, %f, %f, %f)", a[0], a[1], a[2], a[3]));
}
*/
public Global global(RenderList rl, Buffer ctx) {return(this);}
public void postsetup(RenderList rl) {}
public void postrender(RenderList rl, GOut g) {
/* g.image(lbuf, Coord.z, g.sz); */
}
public void prep(Buffer buf) {
buf.put(smap, this);
}
public static class Shader implements ShaderMacro {
public static final Uniform txf = new Uniform(MAT4), sl = new Uniform(INT), map = new Uniform(SAMPLER2D);
public static final AutoVarying stc = new AutoVarying(VEC4) {
public Expression root(VertexContext vctx) {
return(mul(txf.ref(), vctx.eyev.depref()));
}
};
public final Function.Def shcalc;
public Shader(final double xd, final double yd, final int res, final double thr) {
shcalc = new Function.Def(FLOAT) {
{
LValue sdw = code.local(FLOAT, l(0.0)).ref();
Expression mapc = code.local(VEC3, div(pick(stc.ref(), "xyz"), pick(stc.ref(), "w"))).ref();
double xr = xd * (res - 1), yr = yd * (res - 1);
boolean unroll = false;
if(!unroll) {
LValue xo = code.local(FLOAT, null).ref();
LValue yo = code.local(FLOAT, null).ref();
code.add(new For(ass(yo, l(-yr / 2)), lt(yo, l((yr / 2) + (yd / 2))), aadd(yo, l(yd)),
new For(ass(xo, l(-xr / 2)), lt(xo, l((xr / 2) + (xd / 2))), aadd(xo, l(xd)),
new If(gt(add(pick(texture2D(map.ref(), add(pick(mapc, "xy"), vec2(xo, yo))), "z"), l(thr)), pick(mapc, "z")),
stmt(aadd(sdw, l(1.0 / (res * res))))))));
} else {
for(double yo = -yr / 2; yo < (yr / 2) + (yd / 2); yo += yd) {
for(double xo = -xr / 2; xo < (xr / 2) + (xd / 2); xo += xd) {
code.add(new If(gt(add(pick(texture2D(map.ref(), add(pick(mapc, "xy"), vec2(l(xo), l(yo)))), "z"), l(thr)), pick(mapc, "z")),
stmt(aadd(sdw, l(1.0 / (res * res))))));
}
}
}
code.add(new Return(sdw));
}
};
}
public void modify(ProgramContext prog) {
final Phong ph = prog.getmod(Phong.class);
if((ph == null) || !ph.pfrag)
return;
ph.dolight.mod(new Runnable() {
public void run() {
ph.dolight.dcalc.add(new If(eq(sl.ref(), ph.dolight.i),
stmt(amul(ph.dolight.dl.var.ref(), shcalc.call()))),
ph.dolight.dcurs);
}
}, 0);
}
}
public final Shader shader;
private final ShaderMacro[] shaders;
// public boolean reqshaders() {return(true);}
public ShaderMacro[] shaders() {return(shaders);}
private TexUnit sampler;
public void apply(GOut g) {
sampler = g.st.texalloc();
if(g.st.prog != null) {
GL gl = g.gl;
sampler.act();
gl.glBindTexture(GL.GL_TEXTURE_2D, lbuf.glid(g));
reapply(g);
}
}
public void reapply(GOut g) {
GL2 gl = g.gl;
int mapu = g.st.prog.cuniform(Shader.map);
if(mapu >= 0) {
gl.glUniform1i(mapu, sampler.id);
gl.glUniformMatrix4fv(g.st.prog.uniform(Shader.txf), 1, false, txf.m, 0);
gl.glUniform1i(g.st.prog.uniform(Shader.sl), slidx);
}
}
public void unapply(GOut g) {
GL gl = g.gl;
sampler.act();
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
sampler.free(); sampler = null;
}
}