/*
* 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 javax.media.opengl.*;
public class GLFrameBuffer extends GLState {
public static final Slot<GLFrameBuffer> slot = new Slot<GLFrameBuffer>(Slot.Type.SYS, GLFrameBuffer.class, HavenPanel.global);
private final Attachment[] color;
private final Attachment depth;
private final RenderBuffer altdepth;
private FBO fbo;
private final int[] bufmask;
public static class FBO extends GLObject {
public final int id;
public FBO(GL2 gl) {
super(gl);
int[] buf = new int[1];
gl.glGenFramebuffers(1, buf, 0);
this.id = buf[0];
GOut.checkerr(gl);
}
protected void delete() {
int[] buf = {id};
gl.glDeleteFramebuffers(1, buf, 0);
GOut.checkerr(gl);
}
}
public static class RenderBuffer {
public final Coord sz;
public final int samples;
public final int fmt;
private RBO rbo;
public RenderBuffer(Coord sz, int fmt, int samples) {
this.sz = sz;
this.fmt = fmt;
this.samples = samples;
}
public RenderBuffer(Coord sz, int fmt) {
this(sz, fmt, 1);
}
public int glid(GL2 gl) {
if((rbo != null) && (rbo.gl != gl))
dispose();
if(rbo == null) {
rbo = new RBO(gl);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, rbo.id);
if(samples <= 1)
gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, fmt, sz.x, sz.y);
else
gl.glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, samples, fmt, sz.x, sz.y);
}
return(rbo.id);
}
public void dispose() {
synchronized(this) {
if(rbo != null) {
rbo.dispose();
rbo = null;
}
}
}
public static class RBO extends GLObject {
public final int id;
public RBO(GL2 gl) {
super(gl);
int[] buf = new int[1];
gl.glGenRenderbuffers(1, buf, 0);
this.id = buf[0];
GOut.checkerr(gl);
}
protected void delete() {
int[] buf = {id};
gl.glDeleteRenderbuffers(1, buf, 0);
GOut.checkerr(gl);
}
}
}
public static abstract class Attachment {
public abstract void attach(GOut g, GLFrameBuffer fbo, int point);
public abstract Coord sz();
public static Attach2D mk(TexGL tex) {return(new Attach2D(tex));}
public static AttachMS mk(TexMS tex) {return(new AttachMS(tex));}
public static AttachRBO mk(RenderBuffer rbo) {return(new AttachRBO(rbo));}
}
public static class Attach2D extends Attachment {
public final TexGL tex;
public final int level;
public Attach2D(TexGL tex, int level) {this.tex = tex; this.level = level;}
public Attach2D(TexGL tex) {this(tex, 0);}
public void attach(GOut g, GLFrameBuffer fbo, int point) {
g.gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, point, GL.GL_TEXTURE_2D, tex.glid(g), level);
}
public Coord sz() {return(tex.sz());}
}
public static class AttachMS extends Attachment {
public final TexMS tex;
public AttachMS(TexMS tex) {this.tex = tex;}
public void attach(GOut g, GLFrameBuffer fbo, int point) {
g.gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, point, GL3.GL_TEXTURE_2D_MULTISAMPLE, tex.glid(g), 0);
}
public Coord sz() {return(new Coord(tex.w, tex.h));}
}
public static class AttachRBO extends Attachment {
public final RenderBuffer rbo;
public AttachRBO(RenderBuffer rbo) {this.rbo = rbo;}
public void attach(GOut g, GLFrameBuffer fbo, int point) {
g.gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, point, GL.GL_RENDERBUFFER, rbo.glid(g.gl));
}
public Coord sz() {return(rbo.sz);}
}
public GLFrameBuffer(Attachment[] color, Attachment depth) {
this.color = color;
this.bufmask = new int[this.color.length];
if(depth == null) {
if(this.color.length == 0)
throw(new RuntimeException("Cannot create a framebuffer with neither color nor depth"));
this.altdepth = new RenderBuffer(color[0].sz(), GL2.GL_DEPTH_COMPONENT);
this.depth = new AttachRBO(this.altdepth);
} else {
this.altdepth = null;
this.depth = depth;
}
}
private static Attachment[] javaIsRetarded(TexGL[] color) {
Attachment[] ret = new Attachment[color.length];
for(int i = 0; i < color.length; i++)
ret[i] = new Attach2D(color[i]);
return(ret);
}
public GLFrameBuffer(TexGL[] color, TexGL depth) {
this(javaIsRetarded(color), (depth == null)?null:new Attach2D(depth));
}
public GLFrameBuffer(TexGL color, TexGL depth) {
this((color == null)?(new TexGL[0]):(new TexGL[] {color}), depth);
}
public Coord sz() {
return(depth.sz());
}
public void apply(GOut g) {
GL2 gl = g.gl;
synchronized(this) {
if((fbo != null) && (fbo.gl != gl))
dispose();
if(fbo == null) {
fbo = new FBO(gl);
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbo.id);
for(int i = 0; i < color.length; i++)
color[i].attach(g, this, GL.GL_COLOR_ATTACHMENT0 + i);
depth.attach(g, this, GL.GL_DEPTH_ATTACHMENT);
if(color.length == 0) {
gl.glDrawBuffer(GL.GL_NONE);
gl.glReadBuffer(GL.GL_NONE);
} else if(color.length > 1) {
for(int i = 0; i < color.length; i++)
bufmask[i] = GL.GL_COLOR_ATTACHMENT0 + i;
gl.glDrawBuffers(color.length, bufmask, 0);
}
GOut.checkerr(gl);
int st = gl.glCheckFramebufferStatus(GL.GL_FRAMEBUFFER);
if(st != GL.GL_FRAMEBUFFER_COMPLETE)
throw(new RuntimeException("FBO failed completeness test: " + st));
} else {
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, fbo.id);
}
}
gl.glViewport(0, 0, sz().x, sz().y);
}
public void mask(GOut g, int id, boolean flag) {
int nb = flag?(GL.GL_COLOR_ATTACHMENT0 + id):(GL.GL_NONE);
if(bufmask[id] != nb) {
bufmask[id] = nb;
g.gl.glDrawBuffers(color.length, bufmask, 0);
}
}
public void unapply(GOut g) {
GL2 gl = g.gl;
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, 0);
gl.glViewport(g.root().ul.x, g.root().ul.y, g.root().sz.x, g.root().sz.y);
}
public void prep(Buffer buf) {
buf.put(slot, this);
}
public void dispose() {
synchronized(this) {
if(fbo != null) {
fbo.dispose();
fbo = null;
}
}
if(altdepth != null)
altdepth.dispose();
}
}