/* * 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 java.util.*; import java.awt.Color; import haven.MapMesh.Scan; import haven.Resource.Tile; import haven.Resource.Tileset; public class TerrainTile extends Tiler { public final GLState base; public final SNoise3 noise; public final Var[] var; public final Tileset transset; public static class Var { public GLState mat; public double thrl, thrh; public double nz; public Var(GLState mat, double thrl, double thrh, double nz) { this.mat = mat; this.thrl = thrl; this.thrh = thrh; this.nz = nz; } } private static final int sr = 12; public class Blend { final MapMesh m; final Scan vs, es; final float[][] bv; final boolean[][] en; private Blend(MapMesh m) { this.m = m; vs = new Scan(Coord.z.sub(sr, sr), m.sz.add(sr * 2 + 1, sr * 2 + 1)); float[][] buf1 = new float[var.length + 1][vs.l]; float[][] lwc = new float[var.length + 1][vs.l]; for(int i = 0; i < var.length + 1; i++) { for(int y = vs.ul.y; y < vs.br.y; y++) { for(int x = vs.ul.x; x < vs.br.x; x++) { lwc[i][vs.o(x, y)] = (float)noise.getr(0.5, 1.5, 32, x + m.ul.x, y + m.ul.y, i * 23); } } } setbase(buf1); for(int i = 0; i < sr; i++) { float[][] buf2 = new float[var.length + 1][vs.l]; for(int y = vs.ul.y; y < vs.br.y; y++) { for(int x = vs.ul.x; x < vs.br.x; x++) { for(int o = 0; o < var.length + 1; o++) { float s = buf1[o][vs.o(x, y)] * 4; float w = 4; float lw = lwc[o][vs.o(x, y)]; if(lw < 0) lw = lw * lw * lw; else lw = lw * lw; if(x > vs.ul.x) { s += buf1[o][vs.o(x - 1, y)] * lw; w += lw; } if(y > vs.ul.y) { s += buf1[o][vs.o(x, y - 1)] * lw; w += lw; } if(x < vs.br.x - 1) { s += buf1[o][vs.o(x + 1, y)] * lw; w += lw; } if(y < vs.br.y - 1) { s += buf1[o][vs.o(x, y + 1)] * lw; w += lw; } buf2[o][vs.o(x, y)] = s / w; } } } buf1 = buf2; } bv = buf1; for(int y = vs.ul.y; y < vs.br.y; y++) { for(int x = vs.ul.x; x < vs.br.x; x++) { for(int i = 0; i < var.length + 1; i++) { float v = bv[i][vs.o(x, y)]; v = v * 1.2f - 0.1f; if(v < 0) v = 0; else if(v > 1) v = 1; else v = 0.25f + (0.75f * v); bv[i][vs.o(x, y)] = v; } } } es = new Scan(Coord.z, m.sz); en = new boolean[var.length + 1][es.l]; for(int y = es.ul.y; y < es.br.y; y++) { for(int x = es.ul.x; x < es.br.x; x++) { boolean fall = false; for(int i = var.length; i >= 0; i--) { if(fall) { en[i][es.o(x, y)] = false; } else if((bv[i][vs.o(x , y )] < 0.001f) && (bv[i][vs.o(x + 1, y )] < 0.001f) && (bv[i][vs.o(x , y + 1)] < 0.001f) && (bv[i][vs.o(x + 1, y + 1)] < 0.001f)) { en[i][es.o(x, y)] = false; } else { en[i][es.o(x, y)] = true; if((bv[i][vs.o(x , y )] > 0.99f) && (bv[i][vs.o(x + 1, y )] > 0.99f) && (bv[i][vs.o(x , y + 1)] > 0.99f) && (bv[i][vs.o(x + 1, y + 1)] > 0.99f)) { fall = true; } } } } } } private void setbase(float[][] bv) { for(int y = vs.ul.y; y < vs.br.y - 1; y++) { for(int x = vs.ul.x; x < vs.br.x - 1; x++) { fall: { for(int i = var.length - 1; i >= 0; i--) { Var v = var[i]; double n = 0; for(double s = 64; s >= 8; s /= 2) n += noise.get(s, x + m.ul.x, y + m.ul.y, v.nz); if(((n / 2) >= v.thrl) && ((n / 2) <= v.thrh)) { bv[i + 1][vs.o(x, y)] = 1; bv[i + 1][vs.o(x + 1, y)] = 1; bv[i + 1][vs.o(x, y + 1)] = 1; bv[i + 1][vs.o(x + 1, y + 1)] = 1; break fall; } } bv[0][vs.o(x, y)] = 1; bv[0][vs.o(x + 1, y)] = 1; bv[0][vs.o(x, y + 1)] = 1; bv[0][vs.o(x + 1, y + 1)] = 1; } } } } } public final MapMesh.DataID<Blend> blend = new MapMesh.DataID<Blend>() { public Blend make(MapMesh m) { return(new Blend(m)); } }; @ResName("trn") public static class Factory implements Tiler.Factory { public Tiler create(int id, Resource.Tileset set) { Resource res = set.getres(); Tileset trans = null; Material base = null; Collection<Var> var = new LinkedList<Var>(); for(Object rdesc : set.ta) { Object[] desc = (Object[])rdesc; String p = (String)desc[0]; if(p.equals("base")) { int mid = (Integer)desc[1]; base = res.layer(Material.Res.class, mid).get(); } else if(p.equals("var")) { int mid = (Integer)desc[1]; double thrl, thrh; if(desc[2] instanceof Object[]) { thrl = (Float)((Object[])desc[2])[0]; thrh = (Float)((Object[])desc[2])[1]; } else { thrl = (Float)desc[2]; thrh = Double.MAX_VALUE; } double nz = (res.name.hashCode() * mid * 8129) % 10000; var.add(new Var(res.layer(Material.Res.class, mid).get(), thrl, thrh, nz)); } else if(p.equals("trans")) { Resource tres = Resource.load((String)desc[1], (Integer)desc[2]); trans = tres.layer(Resource.tileset); } } return(new TerrainTile(id, res.name.hashCode(), base, var.toArray(new Var[0]), trans)); } } public TerrainTile(int id, long nseed, GLState base, Var[] var, Tileset transset) { super(id); this.noise = new SNoise3(nseed); this.base = GLState.compose(base, States.vertexcolor); for(Var v : this.var = var) v.mat = GLState.compose(v.mat, States.vertexcolor); this.transset = transset; } public class Plane extends MapMesh.Shape { public Coord lc; public MapMesh.SPoint[] vrt; public Coord3f[] tc; public int[] alpha; public Plane(MapMesh m, MapMesh.Surface surf, Coord sc, int z, GLState mat, int[] alpha) { m.super(z, mat); this.lc = new Coord(sc); vrt = surf.fortile(sc); float fac = 25f / 4f; tc = new Coord3f[] { new Coord3f((sc.x + 0) / fac, (sc.y + 0) / fac, 0), new Coord3f((sc.x + 0) / fac, (sc.y + 1) / fac, 0), new Coord3f((sc.x + 1) / fac, (sc.y + 1) / fac, 0), new Coord3f((sc.x + 1) / fac, (sc.y + 0) / fac, 0), }; m.data(BumpMap.MapTangents.id); this.alpha = alpha; } public MeshBuf.Vertex mkvert(MeshBuf buf, int n) { MeshBuf.Vertex v = buf.new Vertex(vrt[n].pos, vrt[n].nrm); buf.layer(MeshBuf.tex).set(v, tc[n]); buf.layer(MeshBuf.col).set(v, new Color(255, 255, 255, alpha[n])); return(v); } public void build(MeshBuf buf) { MeshBuf.Vertex v1 = mkvert(buf, 0); MeshBuf.Vertex v2 = mkvert(buf, 1); MeshBuf.Vertex v3 = mkvert(buf, 2); MeshBuf.Vertex v4 = mkvert(buf, 3); m().data(BumpMap.MapTangents.id).set(buf, lc, v1, v2, v3, v4); MapMesh.splitquad(buf, v1, v2, v3, v4); } } public void lay(MapMesh m, Random rnd, Coord lc, Coord gc) { Blend b = m.data(blend); for(int i = 0; i < var.length + 1; i++) { GLState mat = (i == 0)?base:(var[i - 1].mat); if(b.en[i][b.es.o(lc)]) new Plane(m, m.gnd(), 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), }); } } public class TransPlane extends Plane { public Coord3f[] cc; public TransPlane(MapMesh m, MapMesh.Surface surf, Coord sc, int z, GLState mat, int[] alpha, Tex tex) { super(m, surf, sc, z, mat, alpha); Coord s = tex.sz(); cc = new Coord3f[] { new Coord3f(tex.tcx(0), tex.tcy(0), 0), new Coord3f(tex.tcx(0), tex.tcy(s.y), 0), new Coord3f(tex.tcx(s.x), tex.tcy(s.y), 0), new Coord3f(tex.tcx(s.x), tex.tcy(0), 0), }; } public MeshBuf.Vertex mkvert(MeshBuf buf, int n) { MeshBuf.Vertex v = super.mkvert(buf, n); buf.layer(AlphaTex.lclip).set(v, cc[n]); return(v); } } private final static Map<TexGL, AlphaTex> transtex = new WeakHashMap<TexGL, AlphaTex>(); private final static IDSet<GLState> transmats = new IDSet<GLState>(); private void laytrans(MapMesh m, Coord lc, int z, Tile t) { Blend b = m.data(blend); for(int i = 0; i < var.length + 1; i++) { GLState mat = (i == 0)?base:(var[i - 1].mat); Tex tt = t.tex(); TexGL gt; if(tt instanceof TexGL) gt = (TexGL)tt; else if((tt instanceof TexSI) && (((TexSI)tt).parent instanceof TexGL)) gt = (TexGL)((TexSI)tt).parent; else throw(new RuntimeException("Cannot use texture for transitions: " + tt)); AlphaTex alpha; synchronized(transtex) { if((alpha = transtex.get(gt)) == null) transtex.put(gt, alpha = new AlphaTex(gt, 0.01f)); } mat = transmats.intern(GLState.compose(mat, alpha)); if(b.en[i][b.es.o(lc)]) new TransPlane(m, m.gnd(), lc, z + 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), }, tt); } } public void trans(MapMesh m, Random rnd, Tiler gt, Coord lc, Coord gc, int z, int bmask, int cmask) { if(transset == null) return; if(m.map.gettile(gc) <= id) return; if((transset.btrans != null) && (bmask > 0)) laytrans(m, lc, z, transset.btrans[bmask - 1].pick(rnd)); if((transset.ctrans != null) && (cmask > 0)) laytrans(m, lc, z, transset.ctrans[cmask - 1].pick(rnd)); } }