/*
* 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 javax.media.opengl.*;
import haven.glsl.*;
import haven.GLFrameBuffer.Attachment;
public class FBConfig {
public final PView.ConfContext ctx;
public Coord sz;
public boolean hdr, tdepth;
public int ms = 1;
public GLFrameBuffer fb;
public PView.RenderState wnd;
public Attachment color[], depth;
public GLState state;
private RenderTarget[] tgts = new RenderTarget[0];
private ResolveFilter[] res = new ResolveFilter[0];
private GLState resp;
public FBConfig(PView.ConfContext ctx, Coord sz) {
this.ctx = ctx;
this.sz = sz;
}
public boolean cleanp() {
if(hdr || tdepth || (ms > 1))
return(false);
for(int i = 0; i < tgts.length; i++) {
if(tgts[i] != null)
return(false);
}
for(ResolveFilter rf : res) {
if(!rf.cleanp())
return(false);
}
return(true);
}
private static final ShaderMacro[] nosh = new ShaderMacro[0];
private void create() {
Collection<Attachment> color = new LinkedList<Attachment>();
Attachment depth;
Collection<ShaderMacro> shb = new LinkedList<ShaderMacro>();
Collection<GLState> stb = new LinkedList<GLState>();
{
int fmt = hdr?GL.GL_RGBA16F:GL.GL_RGBA;
if(ms <= 1)
color.add(Attachment.mk(new TexE(sz, fmt, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE)));
else
color.add(Attachment.mk(new TexMSE(sz, ms, fmt, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE)));
}
if(tdepth) {
if(ms <= 1)
depth = Attachment.mk(new TexE(sz, GL2.GL_DEPTH_COMPONENT, GL2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT));
else
depth = Attachment.mk(new TexMSE(sz, ms, GL2.GL_DEPTH_COMPONENT, GL2.GL_DEPTH_COMPONENT, GL.GL_UNSIGNED_INT));
} else {
depth = Attachment.mk(new GLFrameBuffer.RenderBuffer(sz, GL2.GL_DEPTH_COMPONENT, ms));
}
for(int i = 0; i < tgts.length; i++) {
if(tgts[i] != null) {
color.add(tgts[i].maketex(this));
GLState st = tgts[i].state(this, i + 1);
if(st != null)
stb.add(st);
ShaderMacro code = tgts[i].code(this, i + 1);
if(code != null)
shb.add(code);
}
}
this.color = color.toArray(new Attachment[0]);
this.depth = depth;
/* XXX: Shaders should be canonized and cached to avoid
* creation of unnecessary identical programs when
* configurations change. */
final ShaderMacro[] shaders;
if(shb.size() < 1)
shaders = nosh;
else
shaders = shb.toArray(new ShaderMacro[0]);
this.fb = new GLFrameBuffer(this.color, this.depth) {
public ShaderMacro[] shaders() {return(shaders);}
public boolean reqshaders() {return(shaders.length > 0);}
};
this.wnd = new PView.RenderState() {
public Coord ul() {return(Coord.z);}
public Coord sz() {return(sz);}
};
stb.add(fb);
stb.add(wnd);
this.state = GLState.compose(stb.toArray(new GLState[0]));
if(res.length > 0) {
ShaderMacro[] resp = new ShaderMacro[res.length];
for(int i = 0; i < res.length; i++)
resp[i] = res[i].code(this);
resp = ArrayIdentity.intern(resp);
this.resp = new States.AdHoc(resp) {
public void apply(GOut g) {
for(ResolveFilter f : res)
f.apply(FBConfig.this, g);
}
public void unapply(GOut g) {
for(ResolveFilter f : res)
f.unapply(FBConfig.this, g);
}
};
}
}
private static <T> boolean hasuo(T[] a, T[] b) {
outer: for(T ae : a) {
for(T be : b) {
if(Utils.eq(ae, be))
continue outer;
}
return(false);
}
return(true);
}
public static boolean equals(FBConfig a, FBConfig b) {
if(!a.sz.equals(b.sz))
return(false);
if((a.hdr != b.hdr) || (a.tdepth != b.tdepth))
return(false);
if(a.ms != b.ms)
return(false);
if(!hasuo(a.tgts, b.tgts) || !hasuo(b.tgts, a.tgts))
return(false);
if(!hasuo(a.res, b.res) || !hasuo(b.res, a.res))
return(false);
return(true);
}
private void subsume(FBConfig last) {
fb = last.fb;
wnd = last.wnd;
color = last.color;
depth = last.depth;
tgts = last.tgts;
res = last.res;
resp = last.resp;
state = last.state;
}
public void fin(FBConfig last) {
if(ms <= 1)
add(new Resolve1());
else
add(new ResolveMS(ms));
if(equals(this, last)) {
subsume(last);
return;
}
if(last.fb != null)
last.fb.dispose();
if(cleanp())
return;
create();
}
public void resolve(GOut g) {
if(fb != null) {
for(ResolveFilter rf : res)
rf.prepare(this, g);
g.ftexrect(Coord.z, sz, resp);
}
}
public RenderTarget add(RenderTarget tgt) {
if(tgt == null)
throw(new NullPointerException());
for(RenderTarget p : tgts) {
if(Utils.eq(tgt, p))
return(p);
}
int i;
for(i = 0; i < tgts.length; i++) {
if(tgts[i] == null)
tgts[i] = tgt;
return(tgt);
}
tgts = Utils.extend(tgts, i + 1);
tgts[i] = tgt;
return(tgt);
}
public ResolveFilter add(ResolveFilter rf) {
if(rf == null)
throw(new NullPointerException());
for(ResolveFilter p : res) {
if(Utils.eq(rf, p))
return(p);
}
int l = res.length;
res = Utils.extend(res, l + 1);
res[l] = rf;
return(rf);
}
public static abstract class RenderTarget {
public Attachment tex;
public Attachment maketex(FBConfig cfg) {
if(cfg.ms <= 1)
return(tex = Attachment.mk(new TexE(cfg.sz, GL.GL_RGBA, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE)));
else
return(tex = Attachment.mk(new TexMSE(cfg.sz, cfg.ms, GL.GL_RGBA, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE)));
}
public GLState state(FBConfig cfg, int id) {
return(null);
}
public ShaderMacro code(FBConfig cfg, int id) {
return(null);
}
}
public static final Uniform numsamples = new Uniform.AutoApply(Type.INT) {
public void apply(GOut g, int loc) {
g.gl.glUniform1i(loc, ((PView.ConfContext)g.st.get(PView.ctx)).cur.ms);
}
};
public interface ResolveFilter {
public boolean cleanp();
public void prepare(FBConfig cfg, GOut g);
public ShaderMacro code(FBConfig cfg);
public void apply(FBConfig cfg, GOut g);
public void unapply(FBConfig cfg, GOut g);
}
private static class Resolve1 implements ResolveFilter {
public void prepare(FBConfig cfg, GOut g) {}
public boolean cleanp() {return(true);}
private static final Uniform ctex = new Uniform(Type.SAMPLER2D);
private static final ShaderMacro code = new ShaderMacro() {
public void modify(ProgramContext prog) {
prog.fctx.fragcol.mod(new Macro1<Expression>() {
public Expression expand(Expression in) {
return(Cons.texture2D(ctex.ref(), Tex2D.texcoord.ref()));
}
}, 0);
}
};
public ShaderMacro code(FBConfig cfg) {return(code);}
private GLState.TexUnit csmp;
public void apply(FBConfig cfg, GOut g) {
csmp = g.st.texalloc(g, ((GLFrameBuffer.Attach2D)cfg.color[0]).tex);
g.gl.glUniform1i(g.st.prog.uniform(ctex), csmp.id);
}
public void unapply(FBConfig cfg, GOut g) {
csmp.ufree(); csmp = null;
}
public boolean equals(Object o) {return(o instanceof Resolve1);}
}
private static class ResolveMS implements ResolveFilter {
private final int samples;
private ResolveMS(int samples) {
this.samples = samples;
}
public void prepare(FBConfig cfg, GOut g) {}
public boolean cleanp() {return(true);}
private static final Uniform ctex = new Uniform(Type.SAMPLER2DMS);
private final ShaderMacro code = new ShaderMacro() {
public void modify(ProgramContext prog) {
prog.fctx.fragcol.mod(new Macro1<Expression>() {
public Expression expand(Expression in) {
Expression[] texels = new Expression[samples];
for(int i = 0; i < samples; i++)
texels[i] = Cons.texelFetch(ctex.ref(), Cons.ivec2(Cons.floor(Cons.mul(Tex2D.texcoord.ref(), MiscLib.screensize.ref()))), Cons.l(i));
return(Cons.mul(Cons.add(texels), Cons.l(1.0 / samples)));
}
}, 0);
}
};
public ShaderMacro code(FBConfig cfg) {return(code);}
private GLState.TexUnit csmp;
public void apply(FBConfig cfg, GOut g) {
csmp = g.st.texalloc(g, ((GLFrameBuffer.AttachMS)cfg.color[0]).tex);
g.gl.glUniform1i(g.st.prog.uniform(ctex), csmp.id);
}
public void unapply(FBConfig cfg, GOut g) {
csmp.ufree(); csmp = null;
}
public boolean equals(Object o) {return((o instanceof ResolveMS) && (((ResolveMS)o).samples == samples));}
}
}