/* * 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.*; public class RenderList { public final GLConfig cfg; private Slot[] list = new Slot[100]; private int cur = 0; private Slot curp = null; private GLState.Global[] gstates = new GLState.Global[0]; private static final ThreadLocal<RenderList> curref = new ThreadLocal<RenderList>(); public class Slot { public Rendered r; public GLState.Buffer os = new GLState.Buffer(cfg), cs = new GLState.Buffer(cfg); public Rendered.Order o; public boolean d; public Slot p; } public RenderList(GLConfig cfg) { this.cfg = cfg; } private Slot getslot() { int i = cur++; if(i >= list.length) { Slot[] n = new Slot[i * 2]; System.arraycopy(list, 0, n, 0, i); list = n; } Slot s; if((s = list[i]) == null) s = list[i] = new Slot(); return(s); } private final Iterable<Slot> slotsi = new Iterable<Slot>() { public Iterator<Slot> iterator() { return(new Iterator<Slot>() { private int i = 0; public Slot next() { return(list[i++]); } public boolean hasNext() { return(i < cur); } public void remove() { throw(new UnsupportedOperationException()); } }); } }; public Iterable<Slot> slots() { return(slotsi); } public static RenderList current() { return(curref.get()); } protected void setup(Slot s, Rendered r) { s.r = r; Slot pp = s.p = curp; if(pp == null) curref.set(this); try { curp = s; s.d = r.setup(this); } finally { if((curp = pp) == null) curref.remove(); } } protected void postsetup(Slot ps, GLState.Buffer t) { gstates = getgstates(); Slot pp = curp; try { curp = ps; for(GLState.Global gs : gstates) { t.copy(ps.cs); gs.postsetup(this); } } finally { curp = pp; } } public void setup(Rendered r, GLState.Buffer t) { rewind(); Slot s = getslot(); t.copy(s.os); t.copy(s.cs); setup(s, r); postsetup(s, t); } public void add(Rendered r, GLState t) { Slot s = getslot(); if(curp == null) throw(new RuntimeException("Tried to set up relative slot with no parent")); curp.cs.copy(s.os); if(t != null) t.prep(s.os); s.os.copy(s.cs); setup(s, r); } public void add2(Rendered r, GLState.Buffer t) { Slot s = getslot(); t.copy(s.os); s.r = r; s.p = curp; s.d = true; } public GLState.Buffer cstate() { return(curp.cs); } public GLState.Buffer state() { return(curp.os); } public void prepo(GLState t) { t.prep(curp.os); } public void prepc(GLState t) { t.prep(curp.cs); } @SuppressWarnings("unchecked") private static final Comparator<Slot> cmp = new Comparator<Slot>() { public int compare(Slot a, Slot b) { if(!a.d && !b.d) return(0); if(a.d && !b.d) return(-1); if(!a.d && b.d) return(1); int az = a.o.mainz(), bz = b.o.mainz(); if(az != bz) return(az - bz); if(a.o != b.o) throw(new RuntimeException("Found two different orderings with the same main-Z: " + a.o + " and " + b.o)); int ret = a.o.cmp().compare(a.r, b.r, a.os, b.os); if(ret != 0) return(ret); return(System.identityHashCode(a.r) - System.identityHashCode(b.r)); } }; private GLState[] dbc = new GLState[0]; private GLState.Global[] getgstates() { /* This is probably a fast way to intern the states. */ IdentityHashMap<GLState.Global, GLState.Global> gstates = new IdentityHashMap<GLState.Global, GLState.Global>(this.gstates.length); for(int i = 0; i < dbc.length; i++) dbc[i] = null; for(int i = 0; i < cur; i++) { if(!list[i].d) continue; GLState.Buffer ctx = list[i].os; GLState[] sl = ctx.states(); if(sl.length > dbc.length) dbc = new GLState[sl.length]; for(int o = 0; o < sl.length; o++) { GLState st = sl[o]; if(st == dbc[o]) continue; if(st instanceof GLState.GlobalState) { GLState.Global gst = ((GLState.GlobalState)st).global(this, ctx); gstates.put(gst, gst); } dbc[o] = st; } } return(gstates.keySet().toArray(new GLState.Global[0])); } public void fin() { for(int i = 0; i < cur; i++) { if((list[i].o = list[i].os.get(Rendered.order)) == null) list[i].o = Rendered.deflt; if(list[i].os.get(Rendered.skip.slot) != null) list[i].d = false; } Arrays.sort(list, 0, cur, cmp); } public static class RLoad extends Loading { public static RLoad wrap(final Loading l) { return(new RLoad() { public boolean canwait() {return(l.canwait());} public void waitfor() throws InterruptedException {l.waitfor();} }); } } public boolean ignload = true; protected void render(GOut g, Rendered r) { try { r.draw(g); } catch(RLoad l) { if(ignload) return; else throw(l); } } public void render(GOut g) { for(GLState.Global gs : gstates) gs.prerender(this, g); for(int i = 0; i < cur; i++) { Slot s = list[i]; if(!s.d) break; g.st.set(s.os); render(g, s.r); } for(GLState.Global gs : gstates) gs.postrender(this, g); } public void rewind() { if(curp != null) throw(new RuntimeException("Tried to rewind RenderList while adding to it.")); cur = 0; } public void dump(java.io.PrintStream out) { for(Slot s : slots()) out.println((s.d?" ":"!") + s.r + ": " + s.os); } }