#include "extension.h"
#include "ext_framebuffer_object.h"
#include "framebuffer.h"
+#include "misc.h"
#include "renderbuffer.h"
#include "texture2d.h"
+using namespace std;
+
namespace Msp {
namespace GL {
-Framebuffer::Framebuffer()
+Framebuffer::Framebuffer():
+ width(0),
+ height(0)
{
static RequireExtension _ext("GL_EXT_framebuffer_object");
void Framebuffer::bind() const
{
+ if(!cur_fbo)
+ get(GL_VIEWPORT, sys_viewport);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
cur_fbo=this;
+ if(width && height)
+ viewport(0, 0, width, height);
}
void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
{
maybe_bind();
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch, GL_RENDERBUFFER_EXT, rbuf.get_id());
+ get_or_create_attachment(attch)=rbuf;
+ check_size();
}
void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, int level)
{
maybe_bind();
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch, tex.get_target(), tex.get_id(), level);
+ get_or_create_attachment(attch)=tex;
+ check_size();
+}
+
+void Framebuffer::detach(FramebufferAttachment attch)
+{
+ maybe_bind();
+ for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
+ if(i->attachment==attch)
+ {
+ if(i->type==GL_RENDERBUFFER_EXT)
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch, GL_RENDERBUFFER_EXT, 0);
+ else if(i->type==GL_TEXTURE_2D)
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch, GL_TEXTURE_2D, 0, 0);
+ attachments.erase(i);
+ check_size();
+ return;
+ }
}
FramebufferStatus Framebuffer::check_status() const
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
cur_fbo=0;
+ viewport(sys_viewport[0], sys_viewport[1], sys_viewport[2], sys_viewport[3]);
}
}
bind();
}
+Framebuffer::Attachment &Framebuffer::get_or_create_attachment(FramebufferAttachment attch)
+{
+ for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
+ if(i->attachment==attch)
+ return *i;
+ attachments.push_back(Attachment(attch));
+ return attachments.back();
+}
+
+void Framebuffer::check_size()
+{
+ if(!attachments.empty())
+ {
+ const Attachment &attch=attachments.front();
+ if(attch.type==GL_RENDERBUFFER_EXT)
+ {
+ width=attch.rbuf->get_width();
+ height=attch.rbuf->get_height();
+ }
+ else if(attch.type==GL_TEXTURE_2D)
+ {
+ Texture2D *tex=static_cast<Texture2D *>(attch.tex);
+ width=tex->get_width();
+ height=tex->get_height();
+ }
+ if(cur_fbo==this)
+ viewport(0, 0, width, height);
+ }
+}
+
const Framebuffer *Framebuffer::cur_fbo=0;
+int Framebuffer::sys_viewport[4]={ 0, 0, 1, 1 };
+
+
+Framebuffer::Attachment::Attachment(FramebufferAttachment a):
+ attachment(a),
+ type(0)
+{ }
+
+Framebuffer::Attachment &Framebuffer::Attachment::operator=(Renderbuffer &r)
+{
+ type=GL_RENDERBUFFER_EXT;
+ rbuf=&r;
+ return *this;
+}
+
+Framebuffer::Attachment &Framebuffer::Attachment::operator=(Texture &t)
+{
+ type=t.get_target();
+ tex=&t;
+ return *this;
+}
+
+
+void viewport(int x, int y, unsigned w, unsigned h)
+{
+ glViewport(x, y, w, h);
+}
+
+void clear(BufferBits bits)
+{
+ glClear(bits);
+}
+
+void draw_buffer(RWBuffer buf)
+{
+ glDrawBuffer(buf);
+}
+
+void read_buffer(RWBuffer buf)
+{
+ glReadBuffer(buf);
+}
} // namespace GL
} // namespace Msp
#ifndef MSP_GL_FRAMEBUFFER_H_
#define MSP_GL_FRAMEBUFFER_H_
+#include <vector>
#include "gl.h"
#include "types.h"
namespace GL {
class Renderbuffer;
+class Texture;
class Texture2D;
enum FramebufferAttachment
FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE_EXT
};
+enum BufferBits
+{
+ COLOR_BUFFER_BIT = GL_COLOR_BUFFER_BIT,
+ DEPTH_BUFFER_BIT = GL_DEPTH_BUFFER_BIT,
+ STENCIL_BUFFER_BIT = GL_STENCIL_BUFFER_BIT,
+ ACCUM_BUFFER_BIT = GL_ACCUM_BUFFER_BIT
+};
+
+enum RWBuffer
+{
+ NO_BUFFER = GL_NONE,
+ FRONT_LEFT = GL_FRONT_LEFT,
+ FRONT_RIGHT = GL_FRONT_RIGHT,
+ BACK_LEFT = GL_BACK_LEFT,
+ BACK_RIGHT = GL_BACK_RIGHT,
+ FRONT = GL_FRONT,
+ BACK = GL_BACK,
+ LEFT = GL_LEFT,
+ RIGHT = GL_RIGHT,
+ FRONT_AND_BACK = GL_FRONT_AND_BACK
+};
+
/**
Framebuffer objects can be used to perform offscreen rendering. The most
common application is rendering to a texture, which can then be used for
class Framebuffer
{
private:
+ struct Attachment
+ {
+ FramebufferAttachment attachment;
+ GLenum type;
+ union
+ {
+ Renderbuffer *rbuf;
+ Texture *tex;
+ };
+
+ Attachment(FramebufferAttachment);
+ Attachment &operator=(Renderbuffer &);
+ Attachment &operator=(Texture &);
+ };
+
uint id;
+ std::vector<Attachment> attachments;
+ unsigned width;
+ unsigned height;
static const Framebuffer *cur_fbo;
+ static int sys_viewport[4];
public:
Framebuffer();
void attach(FramebufferAttachment attch, Renderbuffer &rbuf);
void attach(FramebufferAttachment attch, Texture2D &tex, int level);
+ void detach(FramebufferAttachment attch);
/**
Checks the completeness status of the framebuffer. Returns
static void unbind();
private:
void maybe_bind() const;
+ Attachment &get_or_create_attachment(FramebufferAttachment);
+ void check_size();
};
+inline BufferBits operator|(BufferBits a, BufferBits b)
+{ return static_cast<BufferBits>(static_cast<int>(a)|static_cast<int>(b)); }
+
+void viewport(int, int, unsigned, unsigned);
+void clear(BufferBits);
+void draw_buffer(RWBuffer);
+void read_buffer(RWBuffer);
+
} // namespace GL
} // namespace Msp
glGetIntegerv(state, &data);
}
+void get(GLenum state, int *data)
+{
+ glGetIntegerv(state, data);
+}
+
int get_i(GLenum state)
{
int data;
- get(state, data);
+ get(state, &data);
return data;
}
void disable(GLenum);
void set(GLenum, bool);
+///Deprecated (can't properly pass an array through a reference)
void get(GLenum, int &);
+
+void get(GLenum, int *);
int get_i(GLenum);
class Bind
if(pass.lighting)
pass.lighting->bind();
for(vector<Effect *>::const_iterator i=pass.effects.begin(); i!=pass.effects.end(); ++i)
- {
(*i)->prepare();
- glViewport(0, 0, width, height);
- }
for(vector<const Renderable *>::const_iterator i=renderables.begin(); i!=renderables.end(); ++i)
(*i)->render(tag);
for(vector<Effect *>::const_iterator i=pass.effects.end(); i--!=pass.effects.begin();)
if(fbo)
{
fbo->bind();
- glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT);
}
for(vector<Effect *>::const_iterator i=effects.begin(); i!=effects.end(); ++i)
- {
(*i)->prepare();
- glViewport(0, 0, width, height);
- }
for(vector<Tag>::const_iterator i=pass_order.begin(); i!=pass_order.end(); ++i)
render(*i);
for(vector<Effect *>::const_iterator i=effects.end(); i--!=effects.begin();)
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id);
}
-void Renderbuffer::storage(PixelFormat fmt, sizei width, sizei height)
+void Renderbuffer::storage(PixelFormat fmt, sizei w, sizei h)
{
bind();
+ width=w;
+ height=h;
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, fmt, width, height);
}
{
private:
uint id;
+ sizei width;
+ sizei height;
public:
Renderbuffer();
~Renderbuffer();
uint get_id() const { return id; }
+ sizei get_width() const { return width; }
+ sizei get_height() const { return height; }
void bind() const;
size(s),
scene(c),
light(l),
+ unit(3),
radius(1)
{
depth_buf.set_min_filter(LINEAR);
depth_buf.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
depth_buf.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
fbo.attach(DEPTH_ATTACHMENT, depth_buf, 0);
- glDrawBuffer(GL_NONE);
+ draw_buffer(NO_BUFFER);
Framebuffer::unbind();
Texture::unbind();
}
radius=r;
}
+void ShadowMap::set_texture_unit(unsigned u)
+{
+ unit=u;
+}
+
void ShadowMap::prepare()
{
const Vector4 &lpos=light.get_position();
const Framebuffer *old_fbo=Framebuffer::current();
fbo.bind();
- glViewport(0, 0, size, size);
- glClear(GL_DEPTH_BUFFER_BIT);
+ clear(DEPTH_BUFFER_BIT);
scene.render("shadow");
matrix_mode(PROJECTION);
Framebuffer::unbind();
}
- depth_buf.bind_to(3);
+ depth_buf.bind_to(unit);
float diam=radius*2;
float s_eq[4]={ matrix[0]/diam, matrix[4]/diam, matrix[8]/diam, matrix[12]/diam+0.5 };
float t_eq[4]={ matrix[1]/diam, matrix[5]/diam, matrix[9]/diam, matrix[13]/diam+0.5 };
void ShadowMap::cleanup()
{
- TexUnit::activate(3);
+ TexUnit::activate(unit);
Texture::unbind();
disable(GL_TEXTURE_GEN_S);
disable(GL_TEXTURE_GEN_T);
class Light;
class Scene;
+/**
+Creates shadows on a Scene through a shadow map texture. In the preparation
+phase, the scene is rendered to a depth texture from the point of view of the
+lightsource. This texture is then used in the rendering phase together with
+texture coordinate generation to determine whether each fragment is lit.
+*/
class ShadowMap: public Effect
{
private:
const Scene &scene;
const Light &light;
Framebuffer fbo;
-public:
+ unsigned unit;
Texture2D depth_buf;
-private:
Vector3 target;
float radius;
public:
ShadowMap(unsigned, const Scene &, const Light &);
+
+ /** Sets the ShadowMap target point and radius. The transformation matrix is
+ computed so that a sphere with the specified parameters will be completely
+ covered by the ShadowMap. */
void set_target(const Vector3 &, float);
+
+ /** Sets the texture unit to bind the shadow map to during the rendering
+ phase. The default is texture unit 3. */
+ void set_texture_unit(unsigned);
+
virtual void prepare();
virtual void cleanup();
};