/* * 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.awt.Color; import java.util.*; import static haven.GOut.checkerr; import javax.media.opengl.*; public abstract class PView extends Widget { public RenderList rls; public static final GLState.Slot<RenderContext> ctx = new GLState.Slot<RenderContext>(GLState.Slot.Type.SYS, RenderContext.class); public static final GLState.Slot<RenderState> wnd = new GLState.Slot<RenderState>(GLState.Slot.Type.SYS, RenderState.class, HavenPanel.proj2d, GLFrameBuffer.slot); public static final GLState.Slot<Projection> proj = new GLState.Slot<Projection>(GLState.Slot.Type.SYS, Projection.class, wnd); public static final GLState.Slot<Camera> cam = new GLState.Slot<Camera>(GLState.Slot.Type.SYS, Camera.class, proj); public static final GLState.Slot<Location.Chain> loc = new GLState.Slot<Location.Chain>(GLState.Slot.Type.GEOM, Location.Chain.class, cam); public Profile prof = new Profile(300); protected Light.Model lm; private final WidgetContext cstate = new WidgetContext(); private final WidgetRenderState rstate = new WidgetRenderState(); private GLState pstate; public static class RenderContext extends GLState.Abstract { private Map<DataID, Object> data = new CacheMap<DataID, Object>(CacheMap.RefType.WEAK); public interface DataID<T> { public T make(RenderContext c); } @SuppressWarnings("unchecked") public <T> T data(DataID<T> id) { T ret = (T)data.get(id); if(ret == null) data.put(id, ret = id.make(this)); return(ret); } public void prep(Buffer b) { b.put(ctx, this); } public Glob glob() { return(null); } } public abstract static class ConfContext extends RenderContext implements GLState.GlobalState { public FBConfig cfg = new FBConfig(this, sz()); public FBConfig cur = new FBConfig(this, sz()); protected abstract Coord sz(); public Global global(RenderList rl, Buffer ctx) { return(glob); } private final Global glob = new Global() { public void postsetup(RenderList rl) { cfg.fin(cur); cur = cfg; cfg = new FBConfig(ConfContext.this, sz()); if(cur.fb != null) { for(RenderList.Slot s : rl.slots()) { if(s.os.get(ctx) == ConfContext.this) cur.state.prep(s.os); } } } public void prerender(RenderList rl, GOut g) {} public void postrender(RenderList rl, GOut g) {} }; } public class WidgetContext extends ConfContext { protected Coord sz() { return(PView.this.sz); } public Glob glob() { return(ui.sess.glob); } public PView widget() { return(PView.this); } } public static abstract class RenderState extends GLState { public void apply(GOut g) { GL2 gl = g.gl; gl.glScissor(g.ul.x, g.root().sz.y - g.ul.y - g.sz.y, g.sz.x, g.sz.y); /* For the viewport, use the renderstate's indicated size * and offset explicitly, so as to not fail on partially * clipped GOuts. */ Coord ul = ul(); Coord sz = sz(); gl.glViewport(ul.x, g.root().sz.y - ul.y - sz.y, sz.x, sz.y); gl.glAlphaFunc(gl.GL_GREATER, 0.5f); gl.glEnable(gl.GL_DEPTH_TEST); gl.glEnable(gl.GL_CULL_FACE); gl.glEnable(gl.GL_SCISSOR_TEST); gl.glDepthFunc(gl.GL_LEQUAL); gl.glClearDepth(1.0); } public void unapply(GOut g) { GL gl = g.gl; gl.glDisable(gl.GL_DEPTH_TEST); gl.glDisable(gl.GL_CULL_FACE); gl.glDisable(gl.GL_SCISSOR_TEST); gl.glViewport(g.root().ul.x, g.root().ul.y, g.root().sz.x, g.root().sz.y); gl.glScissor(g.root().ul.x, g.root().ul.y, g.root().sz.x, g.root().sz.y); } public void prep(Buffer b) { b.put(wnd, this); } public abstract Coord ul(); public abstract Coord sz(); } private class WidgetRenderState extends RenderState { public Coord ul() { return(rootpos()); } public Coord sz() { return(PView.this.sz); } } public PView(Coord c, Coord sz, Widget parent) { super(c, sz, parent); pstate = makeproj(); lm = new Light.Model(); lm.cc = GL2.GL_SEPARATE_SPECULAR_COLOR; } protected GLState.Buffer basic(GOut g) { GLState.Buffer buf = g.basicstate(); cstate.prep(buf); rstate.prep(buf); if(pstate != null) pstate.prep(buf); camera().prep(buf); if(ui.audio != null) ui.audio.prep(buf); return(buf); } protected abstract GLState camera(); protected abstract void setup(RenderList rls); protected Projection makeproj() { float field = 0.5f; float aspect = ((float)sz.y) / ((float)sz.x); return(Projection.frustum(-field, field, -aspect * field, aspect * field, 1, 5000)); } public void resize(Coord sz) { super.resize(sz); pstate = makeproj(); } private final Rendered scene = new Rendered() { public void draw(GOut g) { } public boolean setup(RenderList rl) { PView.this.setup(rl); return(false); } }; protected Color clearcolor() { return(Color.BLACK); } public void draw(GOut g) { if((g.sz.x < 1) || (g.sz.y < 1)) return; if((rls == null) || (rls.cfg != g.gc)) rls = new RenderList(g.gc); Profile.Frame curf = null; if(Config.profile) curf = prof.new Frame(); GLState.Buffer bk = g.st.copy(); GLState.Buffer def = basic(g); if(g.gc.pref.fsaa.val) States.fsaa.prep(def); try { lm.prep(def); new Light.LightList().prep(def); rls.setup(scene, def); if(curf != null) curf.tick("setup"); rls.fin(); if(curf != null) curf.tick("sort"); GOut rg; if(cstate.cur.fb != null) { GLState.Buffer gb = g.basicstate(); HavenPanel.OrthoState.fixed(cstate.cur.fb.sz()).prep(gb); cstate.cur.fb.prep(gb); cstate.cur.fb.prep(def); rg = new GOut(g.gl, g.ctx, g.gc, g.st, gb, cstate.cur.fb.sz()); } else { rg = g; } rg.st.set(def); Color cc = clearcolor(); if((cc == null) && (cstate.cur.fb != null)) cc = new Color(0, 0, 0, 0); rg.apply(); GL gl = rg.gl; if(cc == null) { gl.glClear(gl.GL_DEPTH_BUFFER_BIT); } else { gl.glClearColor((float)cc.getRed() / 255f, (float)cc.getGreen() / 255f, (float)cc.getBlue() / 255f, (float)cc.getAlpha() / 255f); gl.glClear(gl.GL_DEPTH_BUFFER_BIT | gl.GL_COLOR_BUFFER_BIT); } if(curf != null) curf.tick("cls"); g.st.time = 0; rls.render(rg); if(cstate.cur.fb != null) cstate.cur.resolve(g); if(curf != null) { curf.add("apply", g.st.time); curf.tick("render", g.st.time); } } finally { g.st.set(bk); } render2d(g); if(curf != null) curf.tick("2d"); if(curf != null) curf.fin(); } protected void render2d(GOut g) { for(RenderList.Slot s : rls.slots()) { if(s.r instanceof Render2D) ((Render2D)s.r).draw2d(g); } } public interface Render2D extends Rendered { public void draw2d(GOut g); } public static abstract class Draw2D implements Render2D { public void draw(GOut g) {} public boolean setup(RenderList r) { return(false); } } }