/* * 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 java.util.*; import haven.*; import haven.glsl.*; import static haven.glsl.Cons.*; import haven.Resource.Tile; import haven.MapMesh.Surface; import haven.MapMesh.Scan; import javax.media.opengl.*; import java.awt.Color; public class WaterTile extends Tiler { public final int depth; private static final Material.Colors bcol = new Material.Colors(new Color(128, 128, 128), new Color(255, 255, 255), new Color(0, 0, 0), new Color(0, 0, 0)); public static class Bottom extends Surface { final MapMesh m; final boolean[] s; int[] ed; final Scan ss; public Bottom(MapMesh m) { m.super(); this.m = m; Coord sz = m.sz; MCache map = m.map; Scan ds = new Scan(new Coord(-10, -10), sz.add(21, 21)); ss = new Scan(new Coord(-9, -9), sz.add(19, 19)); int[] d = new int[ds.l]; s = new boolean[ss.l]; ed = new int[ss.l]; for(int y = ds.ul.y; y < ds.br.y; y++) { for(int x = ds.ul.y; x < ds.br.x; x++) { Tiler t = map.tiler(map.gettile(m.ul.add(x, y))); if(t instanceof WaterTile) d[ds.o(x, y)] = ((WaterTile)t).depth; else d[ds.o(x, y)] = 0; } } for(int y = ss.ul.y; y < ss.br.y; y++) { for(int x = ss.ul.x; x < ss.br.x; x++) { int td = d[ds.o(x, y)]; if(d[ds.o(x - 1, y - 1)] < td) td = d[ds.o(x - 1, y - 1)]; if(d[ds.o(x, y - 1)] < td) td = d[ds.o(x, y - 1)]; if(d[ds.o(x - 1, y)] < td) td = d[ds.o(x - 1, y)]; ed[ss.o(x, y)] = td; if(td == 0) s[ss.o(x, y)] = true; } } for(int i = 0; i < 8; i++) { int[] sd = new int[ss.l]; for(int y = ss.ul.y + 1; y < ss.br.y - 1; y++) { for(int x = ss.ul.x + 1; x < ss.br.x - 1; x++) { if(s[ss.o(x, y)]) { sd[ss.o(x, y)] = ed[ss.o(x, y)]; } else { sd[ss.o(x, y)] = ((ed[ss.o(x, y)] * 4) + ed[ss.o(x - 1, y)] + ed[ss.o(x + 1, y)] + ed[ss.o(x, y - 1)] + ed[ss.o(x, y + 1)]) / 8; } } } ed = sd; } for(int y = -1; y < sz.y + 2; y++) { for(int x = -1; x < sz.x + 2; x++) { spoint(new Coord(x, y)).pos.z -= ed[ss.o(x, y)]; } } } public int d(int x, int y) { return(ed[ss.o(x, y)]); } public void calcnrm() { super.calcnrm(); Coord c = new Coord(); for(c.y = 0; c.y <= m.sz.y; c.y++) { for(c.x = 0; c.x <= m.sz.x; c.x++) { if(s[ss.o(c)]) spoint(c).nrm = m.gnd().spoint(c).nrm; } } } public static final MapMesh.DataID<Bottom> id = MapMesh.makeid(Bottom.class); } public static final TexCube sky = new TexCube(Resource.loadimg("gfx/tiles/skycube")); static final TexI nrm = (TexI)Resource.loadtex("gfx/tiles/wn"); static { nrm.mipmap(); nrm.magfilter(GL.GL_LINEAR); } public static class SimpleSurface extends GLState.StandAlone { private static States.DepthOffset soff = new States.DepthOffset(2, 2); TexUnit tsky, tnrm; private SimpleSurface() { super(GLState.Slot.Type.DRAW, PView.cam, HavenPanel.global); } public void apply(GOut g) { GL2 gl = g.gl; (tsky = g.st.texalloc()).act(); gl.glTexGeni(GL2.GL_S, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); gl.glTexGeni(GL2.GL_T, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); gl.glTexGeni(GL2.GL_R, GL2.GL_TEXTURE_GEN_MODE, GL2.GL_REFLECTION_MAP); gl.glEnable(GL2.GL_TEXTURE_GEN_S); gl.glEnable(GL2.GL_TEXTURE_GEN_T); gl.glEnable(GL2.GL_TEXTURE_GEN_R); gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE); gl.glEnable(GL.GL_TEXTURE_CUBE_MAP); gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP, sky.glid(g)); gl.glColor4f(1, 1, 1, 0.5f); g.st.matmode(GL.GL_TEXTURE); gl.glPushMatrix(); g.st.cam.transpose().trim3(1).loadgl(gl); } public void unapply(GOut g) { GL2 gl = g.gl; tsky.act(); g.st.matmode(GL.GL_TEXTURE); gl.glPopMatrix(); gl.glDisable(GL.GL_TEXTURE_CUBE_MAP); gl.glDisable(GL2.GL_TEXTURE_GEN_S); gl.glDisable(GL2.GL_TEXTURE_GEN_T); gl.glDisable(GL2.GL_TEXTURE_GEN_R); gl.glColor3f(1, 1, 1); tsky.free(); tsky = null; } public void prep(Buffer buf) { buf.put(States.color, null); buf.put(Light.lighting, null); soff.prep(buf); super.prep(buf); } } public static class BetterSurface extends SimpleSurface { private final Uniform ssky = new Uniform(Type.SAMPLERCUBE); private final Uniform snrm = new Uniform(Type.SAMPLER2D); private final Uniform icam = new Uniform(Type.MAT3); private BetterSurface() { } private ShaderMacro[] shaders = { new ShaderMacro() { final AutoVarying skyc = new AutoVarying(Type.VEC3) { protected Expression root(VertexContext vctx) { return(mul(icam.ref(), reflect(MiscLib.vertedir(vctx).depref(), vctx.eyen.depref()))); } }; public void modify(final ProgramContext prog) { MiscLib.fragedir(prog.fctx); final ValBlock.Value nmod = prog.fctx.uniform.new Value(Type.VEC3) { public Expression root() { /* return(mul(sub(mix(pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.01), l(0.012))), mul(Cons.mod(MiscLib.time.ref(), l(2.0)), vec2(l(0.025), l(0.035))))), "rgb"), pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.019), l(0.018))), mul(Cons.mod(add(MiscLib.time.ref(), l(1.0)), l(2.0)), vec2(l(-0.035), l(-0.025))))), "rgb"), abs(sub(Cons.mod(MiscLib.time.ref(), l(2.0)), l(1.0)))), l(0.5)), vec3(l(1.0 / 16), l(1.0 / 16), l(1.0)))); */ return(mul(sub(mix(add(pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.01), l(0.012))), mul(MiscLib.time.ref(), vec2(l(0.025), l(0.035))))), "rgb"), pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.019), l(0.018))), mul(MiscLib.time.ref(), vec2(l(-0.035), l(-0.025))))), "rgb")), add(pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.01), l(0.012))), add(mul(MiscLib.time.ref(), vec2(l(0.025), l(0.035))), vec2(l(0.5), l(0.5))))), "rgb"), pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.019), l(0.018))), add(mul(MiscLib.time.ref(), vec2(l(-0.035), l(-0.025))), vec2(l(0.5), l(0.5))))), "rgb")), abs(sub(Cons.mod(MiscLib.time.ref(), l(2.0)), l(1.0)))), l(0.5 * 2)), vec3(l(1.0 / 16), l(1.0 / 16), l(1.0)))); /* return(mul(sub(add(pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.01), l(0.012))), mul(MiscLib.time.ref(), vec2(l(0.025), l(0.035))))), "rgb"), pick(texture2D(snrm.ref(), add(mul(pick(MiscLib.fragmapv.ref(), "st"), vec2(l(0.019), l(0.018))), mul(MiscLib.time.ref(), vec2(l(-0.035), l(-0.025))))), "rgb")), l(0.5 * 2)), vec3(l(1.0 / 16), l(1.0 / 16), l(1.0)))); */ /* return(mul(sub(pick(texture2D(snrm.ref(), mul(pick(MiscLib.fragmapv.ref(), "st"), l(0.005))), "rgb"), l(0.5)), vec3(l(1.0 / 32), l(1.0 / 32), l(1.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, "x"), vec3(l(1.0), l(0.0), l(0.0))), mul(pick(m, "y"), vec3(l(0.0), l(1.0), l(0.0))), mul(pick(m, "z"), in))); } }, -10); prog.fctx.fragcol.mod(new Macro1<Expression>() { public Expression expand(Expression in) { return(mul(in, textureCube(ssky.ref(), neg(mul(icam.ref(), reflect(MiscLib.fragedir(prog.fctx).depref(), MiscLib.frageyen(prog.fctx).depref())))), l(0.4))); } }, 0); } } }; public void reapply(GOut g) { GL2 gl = g.gl; gl.glUniform1i(g.st.prog.uniform(ssky), tsky.id); gl.glUniform1i(g.st.prog.uniform(snrm), tnrm.id); gl.glUniformMatrix3fv(g.st.prog.uniform(icam), 1, false, g.st.cam.transpose().trim3(), 0); } private void papply(GOut g) { GL2 gl = g.gl; gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE); (tsky = g.st.texalloc()).act(); gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP, sky.glid(g)); (tnrm = g.st.texalloc()).act(); gl.glBindTexture(GL.GL_TEXTURE_2D, nrm.glid(g)); reapply(g); } private void punapply(GOut g) { GL2 gl = g.gl; tsky.act(); gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP, 0); tnrm.act(); gl.glBindTexture(GL.GL_TEXTURE_2D, 0); tsky.free(); tsky = null; tnrm.free(); tnrm = null; gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA); } public ShaderMacro[] shaders() {return(shaders);} public boolean reqshaders() {return(true);} public void apply(GOut g) { if(g.st.prog == null) super.apply(g); else papply(g); } public void unapply(GOut g) { if(!g.st.usedprog) super.unapply(g); else punapply(g); } } public static final GLState surfmat = new GLState.Abstract() { final GLState s1 = new SimpleSurface(), s2 = new BetterSurface(); public void prep(Buffer buf) { if(buf.cfg.pref.wsurf.val) s2.prep(buf); else s1.prep(buf); } }; public static final MeshBuf.LayerID<MeshBuf.Vec1Layer> depthlayer = new MeshBuf.V1LayerID(BottomFog.depth); public static class BottomFog extends GLState.StandAlone { public static final double maxdepth = 25; public static final Color fogcolor = new Color(13, 38, 25); public static final Expression mfogcolor = mul(col3(fogcolor), pick(fref(idx(ProgramContext.gl_LightSource.ref(), MapView.amblight.ref()), "diffuse"), "rgb")); public static Function rgbmix = new Function.Def(Type.VEC4) {{ Expression a = param(PDir.IN, Type.VEC4).ref(); Expression b = param(PDir.IN, Type.VEC3).ref(); Expression m = param(PDir.IN, Type.FLOAT).ref(); code.add(new Return(vec4(mix(pick(a, "rgb"), b, m), pick(a, "a")))); }}; public static final Attribute depth = new Attribute(Type.FLOAT); public static final AutoVarying fragd = new AutoVarying(Type.FLOAT) { protected Expression root(VertexContext vctx) { return(depth.ref()); } }; private final ShaderMacro shaders[] = { new ShaderMacro() { public void modify(ProgramContext prog) { prog.fctx.fragcol.mod(new Macro1<Expression>() { public Expression expand(Expression in) { return(rgbmix.call(in, mfogcolor, min(div(fragd.ref(), l(maxdepth)), l(1.0)))); } }, 1000); } } }; private BottomFog() { super(Slot.Type.DRAW); } public void apply(GOut g) {} public void unapply(GOut g) {} public ShaderMacro[] shaders() {return(shaders);} public boolean reqshaders() {return(true);} public void prep(Buffer buf) { if(buf.cfg.pref.wsurf.val) super.prep(buf); } } public static final BottomFog waterfog = new BottomFog(); private static final GLState boff = new States.DepthOffset(4, 4); public static final GLState obfog = new GLState.StandAlone(GLState.Slot.Type.DRAW) { final AutoVarying fragd = new AutoVarying(Type.FLOAT) { protected Expression root(VertexContext vctx) { return(sub(pick(MiscLib.maploc.ref(), "z"), pick(vctx.mapv.depref(), "z"))); } }; final ShaderMacro[] shaders = { new ShaderMacro() { public void modify(ProgramContext prog) { prog.fctx.fragcol.mod(new Macro1<Expression>() { public Expression expand(Expression in) { return(BottomFog.rgbmix.call(in, BottomFog.mfogcolor, clamp(div(fragd.ref(), l(BottomFog.maxdepth)), l(0.0), l(1.0)))); } }, 1000); } } }; public void apply(GOut g) {} public void unapply(GOut g) {} public ShaderMacro[] shaders() {return(shaders);} public boolean reqshaders() {return(true);} }; @ResName("water") public static class Fac implements Factory { public Tiler create(int id, Resource.Tileset set) { int a = 0; int depth = (Integer)set.ta[a++]; Resource.Tileset ground = set; TerrainTile terrain = null; while(a < set.ta.length) { Object[] desc = (Object[])set.ta[a++]; String p = (String)desc[0]; if(p.equals("gnd")) { Resource gres = Resource.load((String)desc[1], (Integer)desc[2]); ground = gres.layer(Resource.tileset); } else if(p.equals("trn")) { Resource tres = Resource.load((String)desc[1], (Integer)desc[2]); Resource.Tileset tset = tres.layer(Resource.tileset); terrain = (TerrainTile)tset.tfac().create(-1, tset); } } if(terrain == null) return(new WaterTile(id, set, depth, ground)); else return(new TWaterTile(id, set, depth, terrain)); } } public final Resource.Tileset bottom; public final GLState mat; public WaterTile(int id, Resource.Tileset set, int depth, Resource.Tileset bottom) { super(id); this.depth = depth; this.bottom = bottom; TexGL tex = (TexGL)((TexSI)bottom.ground.pick(0).tex()).parent; mat = new Material(Light.deflight, bcol, tex.draw(), waterfog, boff); } public static class BottomPlane extends MapMesh.Plane { Bottom srf; Coord lc; public BottomPlane(MapMesh m, Bottom srf, Coord lc, int z, GLState mat, Tex tex) { m.super(srf.fortile(lc), z, mat, tex); this.srf = srf; this.lc = new Coord(lc); } public void build(MeshBuf buf) { MeshBuf.Tex ta = buf.layer(MeshBuf.tex); MeshBuf.Vec1Layer da = buf.layer(depthlayer); MeshBuf.Vertex v1 = buf.new Vertex(vrt[0].pos, vrt[0].nrm); MeshBuf.Vertex v2 = buf.new Vertex(vrt[1].pos, vrt[1].nrm); MeshBuf.Vertex v3 = buf.new Vertex(vrt[2].pos, vrt[2].nrm); MeshBuf.Vertex v4 = buf.new Vertex(vrt[3].pos, vrt[3].nrm); ta.set(v1, new Coord3f(tex.tcx(texx[0]), tex.tcy(texy[0]), 0.0f)); ta.set(v2, new Coord3f(tex.tcx(texx[1]), tex.tcy(texy[1]), 0.0f)); ta.set(v3, new Coord3f(tex.tcx(texx[2]), tex.tcy(texy[2]), 0.0f)); ta.set(v4, new Coord3f(tex.tcx(texx[3]), tex.tcy(texy[3]), 0.0f)); da.set(v1, (float)srf.d(lc.x, lc.y)); da.set(v2, (float)srf.d(lc.x, lc.y + 1)); da.set(v3, (float)srf.d(lc.x + 1, lc.y + 1)); da.set(v4, (float)srf.d(lc.x + 1, lc.y)); MapMesh.splitquad(buf, v1, v2, v3, v4); } } public void lay(MapMesh m, Random rnd, Coord lc, Coord gc) { Tile g = bottom.ground.pick(rnd); new BottomPlane(m, m.data(Bottom.id), lc, 0, mat, g.tex()); m.new Plane(m.gnd(), lc, 257, surfmat); } public void trans(MapMesh m, Random rnd, Tiler gt, Coord lc, Coord gc, int z, int bmask, int cmask) { if(m.map.gettile(gc) <= id) return; if((bottom.btrans != null) && (bmask > 0)) { Tile t = bottom.btrans[bmask - 1].pick(rnd); if(gt instanceof WaterTile) new BottomPlane(m, m.data(Bottom.id), lc, z, mat, t.tex()); else gt.layover(m, lc, gc, z, t); } if((bottom.ctrans != null) && (cmask > 0)) { Tile t = bottom.ctrans[cmask - 1].pick(rnd); if(gt instanceof WaterTile) new BottomPlane(m, m.data(Bottom.id), lc, z, mat, t.tex()); else gt.layover(m, lc, gc, z, t); } } public WaterTile(int id, Resource.Tileset set, int depth) { this(id, set, depth, set); } /* XXX: This is quite ugly, and tiling should generally be * reworked to allow it to be less so. */ public static class TWaterTile extends WaterTile { private final static IDSet<GLState> bmats = new IDSet<GLState>(); public final TerrainTile bottom; public TWaterTile(int id, Resource.Tileset set, int depth, TerrainTile bottom) { super(id, set, depth); this.bottom = bottom; } public class BottomPlane extends TerrainTile.Plane { float[] depth; public BottomPlane(MapMesh m, Bottom srf, Coord lc, int z, GLState mat, int[] alpha) { bottom.super(m, srf, lc, z, mat, alpha); this.depth = new float[] { srf.d(lc.x, lc.y), srf.d(lc.x, lc.y + 1), srf.d(lc.x + 1, lc.y + 1), srf.d(lc.x + 1, lc.y), }; } public MeshBuf.Vertex mkvert(MeshBuf buf, int n) { MeshBuf.Vertex v = super.mkvert(buf, n); buf.layer(depthlayer).set(v, depth[n]); return(v); } } public void lay(MapMesh m, Random rnd, Coord lc, Coord gc) { TerrainTile.Blend b = m.data(bottom.blend); for(int i = 0; i < bottom.var.length + 1; i++) { GLState mat = (i == 0)?bottom.base:(bottom.var[i - 1].mat); mat = bmats.intern(GLState.compose(mat, waterfog, boff)); if(b.en[i][b.es.o(lc)]) new BottomPlane(m, m.data(Bottom.id), lc, i, mat, new int[] { (int)(b.bv[i][b.vs.o(lc)] * 255), (int)(b.bv[i][b.vs.o(lc.add(0, 1))] * 255), (int)(b.bv[i][b.vs.o(lc.add(1, 1))] * 255), (int)(b.bv[i][b.vs.o(lc.add(1, 0))] * 255), }); } m.new Plane(m.gnd(), lc, 257, surfmat); } public void trans(MapMesh m, Random rnd, Tiler gt, Coord lc, Coord gc, int z, int bmask, int cmask) { } } public GLState drawstate(Glob glob, GLConfig cfg, Coord3f c) { if(cfg.pref.wsurf.val) return(obfog); return(null); } }