/* * 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 java.nio.*; import javax.media.opengl.*; public class FastMesh implements FRendered, Disposable { public static final GLState.Slot<GLState> vstate = new GLState.Slot<GLState>(GLState.Slot.Type.SYS, GLState.class); public final VertexBuf vert; public final ShortBuffer indb; public final int num; public FastMesh from; private Compiler compiler; private Coord3f nb, pb; public FastMesh(VertexBuf vert, ShortBuffer ind) { this.vert = vert; num = ind.capacity() / 3; if(ind.capacity() != num * 3) throw(new RuntimeException("Invalid index array length")); this.indb = ind; } public FastMesh(VertexBuf vert, short[] ind) { this(vert, Utils.bufcp(ind)); } public FastMesh(FastMesh from, VertexBuf vert) { this.from = from; if(from.vert.num != vert.num) throw(new RuntimeException("V-buf sizes must match")); this.vert = vert; this.indb = from.indb; this.num = from.num; } public static abstract class Compiled { public abstract void draw(GOut g); public abstract void dispose(); } public abstract class Compiler { private GLProgram[] kcache = new GLProgram[0]; private Compiled[] vcache = new Compiled[0]; private Object[] ids = new Object[0]; private Object[] getid(GOut g) { Object[] id = new Object[vert.bufs.length]; for(int i = 0; i < id.length; i++) { if(vert.bufs[i] instanceof VertexBuf.GLArray) id[i] = ((VertexBuf.GLArray)vert.bufs[i]).progid(g); else id[i] = null; } return(ArrayIdentity.intern(id)); } public Compiled get(GOut g) { GLProgram prog = g.st.prog; int i; for(i = 0; i < kcache.length; i++) { if(kcache[i] == prog) return(vcache[i]); } g.apply(); Object[] id = getid(g); Compiled ret; create: { int o; for(o = 0; o < kcache.length; o++) { if(ids[o] == id) { ret = vcache[o]; break create; } } ret = create(g); } kcache = Utils.extend(kcache, i + 1); vcache = Utils.extend(vcache, i + 1); ids = Utils.extend(ids, i + 1); kcache[i] = prog; vcache[i] = ret; ids[i] = id; return(ret); } public abstract Compiled create(GOut g); public void dispose() { for(Compiled c : vcache) c.dispose(); kcache = new GLProgram[0]; vcache = new Compiled[0]; } } public class DLCompiler extends Compiler { public class DLCompiled extends Compiled { private DisplayList list; public void draw(GOut g) { g.apply(); GL2 gl = g.gl; if((list != null) && (list.gl != gl)) { list.dispose(); list = null; } if(list == null) { list = new DisplayList(gl); gl.glNewList(list.id, GL2.GL_COMPILE); cdraw(g); gl.glEndList(); } gl.glCallList(list.id); } public void dispose() { if(list != null) { list.dispose(); list = null; } } } public DLCompiled create(GOut g) {return(new DLCompiled());} } public class VAOState extends GLState { private GLBuffer ind; private GLVertexArray vao; private void bindindbo(GL2 gl) { if((ind != null) && (ind.gl != gl)) { ind.dispose(); ind = null; } if(ind == null) { ind = new GLBuffer(gl); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ind.id); indb.rewind(); gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, indb.remaining() * 2, indb, GL.GL_STATIC_DRAW); GOut.checkerr(gl); } else { gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, ind.id); } } public void apply(GOut g) { GL2 gl = g.gl; if((vao != null) && (vao.gl != gl)) { vao.dispose(); vao = null; } if(vao == null) { vao = new GLVertexArray(gl); gl.glBindVertexArray(vao.id); for(VertexBuf.AttribArray buf : vert.bufs) { if(buf instanceof VertexBuf.GLArray) ((VertexBuf.GLArray)buf).bind(g, true); } bindindbo(gl); } else { gl.glBindVertexArray(vao.id); } } public void unapply(GOut g) { GL2 gl = g.gl; gl.glBindVertexArray(0); gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, 0); } public int capplyfrom(GLState o) { if(o instanceof VAOState) return(1); return(-1); } public void applyfrom(GOut g, GLState from) { apply(g); } public void dispose() { if(vao != null) { vao.dispose(); vao = null; } if(ind != null) { ind.dispose(); ind = null; } } public void prep(Buffer buf) { buf.put(vstate, this); } } public class VAOCompiler extends Compiler { public class VAOCompiled extends Compiled { private VAOState st = new VAOState(); public void draw(GOut g) { GL2 gl = g.gl; g.state(st); g.apply(); gl.glDrawElements(GL.GL_TRIANGLES, num * 3, GL.GL_UNSIGNED_SHORT, 0); } public void dispose() { st.dispose(); } } public VAOCompiled create(GOut g) {return(new VAOCompiled());} } private void cbounds() { Coord3f nb = null, pb = null; VertexBuf.VertexArray vbuf = null; for(VertexBuf.AttribArray buf : vert.bufs) { if(buf instanceof VertexBuf.VertexArray) { vbuf = (VertexBuf.VertexArray)buf; break; } } for(int i = 0; i < indb.capacity(); i++) { int vi = indb.get(i) * 3; float x = vbuf.data.get(vi), y = vbuf.data.get(vi + 1), z = vbuf.data.get(vi + 2); if(nb == null) { nb = new Coord3f(x, y, z); pb = new Coord3f(x, y, z); } else { nb.x = Math.min(nb.x, x); pb.x = Math.max(pb.x, x); nb.y = Math.min(nb.y, y); pb.y = Math.max(pb.y, y); nb.z = Math.min(nb.z, z); pb.z = Math.max(pb.z, z); } } this.nb = nb; this.pb = pb; } public Coord3f nbounds() { if(nb == null) cbounds(); return(nb); } public Coord3f pbounds() { if(pb == null) cbounds(); return(pb); } public void cdraw(GOut g) { g.apply(); indb.rewind(); for(int i = 0; i < vert.bufs.length; i++) { if(vert.bufs[i] instanceof VertexBuf.GLArray) ((VertexBuf.GLArray)vert.bufs[i]).bind(g, false); } g.gl.glDrawElements(GL.GL_TRIANGLES, num * 3, GL.GL_UNSIGNED_SHORT, indb); for(int i = 0; i < vert.bufs.length; i++) { if(vert.bufs[i] instanceof VertexBuf.GLArray) ((VertexBuf.GLArray)vert.bufs[i]).unbind(g); } } private GLSettings.MeshMode curmode = null; public void draw(GOut g) { GL2 gl = g.gl; if(compile()) { if(curmode != g.gc.pref.meshmode.val) { if(compiler != null) { compiler.dispose(); compiler = null; } switch(g.gc.pref.meshmode.val) { case VAO: compiler = new VAOCompiler(); break; case DLIST: compiler = new DLCompiler(); break; } curmode = g.gc.pref.meshmode.val; } } else if(compiler != null) { compiler.dispose(); compiler = null; curmode = null; } if(compiler != null) { compiler.get(g).draw(g); } else { cdraw(g); } GOut.checkerr(gl); } protected boolean compile() { return(true); } public void dispose() { if(compiler != null) { compiler.dispose(); compiler = null; } vert.dispose(); } public void drawflat(GOut g) { draw(g); GOut.checkerr(g.gl); } public boolean setup(RenderList r) { return(true); } public static class ResourceMesh extends FastMesh implements ResPart { public final int id; public final Resource res; public ResourceMesh(VertexBuf vert, short[] ind, MeshRes info) { super(vert, ind); this.id = info.id; this.res = info.getres(); } public int partid() { return(id); } public String toString() { return("FastMesh(" + res.name + ", " + id + ")"); } } @Resource.LayerName("mesh") public static class MeshRes extends Resource.Layer implements Resource.IDLayer<Integer> { public transient FastMesh m; public transient Material.Res mat; private transient short[] tmp; public final int id, ref; private int matid; public MeshRes(Resource res, byte[] buf) { res.super(); int[] off = {0}; int fl = Utils.ub(buf[off[0]]); off[0] += 1; int num = Utils.uint16d(buf, off[0]); off[0] += 2; matid = Utils.int16d(buf, off[0]); off[0] += 2; if((fl & 2) != 0) { id = Utils.int16d(buf, off[0]); off[0] += 2; } else { id = -1; } if((fl & 4) != 0) { ref = Utils.int16d(buf, off[0]); off[0] += 2; } else { ref = -1; } if((fl & ~7) != 0) throw(new Resource.LoadException("Unsupported flags in fastmesh: " + fl, getres())); short[] ind = new short[num * 3]; for(int i = 0; i < num * 3; i++) ind[i] = (short)Utils.int16d(buf, off[0] + (i * 2)); this.tmp = ind; } public void init() { VertexBuf v = getres().layer(VertexBuf.VertexRes.class, false).b; this.m = new ResourceMesh(v, this.tmp, this); this.tmp = null; if(matid >= 0) { for(Material.Res mr : getres().layers(Material.Res.class, false)) { if(mr.id == matid) this.mat = mr; } if(this.mat == null) throw(new Resource.LoadException("Could not find specified material: " + matid, getres())); } } public Integer layerid() { return(id); } } }