diff --git a/BBGE/CMakeLists.txt b/BBGE/CMakeLists.txt index 8eaa5af..ac7acb3 100644 --- a/BBGE/CMakeLists.txt +++ b/BBGE/CMakeLists.txt @@ -70,8 +70,12 @@ set(BBGE_SRCS ReadXML.h Rect.h Refcounted.h + RenderAPI.cpp + RenderAPI.h RenderBase.cpp RenderBase.h + RenderHigh.cpp + RenderHigh.h RenderObject.cpp RenderObject.h RenderObject_inline.h diff --git a/BBGE/RenderAPI.cpp b/BBGE/RenderAPI.cpp new file mode 100644 index 0000000..18ae6a4 --- /dev/null +++ b/BBGE/RenderAPI.cpp @@ -0,0 +1,183 @@ +#include "RenderAPI.h" +#include "Base.h" + +#include "RenderBase.h" + +namespace RenderAPI { + +void BufferBase::destroy() +{ + deleteBuffer(&_bufid); +} + +void BufferBase::upload(Hint usage) +{ + updateBuffer(&_bufid, _data, _bytes, usage); +} + + +// ----- GL backend starts here ----- +// TODO: move backend to separate file once finalized + +struct BlendParams +{ + GLenum src, dst; +}; +static const BlendParams s_blendParams[] = +{ + { GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { GL_SRC_ALPHA, GL_ONE }, + { GL_ZERO, GL_SRC_ALPHA }, + { GL_ZERO, GL_SRC_COLOR }, +}; + +static const GLenum s_primTypes[] = +{ + GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_QUADS, + GL_QUAD_STRIP +}; + + +static void renderNonIndexed(const ObjectData& d) +{ + switch(d.layout) + { + case RAPI_LAYOUT_2D: + { + ::glColor4fv(&d.color.r); + const size_t n = d.verts->bytes() / sizeof(float); + const float *p = (const float*)d.verts->data(); + const float * const end = p + n; + for( ; p < end; p += 4) // Vertex2D + { + ::glTexCoord2f(p[0], p[1]); + ::glVertex2f(p[2], p[3]); + } + } + break; + + case RAPI_LAYOUT_2D_COLOR: + { + const size_t n = d.verts->bytes() / sizeof(float); + const float *p = (const float*)d.verts->data(); + const float * const end = p + n; + for( ; p < end; p += 8) // Vertex2DColor + { + ::glColor4fv(&p[4]); + ::glTexCoord2f(p[0], p[1]); + ::glVertex2f(p[2], p[3]); + } + } + break; + } +} + +static void renderIndexed(const ObjectData& d) +{ + switch(d.layout) + { + case RAPI_LAYOUT_2D: + { + ::glColor4fv(&d.color.r); + const size_t n = d.indices->bytes() / sizeof(unsigned short); + const unsigned short *idx = (const unsigned short*)d.indices->data(); + const float * const pbase = (const float*)d.verts->data(); + const unsigned short * const end = idx + n; + for( ; idx < end; ++idx) + { + const float *p = pbase + *idx; + ::glTexCoord2f(p[0], p[1]); + ::glVertex2f(p[2], p[3]); + } + } + break; + + case RAPI_LAYOUT_2D_COLOR: + { + const size_t n = d.indices->bytes() / sizeof(unsigned short); + const unsigned short *idx = (const unsigned short*)d.indices->data(); + const float * const pbase = (const float*)d.verts->data(); + const unsigned short * const end = idx + n; + for( ; idx < end; ++idx) + { + const float *p = pbase + *idx; + ::glColor4fv(&p[4]); + ::glTexCoord2f(p[0], p[1]); + ::glVertex2f(p[2], p[3]); + } + } + break; + } +} + +void render(const ObjectData* objs, size_t n) +{ + ::glPushMatrix(); + char lastblend = BLEND_DISABLED - 1; // always fail the first check + unsigned lasttex = unsigned(-1); + const ObjectData* const end = objs + n; + for( ; objs < end; ++objs) + { + const ObjectData& d = *objs; + if(lastblend != d.blend) + { + lastblend = d.blend; + unsigned ublend = unsigned(int(d.blend)); // this underflows if BLEND_DISABLED + if (ublend < Countof(s_blendParams)) + { + ::glEnable(GL_BLEND); + const BlendParams& bp = s_blendParams[ublend]; + ::glBlendFunc(bp.src, bp.dst); + } + else + { + ::glDisable(GL_BLEND); + ::glDisable(GL_ALPHA_TEST); + } + } + + + ::glLoadMatrixf(d.pmat); + + + const unsigned prim = d.prim; + switch(prim) + { + case RAPI_PRIM_POINTS: + ::glPointSize(d.u.linewidth); + break; + case RAPI_PRIM_LINES: + case RAPI_PRIM_LINE_STRIP: + ::glLineWidth(d.u.linewidth); + break; + + default: + if(d.u.texid != lasttex) + { + lasttex = d.u.texid; + ::glBindTexture(GL_TEXTURE_2D, d.u.texid); + } + } + + ::glBegin(s_primTypes[prim]); + if(!d.indices) + renderNonIndexed(d); + else + renderIndexed(d); + ::glEnd(); + } + ::glPopMatrix(); +} + +void updateBuffer(unsigned* pbufid, const void* data, size_t bytes, RenderAPI::BufferBase::Hint usage) +{ +} + +void deleteBuffer(unsigned* pbufid) +{ +} + +} diff --git a/BBGE/RenderAPI.h b/BBGE/RenderAPI.h new file mode 100644 index 0000000..1912f71 --- /dev/null +++ b/BBGE/RenderAPI.h @@ -0,0 +1,81 @@ +#ifndef BBGE_RENDER_API_H +#define BBGE_RENDER_API_H + +#include "EngineEnums.h" + +enum RenderAPIConstants +{ + //-- primitives to draw + RAPI_PRIM_POINTS = 0, + RAPI_PRIM_LINES = 1, + RAPI_PRIM_LINE_STRIP = 2, + RAPI_PRIM_QUADS = 3, + RAPI_PRIM_QUAD_STRIP = 4, + + //-- per-vertex layout + RAPI_LAYOUT_2D = 0, // (u,v,x,y) aka Vertex2D + RAPI_LAYOUT_2D_COLOR = 1 // (u,v,x,y,r,g,b,a) aka Vertex2DColor +}; + +namespace RenderAPI { + +struct Vertex2D +{ + float u, v, x, y; +}; +struct Vertex2DColor : public Vertex2D +{ + float r, g, b, a; +}; + + +class BufferBase +{ +public: + enum Hint + { + BUFFER_STATIC, + BUFFER_DYNAMIC + }; + + unsigned bufid() const { return _bufid; } + const void *data() const { return _data; } + size_t bytes() const { return _bytes; } + void destroy(); + void upload(Hint usage); + +protected: + + unsigned _bufid; + void *_data; // pointer to data + size_t _bytes; +}; + +// real POD struct +struct ObjectData +{ + const float *pmat; // pointer to float[16] + struct + { + float r, g, b, a; // used only when no per-vertex colors are used + } color; + const BufferBase *verts; + const BufferBase *indices; // always uint16 if present + union + { + unsigned texid; // if tris/quads + float linewidth; // if lines/points: point size or line width + } u; + char blend; + unsigned char prim; + unsigned char layout; +}; + + +void render(const ObjectData *objs, size_t n); +void updateBuffer(unsigned *pbufid, const void *data, size_t bytes, BufferBase::Hint usage); +void deleteBuffer(unsigned *pbufid); + +} + +#endif // BBGE_RENDER_API_H diff --git a/BBGE/RenderHigh.cpp b/BBGE/RenderHigh.cpp new file mode 100644 index 0000000..a118960 --- /dev/null +++ b/BBGE/RenderHigh.cpp @@ -0,0 +1,97 @@ +#include "RenderHigh.h" + +#include +#include + +ObjectList::ObjectList(size_t minsize) + : _ptr(NULL), _size(0), _cap(0), _minsize(minsize) +{ +} + +ObjectList::~ObjectList() +{ + this->clear(); +} + +RenderAPI::ObjectData* ObjectList::reserve(size_t n) +{ + return _ensure(n) + _size; +} + +void ObjectList::commit(size_t n) +{ + _size += n; + assert(_size <= _cap); +} + +RenderAPI::ObjectData& ObjectList::push(const RenderAPI::ObjectData& a) +{ + size_t cursize = _size; + size_t newsize = _size + 1; + _size = newsize; + return (_ensure(newsize)[cursize] = a); +} + +void ObjectList::reset() +{ + _size = 0; +} + +void ObjectList::clear() +{ + free(_ptr); + _ptr = NULL; + _size = 0; + _cap = 0; +} + +RenderAPI::ObjectData* ObjectList::_ensure(size_t n) +{ + if(n < _cap) + return _ptr; + + size_t newsize = 2 * _cap; + if(newsize < n) + newsize += n; + if(newsize < _minsize) + newsize = _minsize; + + RenderAPI::ObjectData *p = (RenderAPI::ObjectData*)realloc(_ptr, sizeof(RenderAPI::ObjectData) * newsize); + if(!p) + return NULL; + + _ptr = p; + _cap = newsize; + return p; +} + +DynamicBuffer::DynamicBuffer() + : usage(BUFFER_DYNAMIC) +{ + _bufid = 0; + _data = NULL; + _bytes = 0; +} + +DynamicBuffer::~DynamicBuffer() +{ + free(_data); +} + +void* DynamicBuffer::resizeBytes(size_t bytes) +{ + _bytes = bytes; + return realloc(_data, bytes); +} + +void* DynamicBuffer::_ensureBytes(size_t bytes) +{ + if(bytes < _bytes) + return _data; + + size_t newsize = 2 * _bytes; + if(newsize < bytes) + newsize += bytes; + + return resizeBytes(newsize); +} diff --git a/BBGE/RenderHigh.h b/BBGE/RenderHigh.h new file mode 100644 index 0000000..ce424e9 --- /dev/null +++ b/BBGE/RenderHigh.h @@ -0,0 +1,115 @@ +#ifndef BBGE_RENDER_HIGH_H +#define BBGE_RENDER_HIGH_H + +// High-level API for the renderer + +#include "RenderAPI.h" +#include "Base.h" +#include + +// Stores data for later submission to the renderer. +// Workflow: +// Either: reserve() some, then commit() +// Or: add() some +// Finally: pass (data(), size()) to the renderer +class ObjectList +{ +public: + ObjectList(size_t minsize); + ~ObjectList(); + + RenderAPI::ObjectData *reserve(size_t n); // reserve at least n elements and return pointer to writable area + void commit(size_t n); // acknowledge writing n elements to previously reserve()d + + RenderAPI::ObjectData& push(const RenderAPI::ObjectData& a); // add one element + void reset(); + void clear(); + + const RenderAPI::ObjectData *data() const { return _ptr; } + size_t size() const { return _size; } + +private: + RenderAPI::ObjectData *_ptr; + size_t _size, _cap; + size_t _minsize; + + RenderAPI::ObjectData *_ensure(size_t n); // ensures that there's space for at least n entries in total +}; + +template +class StaticMesh : public RenderAPI::BufferBase +{ +public: + StaticMesh() + { + this->_bufid = 0; + this->_data = &data[0]; + this->_size = sizeof(data); + } + T data[N]; + + inline T& operator[](size_t i) { assert(i < Countof(data)); return data[i]; } + inline const T& operator[](size_t i) const { assert(i < Countof(data)); return data[i]; } + + inline void upload() { this->upload(BUFFER_STATIC); } +}; + +class DynamicBuffer : public RenderAPI::BufferBase +{ +public: + typedef RenderAPI::BufferBase Base; + DynamicBuffer(); + ~DynamicBuffer(); + Hint usage; + inline void upload() { Base::upload(usage); } + void *resizeBytes(size_t bytes); // resize to exactly this size +protected: + void *_ensureBytes(size_t bytes); // ensures that there's space for at least that many bytes +}; + +template +class DynamicMesh : public DynamicBuffer +{ +public: + T& push(const T& val) + { + size_t sz = _size; + T *p = _ensure(sz + 1); + _size = sz + 1 + return (p[sz] = val); + } + + inline T *resize(size_t n) // ptr to start + { + return (T*)this->resizeBytes(n); + } + + inline T *reserve(size_t n) // ptr to writable region of n elems + { + return _ensure(n) + _size; + } + + inline void commit(size_t n) // commit prev. reserve()d + { + _size += n; + } + + inline void reset() + { + _size = 0; + } + + inline T& operator[](size_t i) { assert(i < _size); return (( T*)_data)[i]; } + inline const T& operator[](size_t i) const { assert(i < _size); return ((const T*)_data)[i]; } + +private: + T *_ensure(size_t n) + { + return (T*)this->_ensureBytes(n * sizeof(T)); + } + + size_t _size; // in elements +}; + + +#endif // BBGE_RENDER_HIGH_H