#ifndef GLBUFFERS_H
#define GLBUFFERS_H
#include "glextensions.h"
#include <QtWidgets>
#include <QtOpenGL>
#define BUFFER_OFFSET(i) ((char*)0 + (i))
#define SIZE_OF_MEMBER(cls, member) sizeof(static_cast<cls *>(0)->member)
#define GLBUFFERS_ASSERT_OPENGL(prefix, assertion, returnStatement) \
if (m_failed || !(assertion)) { \
if (!m_failed) qCritical(prefix ": The necessary OpenGL functions are not available."); \
m_failed = true; \
returnStatement; \
}
void qgluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);
class QMatrix4x4;
class GLTexture
{
public:
GLTexture();
virtual ~GLTexture();
virtual void bind() = 0;
virtual void unbind() = 0;
virtual bool failed() const {return m_failed;}
protected:
GLuint m_texture;
bool m_failed;
};
class GLFrameBufferObject
{
public:
friend class GLRenderTargetCube;
GLFrameBufferObject(int width, int height);
virtual ~GLFrameBufferObject();
bool isComplete();
virtual bool failed() const {return m_failed;}
protected:
void setAsRenderTarget(bool state = true);
GLuint m_fbo;
GLuint m_depthBuffer;
int m_width, m_height;
bool m_failed;
};
class GLTexture2D : public GLTexture
{
public:
GLTexture2D(int width, int height);
GLTexture2D(const QString& fileName, int width = 0, int height = 0);
void load(int width, int height, QRgb *data);
virtual void bind();
virtual void unbind();
};
class GLTexture3D : public GLTexture
{
public:
GLTexture3D(int width, int height, int depth);
void load(int width, int height, int depth, QRgb *data);
virtual void bind();
virtual void unbind();
};
class GLTextureCube : public GLTexture
{
public:
GLTextureCube(int size);
GLTextureCube(const QStringList& fileNames, int size = 0);
void load(int size, int face, QRgb *data);
virtual void bind();
virtual void unbind();
};
class GLRenderTargetCube : public GLTextureCube
{
public:
GLRenderTargetCube(int size);
void begin(int face);
void end();
virtual bool failed() const {return m_failed || m_fbo.failed();}
static void getViewMatrix(QMatrix4x4& mat, int face);
static void getProjectionMatrix(QMatrix4x4& mat, float nearZ, float farZ);
private:
GLFrameBufferObject m_fbo;
};
struct VertexDescription
{
enum
{
Null = 0,
Position,
TexCoord,
Normal,
Color,
};
int field;
int type;
int count;
int offset;
int index;
};
template<class T>
class GLVertexBuffer
{
public:
GLVertexBuffer(int length, const T *data = 0, int mode = GL_STATIC_DRAW)
: m_length(0)
, m_mode(mode)
, m_buffer(0)
, m_failed(false)
{
GLBUFFERS_ASSERT_OPENGL("GLVertexBuffer::GLVertexBuffer", glGenBuffers && glBindBuffer && glBufferData, return)
glGenBuffers(1, &m_buffer);
glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
glBufferData(GL_ARRAY_BUFFER, (m_length = length) * sizeof(T), data, mode);
}
~GLVertexBuffer()
{
GLBUFFERS_ASSERT_OPENGL("GLVertexBuffer::~GLVertexBuffer", glDeleteBuffers, return)
glDeleteBuffers(1, &m_buffer);
}
void bind()
{
GLBUFFERS_ASSERT_OPENGL("GLVertexBuffer::bind", glBindBuffer, return)
glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
for (VertexDescription *desc = T::description; desc->field != VertexDescription::Null; ++desc) {
switch (desc->field) {
case VertexDescription::Position:
glVertexPointer(desc->count, desc->type, sizeof(T), BUFFER_OFFSET(desc->offset));
glEnableClientState(GL_VERTEX_ARRAY);
break;
case VertexDescription::TexCoord:
glTexCoordPointer(desc->count, desc->type, sizeof(T), BUFFER_OFFSET(desc->offset));
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
break;
case VertexDescription::Normal:
glNormalPointer(desc->type, sizeof(T), BUFFER_OFFSET(desc->offset));
glEnableClientState(GL_NORMAL_ARRAY);
break;
case VertexDescription::Color:
glColorPointer(desc->count, desc->type, sizeof(T), BUFFER_OFFSET(desc->offset));
glEnableClientState(GL_COLOR_ARRAY);
break;
default:
break;
}
}
}
void unbind()
{
GLBUFFERS_ASSERT_OPENGL("GLVertexBuffer::unbind", glBindBuffer, return)
glBindBuffer(GL_ARRAY_BUFFER, 0);
for (VertexDescription *desc = T::description; desc->field != VertexDescription::Null; ++desc) {
switch (desc->field) {
case VertexDescription::Position:
glDisableClientState(GL_VERTEX_ARRAY);
break;
case VertexDescription::TexCoord:
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
break;
case VertexDescription::Normal:
glDisableClientState(GL_NORMAL_ARRAY);
break;
case VertexDescription::Color:
glDisableClientState(GL_COLOR_ARRAY);
break;
default:
break;
}
}
}
int length() const {return m_length;}
T *lock()
{
GLBUFFERS_ASSERT_OPENGL("GLVertexBuffer::lock", glBindBuffer && glMapBuffer, return 0)
glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
GLvoid* buffer = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
m_failed = (buffer == 0);
return reinterpret_cast<T *>(buffer);
}
void unlock()
{
GLBUFFERS_ASSERT_OPENGL("GLVertexBuffer::unlock", glBindBuffer && glUnmapBuffer, return)
glBindBuffer(GL_ARRAY_BUFFER, m_buffer);
glUnmapBuffer(GL_ARRAY_BUFFER);
}
bool failed()
{
return m_failed;
}
private:
int m_length, m_mode;
GLuint m_buffer;
bool m_failed;
};
template<class T>
class GLIndexBuffer
{
public:
GLIndexBuffer(int length, const T *data = 0, int mode = GL_STATIC_DRAW)
: m_length(0)
, m_mode(mode)
, m_buffer(0)
, m_failed(false)
{
GLBUFFERS_ASSERT_OPENGL("GLIndexBuffer::GLIndexBuffer", glGenBuffers && glBindBuffer && glBufferData, return)
glGenBuffers(1, &m_buffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (m_length = length) * sizeof(T), data, mode);
}
~GLIndexBuffer()
{
GLBUFFERS_ASSERT_OPENGL("GLIndexBuffer::~GLIndexBuffer", glDeleteBuffers, return)
glDeleteBuffers(1, &m_buffer);
}
void bind()
{
GLBUFFERS_ASSERT_OPENGL("GLIndexBuffer::bind", glBindBuffer, return)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer);
}
void unbind()
{
GLBUFFERS_ASSERT_OPENGL("GLIndexBuffer::unbind", glBindBuffer, return)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
int length() const {return m_length;}
T *lock()
{
GLBUFFERS_ASSERT_OPENGL("GLIndexBuffer::lock", glBindBuffer && glMapBuffer, return 0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer);
GLvoid* buffer = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_WRITE);
m_failed = (buffer == 0);
return reinterpret_cast<T *>(buffer);
}
void unlock()
{
GLBUFFERS_ASSERT_OPENGL("GLIndexBuffer::unlock", glBindBuffer && glUnmapBuffer, return)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_buffer);
glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
}
bool failed()
{
return m_failed;
}
private:
int m_length, m_mode;
GLuint m_buffer;
bool m_failed;
};
#endif