/* * 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.Resource.Tile; import haven.MapMesh.*; public class RidgeTile extends GroundTile { public final int[] breaks; public final Resource[] walls; public final Resource[] lcorn; public final Resource[] rcorn; public final Resource[] strans, c1trans, c2trans; public RidgeTile(int id, Resource.Tileset set, int[] breaks, Resource[] walls, Resource[] lcorn, Resource[] rcorn, Resource[] strans, Resource[] c1trans, Resource[] c2trans) { super(id, set); this.breaks = breaks; this.walls = walls; this.lcorn = lcorn; this.rcorn = rcorn; this.strans = strans; this.c1trans = c1trans; this.c2trans = c2trans; } public boolean[] breaks(MapMesh m, Coord gc, int diff) { int z00 = m.map.getz(gc), z10 = m.map.getz(gc.add(1, 0)), z01 = m.map.getz(gc.add(0, 1)), z11 = m.map.getz(gc.add(1, 1)); return(new boolean[] { Math.abs(z00 - z10) >= diff, Math.abs(z10 - z11) >= diff, Math.abs(z11 - z01) >= diff, Math.abs(z01 - z00) >= diff, }); } public boolean isend(MapMesh m, Coord gc, boolean[] b) { return(((b[0]?1:0) + (b[1]?1:0) + (b[2]?1:0) + (b[3]?1:0)) == 1); } public boolean isstraight(MapMesh m, Coord gc, boolean[] b) { return((b[0] && b[2] && !b[1] && !b[3]) || (b[1] && b[3] && !b[0] && !b[2])); } @SuppressWarnings("unchecked") private static <T> T[] shift(T[] a, int n) { T[] r = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), a.length); for(int i = 0; i < a.length; i++) r[(i + n) % a.length] = a[i]; return(r); } public void makewall(MapMesh m, Coord3f ul, Coord3f bl, Coord3f br, Coord3f ur, Resource wall, float w) { float hw = w / 2.0f; float xbx, xby; float lzof, lzsf, lzs; float rzof, rzsf, rzs; float tysf, tys; { double tx = br.x - bl.x, ty = br.y - bl.y; double lf = 1.0 / Math.sqrt((tx * tx) + (ty * ty)); xbx = (float)(tx * lf); xby = (float)(ty * lf); lzof = (float)((br.z - bl.z) * lf); lzsf = (float)((ur.z - br.z - ul.z + bl.z) * lf / 11.0); lzs = (float)((ul.z - bl.z) / 11.0); rzof = (float)((bl.z - br.z) * lf); rzsf = (float)((ul.z - bl.z - ur.z + br.z) * lf / 11.0); rzs = (float)((ur.z - br.z) / 11.0); tys = (int)(((ul.z - bl.z) + 5) / 11); tysf = (float)((((int)(((ur.z - br.z) + 5) / 11)) - tys) * lf); } float ybx = -xby, yby = xbx; for(FastMesh.MeshRes r : wall.layers(FastMesh.MeshRes.class)) { MeshBuf buf = MapMesh.Models.get(m, r.mat.get()); MeshBuf.Tex ta = buf.layer(MeshBuf.tex); MeshBuf.Vertex[] vs = buf.copy(r.m); for(MeshBuf.Vertex v : vs) { float x = v.pos.x, y = v.pos.y, z = v.pos.z; v.pos.x = (x * xbx) + (y * ybx) + bl.x; v.pos.y = (x * xby) + (y * yby) + bl.y; if(x < hw) { v.pos.z = (lzof * x) + ((lzs + (lzsf * x)) * z) + bl.z; } else { float X = w - x; v.pos.z = (rzof * X) + ((rzs + (rzsf * X)) * z) + br.z; } float nx = v.nrm.x, ny = v.nrm.y; v.nrm.x = (nx * xbx) + (ny * ybx); v.nrm.y = (nx * xby) + (ny * yby); ta.get(v).y = (tys + (tysf * x)) * ta.get(v).y; } } } public static class Ridges extends MapMesh.Hooks { public final MapMesh m; private final Tile[] tiles; public Ridges(MapMesh m) { this.m = m; this.tiles = new Tile[m.sz.x * m.sz.y]; } public class Tile { public TilePlane[] planes = new TilePlane[4]; int n; public class TilePlane { public SPoint[] vrt; public float u, l, b, r; public TilePlane(SPoint[] vrt) { this.vrt = vrt; u = l = 0; b = r = 1; planes[n++] = this; } } public void layover(int z, Resource.Tile tile) { int w = tile.tex().sz().x, h = tile.tex().sz().y; for(int i = 0; i < n; i++) { Plane p = m.new Plane(planes[i].vrt, z, tile.tex(), tile.t == 'g'); p.texrot(new Coord((int)(w * planes[i].l), (int)(h * planes[i].u)), new Coord((int)(w * planes[i].r), (int)(h * planes[i].b)), 0, false); } } } public Tile get(Coord c) { return(tiles[c.x + (m.sz.x * c.y)]); } public void set(Coord c, Tile t) { tiles[c.x + (m.sz.x * c.y)] = t; } public void postcalcnrm(Random rnd) { } } private static final MapMesh.DataID<Ridges> rid = MapMesh.makeid(Ridges.class); private static final int[] cwx = {0, 1, 1, 0}, cwy = {0, 0, 1, 1}, ecwx = { 0, 1, 0, -1}, ecwy = {-1, 0, 1, 0}; public void remapquad(Ridges.Tile.TilePlane p, int q) { p.u = cwy[q] * 0.5f; p.l = cwx[q] * 0.5f; p.b = p.u + 0.5f; p.r = p.l + 0.5f; } public void remaphalf(Ridges.Tile.TilePlane p, int fq) { int l = Math.min(cwx[fq], cwx[(fq + 1) % 4]), r = Math.max(cwx[fq], cwx[(fq + 1) % 4]) + 1; int t = Math.min(cwy[fq], cwy[(fq + 1) % 4]), b = Math.max(cwy[fq], cwy[(fq + 1) % 4]) + 1; p.u = t * 0.5f; p.l = l * 0.5f; p.b = b * 0.5f; p.r = r * 0.5f; } private void layend(MapMesh m, Random rnd, Coord lc, Coord gc, int dir) { Surface g = m.gnd(); SPoint bl = g.spoint(lc.add(cwx[dir], cwy[dir])), br = g.spoint(lc.add(cwx[(dir + 1) % 4], cwy[(dir + 1) % 4])), fr = g.spoint(lc.add(cwx[(dir + 2) % 4], cwy[(dir + 2) % 4])), fl = g.spoint(lc.add(cwx[(dir + 3) % 4], cwy[(dir + 3) % 4])); boolean cw = bl.pos.z > br.pos.z; SPoint bu = new SPoint(bl.pos.add(br.pos).mul(0.5f)); SPoint bb = new SPoint(bl.pos.add(br.pos).mul(0.5f)); SPoint fm = new SPoint(fl.pos.add(fr.pos).mul(0.5f)); Ridges r = m.data(rid); Ridges.Tile tile = r.new Tile(); Ridges.Tile.TilePlane left, right; SPoint[] uh; if(cw) { bu.pos.z = bl.pos.z; bb.pos.z = br.pos.z; left = tile.new TilePlane(uh = shift(new SPoint[] {fl, fm, bu, bl}, 5 - dir)); right = tile.new TilePlane( shift(new SPoint[] {fm, fr, br, bb}, 5 - dir)); } else { bu.pos.z = br.pos.z; bb.pos.z = bl.pos.z; left = tile.new TilePlane( shift(new SPoint[] {fl, fm, bb, bl}, 5 - dir)); right = tile.new TilePlane(uh = shift(new SPoint[] {fm, fr, br, bu}, 5 - dir)); } remaphalf(left , (dir + 3) % 4); remaphalf(right, (dir + 1) % 4); r.set(lc, tile); tile.layover(0, set.ground.pick(rnd)); m.new Plane(uh, 256, strans[rnd.nextInt(strans.length)].layer(Resource.imgc).tex(), false) .texrot(null, null, 1 + dir + (cw?2:0), false); if(cw) makewall(m, fm.pos, fm.pos, bb.pos, bu.pos, walls[rnd.nextInt(walls.length)], 11); else makewall(m, bu.pos, bb.pos, fm.pos, fm.pos, walls[rnd.nextInt(walls.length)], 11); } public void layend(MapMesh m, Random rnd, Coord lc, Coord gc, boolean[] b) { for(int dir = 0; dir < 4; dir++) { if(b[dir]) { layend(m, rnd, lc, gc, dir); return; } } } public void layridge(MapMesh m, Random rnd, Coord lc, Coord gc, boolean[] b) { int z00 = m.map.getz(gc), z10 = m.map.getz(gc.add(1, 0)), z01 = m.map.getz(gc.add(0, 1)), z11 = m.map.getz(gc.add(1, 1)); int dir = b[0]?((z00 > z10)?0:2):((z00 > z01)?1:3); boolean tb1 = m.map.tiler(m.map.gettile(gc.add(ecwx[dir], ecwy[dir]))) instanceof RidgeTile; boolean tb2 = m.map.tiler(m.map.gettile(gc.add(ecwx[(dir + 2) % 4], ecwy[(dir + 2) % 4]))) instanceof RidgeTile; if(!tb1 && !tb2) { /* XXX */ } else if(!tb1) { layend(m, rnd, lc, gc, (dir + 2) % 4); return; } else if(!tb2) { layend(m, rnd, lc, gc, dir); } Surface g = m.gnd(); SPoint ur = g.spoint(lc.add(cwx[dir], cwy[dir])), br = g.spoint(lc.add(cwx[(dir + 1) % 4], cwy[(dir + 1) % 4])), bl = g.spoint(lc.add(cwx[(dir + 2) % 4], cwy[(dir + 2) % 4])), ul = g.spoint(lc.add(cwx[(dir + 3) % 4], cwy[(dir + 3) % 4])); SPoint mlu = new SPoint(ul.pos.add(bl.pos).mul(0.5f)), mlb = new SPoint(ul.pos.add(bl.pos).mul(0.5f)), mru = new SPoint(ur.pos.add(br.pos).mul(0.5f)), mrb = new SPoint(ur.pos.add(br.pos).mul(0.5f)); mlu.pos.z = ul.pos.z; mru.pos.z = ur.pos.z; mlb.pos.z = bl.pos.z; mrb.pos.z = br.pos.z; Ridges r = m.data(rid); Ridges.Tile tile = r.new Tile(); Ridges.Tile.TilePlane upper = tile.new TilePlane(shift(new SPoint[] {ul, mlu, mru, ur}, 5 - dir)); Ridges.Tile.TilePlane lower = tile.new TilePlane(shift(new SPoint[] {mlb, bl, br, mrb}, 5 - dir)); remaphalf(upper, (dir + 3) % 4); remaphalf(lower, (dir + 1) % 4); r.set(lc, tile); tile.layover(0, set.ground.pick(rnd)); m.new Plane(upper.vrt, 256, strans[rnd.nextInt(strans.length)].layer(Resource.imgc).tex(), false) .texrot(null, null, 3 + dir, false); makewall(m, mlu.pos, mlb.pos, mrb.pos, mru.pos, walls[rnd.nextInt(walls.length)], 11); } public void mkcornwall(MapMesh m, Random rnd, Coord3f ul, Coord3f bl, Coord3f br, Coord3f ur, boolean cw) { if(cw) makewall(m, ul, bl, br, ur, lcorn[rnd.nextInt(lcorn.length)], 5.5f); else makewall(m, ul, bl, br, ur, rcorn[rnd.nextInt(rcorn.length)], 5.5f); } public void laycomplex(MapMesh m, Random rnd, Coord lc, Coord gc, boolean[] b) { Surface g = m.gnd(); SPoint[] crn = { g.spoint(lc), g.spoint(lc.add(1, 0)), g.spoint(lc.add(1, 1)), g.spoint(lc.add(0, 1)), }; int s; for(s = 0; true; s++) { if(b[s]) { s = (s + 1) % 4; break; } } SPoint[] ct = new SPoint[4]; SPoint[] h1 = new SPoint[4]; SPoint[] h2 = new SPoint[4]; { for(int i = s, n = 0; n < 4; i = (i + 1) % 4, n++) { if(!b[(i + 3) % 4]) { h1[i] = h2[(i + 3) % 4]; h1[i].pos.z = (h1[i].pos.z + crn[i].pos.z) * 0.5f; } else { h1[i] = new SPoint(crn[(i + 3) % 4].pos.add(crn[i].pos).mul(0.5f)); h1[i].pos.z = crn[i].pos.z; } h2[i] = new SPoint(crn[(i + 1) % 4].pos.add(crn[i].pos).mul(0.5f)); h2[i].pos.z = crn[i].pos.z; } SPoint cc = null; for(int i = s, n = 0; n < 4; i = (i + 1) % 4, n++) { if(cc == null) { cc = new SPoint(crn[0].pos.add(crn[1].pos).add(crn[2].pos).add(crn[3].pos).mul(0.25f)); if(b[i]) cc.pos.z = crn[i].pos.z; else cc.pos.z = (h1[i].pos.z + h2[(i + 1) % 4].pos.z) * 0.5f; } ct[i] = cc; if(b[i]) cc = null; } for(int i = s, n = 0; n < 4; i = (i + 1) % 4, n++) { if(b[i] && !(m.map.tiler(m.map.gettile(gc.add(ecwx[i], ecwy[i]))) instanceof RidgeTile)) { h2[i].pos.z = (h2[i].pos.z + h1[(i + 1) % 4].pos.z) * 0.5f; h1[(i + 1) % 4] = h2[i]; } } } Ridges r = m.data(rid); Ridges.Tile tile = r.new Tile(); boolean cont = false; for(int i = s, n = 0; n < 4; i = (i + 1) % 4, n++) { if(cont) { cont = false; } else if(!b[i] && b[(i + 1) % 4] && b[(i + 3) % 4]) { Ridges.Tile.TilePlane pl = tile.new TilePlane(shift(new SPoint[] {crn[i], h1[i], h2[(i + 1) % 4], crn[(i + 1) % 4]}, 4 - i)); remaphalf(pl, i); cont = true; SPoint pc = ct[(i + 3) % 4], cc = ct[i]; if(pc.pos.z > cc.pos.z) { mkcornwall(m, rnd, pc.pos, cc.pos, h1[i].pos, h2[(i + 3) % 4].pos, true); } else { mkcornwall(m, rnd, h1[i].pos, h2[(i + 3) % 4].pos, pc.pos, cc.pos, false); m.new Plane(pl.vrt, 256, strans[rnd.nextInt(strans.length)].layer(Resource.imgc).tex(), false) .texrot(null, null, i, false); } } else { Ridges.Tile.TilePlane pl = tile.new TilePlane(shift(new SPoint[] {crn[i], h1[i], ct[i], h2[i]}, 4 - i)); remapquad(pl, i); boolean[] ub = new boolean[4], db = new boolean[4], tb = new boolean[4]; for(int o = 0; o < 4; o++) { int u = (i + o) % 4; tb[o] = b[u]; ub[o] = b[u] && (h2[u].pos.z < h1[(u + 1) % 4].pos.z); db[o] = b[u] && (h2[u].pos.z > h1[(u + 1) % 4].pos.z); } if(ub[3] && db[0]) { m.new Plane(pl.vrt, 256, c1trans[rnd.nextInt(c1trans.length)].layer(Resource.imgc).tex(), false) .texrot(null, null, i, false); } else if(!tb[0] && !tb[3] && db[1] && ub[2]) { m.new Plane(pl.vrt, 256, c2trans[rnd.nextInt(c2trans.length)].layer(Resource.imgc).tex(), false) .texrot(null, null, i, false); } else if(ub[3] && !db[0]) { Tex t = strans[rnd.nextInt(strans.length)].layer(Resource.imgc).tex(); m.new Plane(pl.vrt, 256, t, false) .texrot(Coord.z, new Coord(t.sz().x / 2, t.sz().y), i, false); } else if(!ub[3] && db[0]) { Tex t = strans[rnd.nextInt(strans.length)].layer(Resource.imgc).tex(); m.new Plane(pl.vrt, 256, t, false) .texrot(Coord.z, new Coord(t.sz().x / 2, t.sz().y), i + 3, false); } if(b[(i + 3) % 4]) { SPoint pc = ct[(i + 3) % 4], cc = ct[i]; if(pc.pos.z > cc.pos.z) mkcornwall(m, rnd, pc.pos, cc.pos, h1[i].pos, h2[(i + 3) % 4].pos, true); else mkcornwall(m, rnd, h1[i].pos, h2[(i + 3) % 4].pos, pc.pos, cc.pos, false); } } } r.set(lc, tile); tile.layover(0, set.ground.pick(rnd)); } public void lay(MapMesh m, Random rnd, Coord lc, Coord gc) { boolean[] b = breaks(m, gc, breaks[0]); if(b[0] || b[1] || b[2] || b[3]) { if(isend(m, gc, b)) { layend(m, rnd, lc, gc, b); } else if(isstraight(m, gc, b)) { layridge(m, rnd, lc, gc, b); } else { laycomplex(m, rnd, lc, gc, b); } } else { super.lay(m, rnd, lc, gc); } } public void layover(MapMesh m, Coord lc, Coord gc, int z, Tile t) { boolean[] b = breaks(m, gc, breaks[0]); if(b[0] || b[1] || b[2] || b[3]) { Ridges.Tile tile = m.data(rid).get(lc); if(tile == null) throw(new NullPointerException("Ridged tile has not been properly initialized")); tile.layover(z, t); } else { super.layover(m, lc, gc, z, t); } } public boolean ridgep(MCache map, Coord tc) { int z00 = map.getz(tc), z10 = map.getz(tc.add(1, 0)), z01 = map.getz(tc.add(0, 1)), z11 = map.getz(tc.add(1, 1)); int diff = breaks[0]; return(Math.abs(z00 - z10) >= diff || Math.abs(z10 - z11) >= diff || Math.abs(z11 - z01) >= diff || Math.abs(z01 - z00) >= diff); } }