/*
* 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.resutil;
import haven.*;
import haven.glsl.*;
import static haven.glsl.Cons.*;
import static haven.glsl.Type.*;
import haven.MapMesh.Scan;
import java.util.*;
import javax.media.opengl.*;
public class BumpMap extends GLState {
public static final Slot<BumpMap> slot = new Slot<BumpMap>(Slot.Type.DRAW, BumpMap.class);
public static final Attribute tan = new Attribute(VEC3);
public static final Attribute bit = new Attribute(VEC3);
private static final Uniform ctex = new Uniform(SAMPLER2D);
public final TexGL tex;
private TexUnit sampler;
public BumpMap(TexGL tex) {
this.tex = tex;
}
private static final ShaderMacro[] shaders = {
new ShaderMacro() {
final AutoVarying tanc = new AutoVarying(VEC3) {
protected Expression root(VertexContext vctx) {
return(mul(vctx.gl_NormalMatrix.ref(), tan.ref()));
}
};
final AutoVarying bitc = new AutoVarying(VEC3) {
protected Expression root(VertexContext vctx) {
return(mul(vctx.gl_NormalMatrix.ref(), bit.ref()));
}
};
public void modify(ProgramContext prog) {
final ValBlock.Value nmod = prog.fctx.uniform.new Value(VEC3) {
public Expression root() {
return(mul(sub(pick(texture2D(ctex.ref(), Tex2D.texcoord.ref()), "rgb"),
l(0.5)), l(2.0)));
}
};
nmod.force();
MiscLib.frageyen(prog.fctx).mod(new Macro1<Expression>() {
public Expression expand(Expression in) {
Expression m = nmod.ref();
return(add(mul(pick(m, "s"), tanc.ref()),
mul(pick(m, "t"), bitc.ref()),
mul(pick(m, "p"), in)));
}
}, -100);
/*
prog.fctx.fragcol.mod(new Macro1<Expression>() {
public Expression expand(Expression in) {
return(mix(in, vec4(nmod.ref(), l(1.0)), l(0.5)));
}
}, 1000);
*/
}
}
};
public ShaderMacro[] shaders() {return(shaders);}
public boolean reqshaders() {return(true);}
public void reapply(GOut g) {
g.gl.glUniform1i(g.st.prog.uniform(ctex), sampler.id);
}
public void apply(GOut g) {
sampler = TexGL.lbind(g, tex);
reapply(g);
}
public void unapply(GOut g) {
GL2 gl = g.gl;
sampler.act();
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
sampler.free(); sampler = null;
}
public void prep(Buffer buf) {
buf.put(slot, this);
}
public static class MapTangents extends MapMesh.Hooks {
public final MapMesh m;
public final Scan s;
public final Coord3f[] tan, bit;
public MapTangents(MapMesh m) {
this.m = m;
s = new Scan(Coord.z, m.sz.add(1, 1));
tan = new Coord3f[s.l];
bit = new Coord3f[s.l];
for(int i = 0; i < s.l; i++) {
tan[i] = new Coord3f(0, 0, 0);
bit[i] = new Coord3f(0, 0, 0);
}
}
public void postcalcnrm(Random rnd) {
MapMesh.Surface gnd = m.gnd();
for(int y = s.ul.y; y < s.br.y; y++) {
for(int x = s.ul.x; x < s.br.x; x++) {
MapMesh.SPoint sp = gnd.spoint(new Coord(x, y));
Coord3f ct = Coord3f.yu.cmul(sp.nrm).norm();
Coord3f cb = sp.nrm.cmul(Coord3f.xu).norm();
Coord3f mt = tan[s.o(x, y)];
mt.x = ct.x; mt.y = ct.y; mt.z = ct.z;
Coord3f mb = bit[s.o(x, y)];
mb.x = cb.x; mb.y = cb.y; mb.z = cb.z;
}
}
}
public void set(MeshBuf buf, Coord lc, MeshBuf.Vertex v1, MeshBuf.Vertex v2, MeshBuf.Vertex v3, MeshBuf.Vertex v4) {
MeshBuf.Vec3Layer btan = buf.layer(ltan);
MeshBuf.Vec3Layer bbit = buf.layer(lbit);
btan.set(v1, tan[s.o(lc)]); bbit.set(v1, bit[s.o(lc)]);
btan.set(v2, tan[s.o(lc.add(0, 1))]); bbit.set(v2, bit[s.o(lc.add(0, 1))]);
btan.set(v3, tan[s.o(lc.add(1, 1))]); bbit.set(v3, bit[s.o(lc.add(1, 1))]);
btan.set(v4, tan[s.o(lc.add(1, 0))]); bbit.set(v4, bit[s.o(lc.add(1, 0))]);
}
public static final MapMesh.DataID<MapTangents> id = MapMesh.makeid(MapTangents.class);
}
public static final MeshBuf.LayerID<MeshBuf.Vec3Layer> ltan = new MeshBuf.V3LayerID(tan);
public static final MeshBuf.LayerID<MeshBuf.Vec3Layer> lbit = new MeshBuf.V3LayerID(bit);
@Material.ResName("bump")
public static class $bump implements Material.ResCons2 {
public void cons(final Resource res, List<GLState> states, List<Material.Res.Resolver> left, Object... args) {
final Resource tres;
final int tid;
int a = 0;
if(args[a] instanceof String) {
tres = Resource.load((String)args[a], (Integer)args[a + 1]);
tid = (Integer)args[a + 2];
a += 3;
} else {
tres = res;
tid = (Integer)args[a];
a += 1;
}
left.add(new Material.Res.Resolver() {
public void resolve(Collection<GLState> buf) {
TexR rt = tres.layer(TexR.class, tid);
if(rt == null)
throw(new RuntimeException(String.format("Specified texture %d for %s not found in %s", tid, res, tres)));
/* XXX: It is somewhat doubtful that this cast is really quite reasonable. */
buf.add(new BumpMap((TexGL)rt.tex()));
}
});
}
}
}