867 lines
24 KiB
C++
867 lines
24 KiB
C++
/* This file is part of the Pangolin Project.
|
|
* http://github.com/stevenlovegrove/Pangolin
|
|
*
|
|
* Copyright (c) 2011 Steven Lovegrove
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <pangolin/gl/gl.h>
|
|
#include <pangolin/gl/glpixformat.h>
|
|
#include <pangolin/display/display.h>
|
|
#include <pangolin/image/image_io.h>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
|
|
namespace pangolin
|
|
{
|
|
|
|
////////////////////////////////////////////////
|
|
// Implementation of gl.h
|
|
////////////////////////////////////////////////
|
|
|
|
#ifndef HAVE_GLES
|
|
const int MAX_ATTACHMENTS = 8;
|
|
const static GLuint attachment_buffers[] = {
|
|
GL_COLOR_ATTACHMENT0_EXT,
|
|
GL_COLOR_ATTACHMENT1_EXT,
|
|
GL_COLOR_ATTACHMENT2_EXT,
|
|
GL_COLOR_ATTACHMENT3_EXT,
|
|
GL_COLOR_ATTACHMENT4_EXT,
|
|
GL_COLOR_ATTACHMENT5_EXT,
|
|
GL_COLOR_ATTACHMENT6_EXT,
|
|
GL_COLOR_ATTACHMENT7_EXT
|
|
};
|
|
#else // HAVE_GLES
|
|
const int MAX_ATTACHMENTS = 1;
|
|
const static GLuint attachment_buffers[] = {
|
|
GL_COLOR_ATTACHMENT0_EXT
|
|
};
|
|
#endif // HAVE_GLES
|
|
|
|
const static size_t datatype_bytes[] = {
|
|
1, // #define GL_BYTE 0x1400
|
|
1, // #define GL_UNSIGNED_BYTE 0x1401
|
|
2, // #define GL_SHORT 0x1402
|
|
2, // #define GL_UNSIGNED_SHORT 0x1403
|
|
4, // #define GL_INT 0x1404
|
|
4, // #define GL_UNSIGNED_INT 0x1405
|
|
4, // #define GL_FLOAT 0x1406
|
|
2, // #define GL_2_BYTES 0x1407
|
|
3, // #define GL_3_BYTES 0x1408
|
|
4, // #define GL_4_BYTES 0x1409
|
|
8 // #define GL_DOUBLE 0x140A
|
|
};
|
|
|
|
const static size_t format_channels[] = {
|
|
1, // #define GL_RED 0x1903
|
|
1, // #define GL_GREEN 0x1904
|
|
1, // #define GL_BLUE 0x1905
|
|
1, // #define GL_ALPHA 0x1906
|
|
3, // #define GL_RGB 0x1907
|
|
4, // #define GL_RGBA 0x1908
|
|
1, // #define GL_LUMINANCE 0x1909
|
|
2 // #define GL_LUMINANCE_ALPHA 0x190A
|
|
};
|
|
|
|
inline size_t GlDataTypeBytes(GLenum type)
|
|
{
|
|
return datatype_bytes[type - GL_BYTE];
|
|
}
|
|
|
|
inline size_t GlFormatChannels(GLenum data_layout)
|
|
{
|
|
if (data_layout == GL_BGR) return 3;
|
|
if (data_layout == GL_BGRA) return 4;
|
|
return format_channels[data_layout - GL_RED];
|
|
}
|
|
|
|
//template<typename T>
|
|
//struct GlDataTypeTrait {};
|
|
//template<> struct GlDataTypeTrait<float>{ static const GLenum type = GL_FLOAT; };
|
|
//template<> struct GlDataTypeTrait<int>{ static const GLenum type = GL_INT; };
|
|
//template<> struct GlDataTypeTrait<unsigned char>{ static const GLenum type = GL_UNSIGNED_BYTE; };
|
|
|
|
inline GlTexture::GlTexture()
|
|
: internal_format(0), tid(0), width(0), height(0)
|
|
{
|
|
// Not a texture constructor
|
|
}
|
|
|
|
inline GlTexture::GlTexture(GLint width, GLint height, GLint internal_format, bool sampling_linear, int border, GLenum glformat, GLenum gltype, GLvoid* data )
|
|
: internal_format(0), tid(0)
|
|
{
|
|
Reinitialise(width,height,internal_format,sampling_linear,border,glformat,gltype,data);
|
|
}
|
|
|
|
inline GlTexture::GlTexture(const TypedImage& img, bool sampling_linear)
|
|
{
|
|
this->Load(img, sampling_linear);
|
|
}
|
|
|
|
inline GlTexture::GlTexture(GlTexture&& tex)
|
|
{
|
|
*this = std::move(tex);
|
|
}
|
|
|
|
inline GlTexture& GlTexture::operator=(GlTexture&& tex)
|
|
{
|
|
if (&tex != this) {
|
|
internal_format = tex.internal_format;
|
|
tid = tex.tid;
|
|
width = tex.width;
|
|
height = tex.height;
|
|
|
|
tex.internal_format = 0;
|
|
tex.tid = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
inline bool GlTexture::IsValid() const
|
|
{
|
|
return tid != 0;
|
|
}
|
|
|
|
inline void GlTexture::Delete()
|
|
{
|
|
// We have no GL context whilst exiting.
|
|
if(internal_format!=0 && !pangolin::ShouldQuit() ) {
|
|
glDeleteTextures(1,&tid);
|
|
internal_format = 0;
|
|
tid = 0;
|
|
width = 0;
|
|
height = 0;
|
|
}
|
|
}
|
|
|
|
inline GlTexture::~GlTexture()
|
|
{
|
|
// We have no GL context whilst exiting.
|
|
if(internal_format!=0 && !pangolin::ShouldQuit() ) {
|
|
glDeleteTextures(1,&tid);
|
|
}
|
|
}
|
|
|
|
inline void GlTexture::Bind() const
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, tid);
|
|
}
|
|
|
|
inline void GlTexture::Unbind() const
|
|
{
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
inline void GlTexture::Reinitialise(GLsizei w, GLsizei h, GLint int_format, bool sampling_linear, int border, GLenum glformat, GLenum gltype, GLvoid* data )
|
|
{
|
|
if(tid!=0) {
|
|
glDeleteTextures(1,&tid);
|
|
}
|
|
|
|
internal_format = int_format;
|
|
width = w;
|
|
height = h;
|
|
|
|
glGenTextures(1,&tid);
|
|
Bind();
|
|
|
|
// GL_LUMINANCE and GL_FLOAT don't seem to actually affect buffer, but some values are required
|
|
// for call to succeed.
|
|
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, border, glformat, gltype, data);
|
|
|
|
if(sampling_linear) {
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
}else{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
}
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline void GlTexture::Upload(
|
|
const void* data,
|
|
GLenum data_format, GLenum data_type
|
|
) {
|
|
Bind();
|
|
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,data_format,data_type,data);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline void GlTexture::Upload(
|
|
const void* data,
|
|
GLsizei tex_x_offset, GLsizei tex_y_offset,
|
|
GLsizei data_w, GLsizei data_h,
|
|
GLenum data_format, GLenum data_type )
|
|
{
|
|
Bind();
|
|
glTexSubImage2D(GL_TEXTURE_2D,0,tex_x_offset,tex_y_offset,data_w,data_h,data_format,data_type,data);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline void GlTexture::Load(const TypedImage& image, bool sampling_linear)
|
|
{
|
|
GlPixFormat fmt(image.fmt);
|
|
Reinitialise((GLint)image.w, (GLint)image.h, GL_RGBA32F, sampling_linear, 0, fmt.glformat, fmt.gltype, image.ptr );
|
|
}
|
|
|
|
inline void GlTexture::LoadFromFile(const std::string& filename, bool sampling_linear)
|
|
{
|
|
TypedImage image = LoadImage(filename);
|
|
Load(image, sampling_linear);
|
|
}
|
|
|
|
#ifndef HAVE_GLES
|
|
inline void GlTexture::Download(void* image, GLenum data_layout, GLenum data_type) const
|
|
{
|
|
Bind();
|
|
glGetTexImage(GL_TEXTURE_2D, 0, data_layout, data_type, image);
|
|
Unbind();
|
|
}
|
|
|
|
inline void GlTexture::Download(TypedImage& image) const
|
|
{
|
|
switch (internal_format)
|
|
{
|
|
case GL_LUMINANCE8:
|
|
image.Reinitialise(width, height, PixelFormatFromString("GRAY8") );
|
|
Download(image.ptr, GL_LUMINANCE, GL_UNSIGNED_BYTE);
|
|
break;
|
|
case GL_LUMINANCE16:
|
|
image.Reinitialise(width, height, PixelFormatFromString("GRAY16LE") );
|
|
Download(image.ptr, GL_LUMINANCE, GL_UNSIGNED_SHORT);
|
|
break;
|
|
case GL_RGB8:
|
|
image.Reinitialise(width, height, PixelFormatFromString("RGB24"));
|
|
Download(image.ptr, GL_RGB, GL_UNSIGNED_BYTE);
|
|
break;
|
|
case GL_RGBA8:
|
|
image.Reinitialise(width, height, PixelFormatFromString("RGBA32"));
|
|
Download(image.ptr, GL_RGBA, GL_UNSIGNED_BYTE);
|
|
break;
|
|
case GL_RGB16:
|
|
image.Reinitialise(width, height, PixelFormatFromString("RGB48"));
|
|
Download(image.ptr, GL_RGB, GL_UNSIGNED_SHORT);
|
|
break;
|
|
case GL_RGBA16:
|
|
image.Reinitialise(width, height, PixelFormatFromString("RGBA64"));
|
|
Download(image.ptr, GL_RGBA, GL_UNSIGNED_SHORT);
|
|
break;
|
|
case GL_LUMINANCE:
|
|
case GL_LUMINANCE32F_ARB:
|
|
image.Reinitialise(width, height, PixelFormatFromString("GRAY32F"));
|
|
Download(image.ptr, GL_LUMINANCE, GL_FLOAT);
|
|
break;
|
|
case GL_RGB:
|
|
case GL_RGB32F:
|
|
image.Reinitialise(width, height, PixelFormatFromString("RGB96F"));
|
|
Download(image.ptr, GL_RGB, GL_FLOAT);
|
|
break;
|
|
case GL_RGBA:
|
|
case GL_RGBA32F:
|
|
image.Reinitialise(width, height, PixelFormatFromString("RGBA128F"));
|
|
Download(image.ptr, GL_RGBA, GL_FLOAT);
|
|
break;
|
|
default:
|
|
throw std::runtime_error(
|
|
"GlTexture::Download - Unknown internal format (" +
|
|
pangolin::Convert<std::string,GLint>::Do(internal_format) +
|
|
")"
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
inline void GlTexture::CopyFrom(const GlTexture& tex)
|
|
{
|
|
if(!tid || width != tex.width || height != tex.height ||
|
|
internal_format != tex.internal_format)
|
|
{
|
|
Reinitialise(tex.width, tex.height, tex.internal_format, true);
|
|
}
|
|
|
|
glCopyImageSubData(tex.tid, GL_TEXTURE_2D, 0, 0, 0, 0,
|
|
tid, GL_TEXTURE_2D, 0, 0, 0, 0,
|
|
width, height, 1);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline void GlTexture::Save(const std::string& filename, bool top_line_first)
|
|
{
|
|
TypedImage image;
|
|
Download(image);
|
|
pangolin::SaveImage(image, filename, top_line_first);
|
|
}
|
|
#endif // HAVE_GLES
|
|
|
|
inline void GlTexture::SetLinear()
|
|
{
|
|
Bind();
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
Unbind();
|
|
}
|
|
|
|
inline void GlTexture::SetNearestNeighbour()
|
|
{
|
|
Bind();
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
Unbind();
|
|
}
|
|
|
|
inline void GlTexture::RenderToViewport(const bool flip) const
|
|
{
|
|
if(flip) {
|
|
RenderToViewportFlipY();
|
|
}else{
|
|
RenderToViewport();
|
|
}
|
|
}
|
|
|
|
inline void GlTexture::RenderToViewport() const
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 };
|
|
glVertexPointer(2, GL_FLOAT, 0, sq_vert);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
GLfloat sq_tex[] = { 0,0, 1,0, 1,1, 0,1 };
|
|
glTexCoordPointer(2, GL_FLOAT, 0, sq_tex);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
Bind();
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
inline void GlTexture::RenderToViewport(Viewport tex_vp, bool flipx, bool flipy) const
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 };
|
|
glVertexPointer(2, GL_FLOAT, 0, sq_vert);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
GLfloat l = tex_vp.l / (float)(width);
|
|
GLfloat b = tex_vp.b / (float)(height);
|
|
GLfloat r = (tex_vp.l+tex_vp.w) / (float)(width);
|
|
GLfloat t = (tex_vp.b+tex_vp.h) / (float)(height);
|
|
|
|
if(flipx) std::swap(l,r);
|
|
if(flipy) std::swap(b,t);
|
|
|
|
GLfloat sq_tex[] = { l,b, r,b, r,t, l,t };
|
|
glTexCoordPointer(2, GL_FLOAT, 0, sq_tex);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
Bind();
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
inline void GlTexture::RenderToViewportFlipY() const
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
GLfloat sq_vert[] = { -1,-1, 1,-1, 1, 1, -1, 1 };
|
|
glVertexPointer(2, GL_FLOAT, 0, sq_vert);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
GLfloat sq_tex[] = { 0,1, 1,1, 1,0, 0,0 };
|
|
glTexCoordPointer(2, GL_FLOAT, 0, sq_tex);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
Bind();
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
inline void GlTexture::RenderToViewportFlipXFlipY() const
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
GLfloat sq_vert[] = { 1,1, -1,1, -1,-1, 1,-1 };
|
|
glVertexPointer(2, GL_FLOAT, 0, sq_vert);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
GLfloat sq_tex[] = { 0,0, 1,0, 1,1, 0,1 };
|
|
glTexCoordPointer(2, GL_FLOAT, 0, sq_tex);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
Bind();
|
|
|
|
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
|
|
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline GlRenderBuffer::GlRenderBuffer()
|
|
: width(0), height(0), rbid(0)
|
|
{
|
|
}
|
|
|
|
inline GlRenderBuffer::GlRenderBuffer(GLint width, GLint height, GLint internal_format )
|
|
: width(0), height(0), rbid(0)
|
|
{
|
|
Reinitialise(width,height,internal_format);
|
|
}
|
|
|
|
#ifndef HAVE_GLES
|
|
inline void GlRenderBuffer::Reinitialise(GLint width, GLint height, GLint internal_format)
|
|
{
|
|
if( this->width != 0 ) {
|
|
glDeleteRenderbuffersEXT(1, &rbid);
|
|
}
|
|
|
|
this->width = width;
|
|
this->height = height;
|
|
glGenRenderbuffersEXT(1, &rbid);
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rbid);
|
|
glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internal_format, width, height);
|
|
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
|
|
}
|
|
|
|
inline GlRenderBuffer::~GlRenderBuffer()
|
|
{
|
|
// We have no GL context whilst exiting.
|
|
if( width!=0 && !pangolin::ShouldQuit() ) {
|
|
glDeleteRenderbuffersEXT(1, &rbid);
|
|
}
|
|
}
|
|
#else
|
|
inline void GlRenderBuffer::Reinitialise(GLint width, GLint height, GLint internal_format)
|
|
{
|
|
if( width!=0 ) {
|
|
glDeleteTextures(1, &rbid);
|
|
}
|
|
|
|
// Use a texture instead...
|
|
glGenTextures(1, &rbid);
|
|
glBindTexture(GL_TEXTURE_2D, rbid);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, internal_format,
|
|
width, height,
|
|
0, internal_format, GL_UNSIGNED_SHORT, NULL);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
}
|
|
|
|
inline GlRenderBuffer::~GlRenderBuffer()
|
|
{
|
|
// We have no GL context whilst exiting.
|
|
if( width!=0 && !pangolin::ShouldQuit() ) {
|
|
glDeleteTextures(1, &rbid);
|
|
}
|
|
}
|
|
#endif // HAVE_GLES
|
|
|
|
inline GlRenderBuffer::GlRenderBuffer(GlRenderBuffer&& tex)
|
|
: width(tex.width), height(tex.height), rbid(tex.rbid)
|
|
{
|
|
tex.rbid = tex.width = tex.height = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline GlFramebuffer::GlFramebuffer()
|
|
: fbid(0), attachments(0)
|
|
{
|
|
}
|
|
|
|
inline GlFramebuffer::~GlFramebuffer()
|
|
{
|
|
if(fbid) {
|
|
glDeleteFramebuffersEXT(1, &fbid);
|
|
}
|
|
}
|
|
|
|
inline GlFramebuffer::GlFramebuffer(GlTexture& colour, GlRenderBuffer& depth)
|
|
: attachments(0)
|
|
{
|
|
glGenFramebuffersEXT(1, &fbid);
|
|
AttachColour(colour);
|
|
AttachDepth(depth);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline GlFramebuffer::GlFramebuffer(GlTexture& colour0, GlTexture& colour1, GlRenderBuffer& depth)
|
|
: attachments(0)
|
|
{
|
|
glGenFramebuffersEXT(1, &fbid);
|
|
AttachColour(colour0);
|
|
AttachColour(colour1);
|
|
AttachDepth(depth);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline GlFramebuffer::GlFramebuffer(GlTexture& colour0, GlTexture& colour1, GlTexture& colour2, GlRenderBuffer& depth)
|
|
: attachments(0)
|
|
{
|
|
glGenFramebuffersEXT(1, &fbid);
|
|
AttachColour(colour0);
|
|
AttachColour(colour1);
|
|
AttachColour(colour2);
|
|
AttachDepth(depth);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline GlFramebuffer::GlFramebuffer(GlTexture& colour0, GlTexture& colour1, GlTexture& colour2, GlTexture& colour3, GlRenderBuffer& depth)
|
|
: attachments(0)
|
|
{
|
|
glGenFramebuffersEXT(1, &fbid);
|
|
AttachColour(colour0);
|
|
AttachColour(colour1);
|
|
AttachColour(colour2);
|
|
AttachColour(colour3);
|
|
AttachDepth(depth);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
inline void GlFramebuffer::Bind() const
|
|
{
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbid);
|
|
#ifndef HAVE_GLES
|
|
glDrawBuffers( attachments, attachment_buffers );
|
|
#endif
|
|
}
|
|
|
|
inline void GlFramebuffer::Reinitialise()
|
|
{
|
|
if(fbid) {
|
|
glDeleteFramebuffersEXT(1, &fbid);
|
|
}
|
|
glGenFramebuffersEXT(1, &fbid);
|
|
}
|
|
|
|
inline void GlFramebuffer::Unbind() const
|
|
{
|
|
#ifndef HAVE_GLES
|
|
glDrawBuffers( 1, attachment_buffers );
|
|
#endif
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
}
|
|
|
|
inline GLenum GlFramebuffer::AttachColour(GlTexture& tex )
|
|
{
|
|
if(!fbid) Reinitialise();
|
|
|
|
const GLenum color_attachment = GL_COLOR_ATTACHMENT0_EXT + attachments;
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbid);
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, color_attachment, GL_TEXTURE_2D, tex.tid, 0);
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
attachments++;
|
|
CheckGlDieOnError();
|
|
return color_attachment;
|
|
}
|
|
|
|
inline void GlFramebuffer::AttachDepth(GlRenderBuffer& rb )
|
|
{
|
|
if(!fbid) Reinitialise();
|
|
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbid);
|
|
#if !defined(HAVE_GLES)
|
|
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rb.rbid);
|
|
#elif defined(HAVE_GLES_2)
|
|
glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, rb.rbid, 0);
|
|
#else
|
|
throw std::exception();
|
|
#endif
|
|
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
|
|
CheckGlDieOnError();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline GlBufferData::GlBufferData()
|
|
: bo(0)
|
|
{
|
|
}
|
|
|
|
inline GlBufferData::GlBufferData(GlBufferType buffer_type, GLuint size_bytes, GLenum gluse, const unsigned char* data )
|
|
: bo(0)
|
|
{
|
|
Reinitialise(buffer_type, size_bytes, gluse, data );
|
|
}
|
|
|
|
//! Move Constructor
|
|
inline GlBufferData::GlBufferData(GlBufferData&& tex)
|
|
: bo(0)
|
|
{
|
|
*this = std::move(tex);
|
|
}
|
|
inline GlBufferData& GlBufferData::operator=(GlBufferData&& tex)
|
|
{
|
|
Free();
|
|
this->bo = tex.bo;
|
|
this->buffer_type = tex.buffer_type;
|
|
this->gluse = tex.gluse;
|
|
this->size_bytes = tex.size_bytes;
|
|
tex.bo = 0;
|
|
return *this;
|
|
}
|
|
|
|
inline GlBufferData::~GlBufferData()
|
|
{
|
|
Free();
|
|
}
|
|
|
|
inline void GlBufferData::Free()
|
|
{
|
|
if(bo!=0) {
|
|
glDeleteBuffers(1, &bo);
|
|
}
|
|
}
|
|
|
|
inline bool GlBufferData::IsValid() const
|
|
{
|
|
return bo != 0;
|
|
}
|
|
|
|
inline size_t GlBufferData::SizeBytes() const
|
|
{
|
|
return size_bytes;
|
|
}
|
|
|
|
inline void GlBufferData::Reinitialise(GlBufferType buffer_type, GLuint size_bytes, GLenum gluse, const unsigned char* data )
|
|
{
|
|
if(!bo) {
|
|
glGenBuffers(1, &bo);
|
|
}
|
|
|
|
this->buffer_type = buffer_type;
|
|
this->gluse = gluse;
|
|
this->size_bytes = size_bytes;
|
|
|
|
Bind();
|
|
glBufferData(buffer_type, size_bytes, data, gluse);
|
|
Unbind();
|
|
}
|
|
|
|
inline void GlBufferData::Bind() const
|
|
{
|
|
glBindBuffer(buffer_type, bo);
|
|
}
|
|
|
|
inline void GlBufferData::Unbind() const
|
|
{
|
|
glBindBuffer(buffer_type, 0);
|
|
}
|
|
|
|
inline void GlBufferData::Upload(const GLvoid* data, GLsizeiptr size_bytes, GLintptr offset)
|
|
{
|
|
if(offset + size_bytes > this->size_bytes) {
|
|
throw std::runtime_error("GlBufferData: Trying to upload past capacity.");
|
|
}
|
|
|
|
Bind();
|
|
glBufferSubData(buffer_type,offset,size_bytes,data);
|
|
Unbind();
|
|
}
|
|
|
|
inline void GlBufferData::Download(GLvoid* data, GLsizeiptr size_bytes, GLintptr offset) const
|
|
{
|
|
Bind();
|
|
glGetBufferSubData(buffer_type, offset, size_bytes, data);
|
|
Unbind();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline GlBuffer::GlBuffer()
|
|
: GlBufferData(), num_elements(0)
|
|
{
|
|
}
|
|
|
|
inline GlBuffer::GlBuffer(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse )
|
|
: GlBufferData(buffer_type, num_elements * count_per_element * GlDataTypeBytes(datatype), gluse),
|
|
datatype(datatype), num_elements(num_elements), count_per_element(count_per_element)
|
|
{
|
|
}
|
|
|
|
|
|
inline GlBuffer::GlBuffer(GlBuffer&& o)
|
|
: GlBufferData()
|
|
{
|
|
*this = std::move(o);
|
|
}
|
|
|
|
inline GlBuffer& GlBuffer::operator=(GlBuffer&& o)
|
|
{
|
|
datatype = o.datatype;
|
|
num_elements = o.num_elements;
|
|
count_per_element = o.count_per_element;
|
|
GlBufferData::operator =(std::move(o));
|
|
return *this;
|
|
}
|
|
|
|
inline void GlBuffer::Reinitialise(GlBufferType buffer_type, GLuint num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse, const unsigned char* data )
|
|
{
|
|
this->datatype = datatype;
|
|
this->num_elements = num_elements;
|
|
this->count_per_element = count_per_element;
|
|
const GLuint size_bytes = num_elements * count_per_element * GlDataTypeBytes(datatype);
|
|
GlBufferData::Reinitialise(buffer_type, size_bytes, gluse, data);
|
|
}
|
|
|
|
inline void GlBuffer::Reinitialise(GlBuffer const& other )
|
|
{
|
|
Reinitialise(other.buffer_type, other.num_elements, other.datatype, other.count_per_element, other.gluse);
|
|
}
|
|
|
|
inline void GlBuffer::Resize(GLuint new_num_elements)
|
|
{
|
|
if(bo!=0) {
|
|
#ifndef HAVE_GLES
|
|
// Backup current data, reinit memory, restore old data
|
|
const size_t backup_elements = std::min(new_num_elements,num_elements);
|
|
const size_t backup_size_bytes = backup_elements*GlDataTypeBytes(datatype)*count_per_element;
|
|
unsigned char* backup = new unsigned char[backup_size_bytes];
|
|
Bind();
|
|
glGetBufferSubData(buffer_type, 0, backup_size_bytes, backup);
|
|
glBufferData(buffer_type, new_num_elements*GlDataTypeBytes(datatype)*count_per_element, 0, gluse);
|
|
glBufferSubData(buffer_type, 0, backup_size_bytes, backup);
|
|
Unbind();
|
|
delete[] backup;
|
|
#else
|
|
throw std::exception();
|
|
#endif
|
|
}else{
|
|
Reinitialise(buffer_type, new_num_elements, datatype, count_per_element, gluse);
|
|
}
|
|
num_elements = new_num_elements;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
inline GlSizeableBuffer::GlSizeableBuffer(GlBufferType buffer_type, GLuint initial_num_elements, GLenum datatype, GLuint count_per_element, GLenum gluse )
|
|
: GlBuffer(buffer_type, initial_num_elements, datatype, count_per_element, gluse), m_num_verts(0)
|
|
{
|
|
|
|
}
|
|
|
|
inline void GlSizeableBuffer::Clear()
|
|
{
|
|
m_num_verts = 0;
|
|
}
|
|
|
|
#ifdef USE_EIGEN
|
|
template<typename Derived> inline
|
|
void GlSizeableBuffer::Add(const Eigen::DenseBase<Derived>& vec)
|
|
{
|
|
typedef typename Eigen::DenseBase<Derived>::Scalar Scalar;
|
|
assert(vec.rows()==GlBuffer::count_per_element);
|
|
CheckResize(m_num_verts + 1);
|
|
// TODO: taking address of first element is really dodgey. Need to work out
|
|
// when this is okay!
|
|
Upload(&vec(0,0), sizeof(Scalar)*vec.rows()*vec.cols(), sizeof(Scalar)*vec.rows()*m_num_verts);
|
|
m_num_verts += vec.cols();
|
|
}
|
|
|
|
template<typename Derived> inline
|
|
void GlSizeableBuffer::Update(const Eigen::DenseBase<Derived>& vec, size_t position )
|
|
{
|
|
typedef typename Eigen::DenseBase<Derived>::Scalar Scalar;
|
|
assert(vec.rows()==GlBuffer::count_per_element);
|
|
CheckResize(position + vec.cols() );
|
|
// TODO: taking address of first element is really dodgey. Need to work out
|
|
// when this is okay!
|
|
Upload(&vec(0,0), sizeof(Scalar)*vec.rows()*vec.cols(), sizeof(Scalar)*vec.rows()*position );
|
|
m_num_verts = std::max(position+vec.cols(), m_num_verts);
|
|
}
|
|
#endif
|
|
|
|
inline size_t GlSizeableBuffer::start() const {
|
|
return 0;
|
|
}
|
|
|
|
inline size_t GlSizeableBuffer::size() const {
|
|
return m_num_verts;
|
|
}
|
|
|
|
inline void GlSizeableBuffer::CheckResize(size_t num_verts)
|
|
{
|
|
if( num_verts > GlBuffer::num_elements) {
|
|
const size_t new_size = NextSize(num_verts);
|
|
GlBuffer::Resize((GLuint)new_size);
|
|
}
|
|
}
|
|
|
|
inline size_t GlSizeableBuffer::NextSize(size_t min_size) const
|
|
{
|
|
size_t new_size = std::max(GlBuffer::num_elements, 1u);
|
|
while(new_size < min_size) {
|
|
new_size *= 2;
|
|
}
|
|
return new_size;
|
|
}
|
|
|
|
}
|