#include "GeometryGL.h" #include "DeviceGL.h" #include "HeadersGL.h" namespace RBX { namespace Graphics { static const GLenum gBufferUsageGL[GeometryBuffer::Usage_Count] = { GL_STATIC_DRAW, GL_DYNAMIC_DRAW }; static const GLenum gBufferLockGL[GeometryBuffer::Lock_Count] = { GL_MAP_WRITE_BIT, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT }; struct VertexFormatGL { int size; GLenum type; bool normalized; }; static const VertexFormatGL gVertexFormatGL[VertexLayout::Format_Count] = { { 1, GL_FLOAT, false }, { 2, GL_FLOAT, false }, { 3, GL_FLOAT, false }, { 4, GL_FLOAT, false }, { 2, GL_SHORT, false }, { 4, GL_SHORT, false }, { 4, GL_UNSIGNED_BYTE, false }, { 4, GL_UNSIGNED_BYTE, true }, }; static const GLenum gGeometryPrimitiveGL[Geometry::Primitive_Count] = { GL_TRIANGLES, GL_LINES, GL_POINTS, GL_TRIANGLE_STRIP }; static unsigned int getVertexAttributeGL(VertexLayout::Semantic semantic, unsigned int index) { switch (semantic) { case VertexLayout::Semantic_Position: RBXASSERT(index == 0); return 0; case VertexLayout::Semantic_Normal: RBXASSERT(index == 0); return 1; case VertexLayout::Semantic_Color: RBXASSERT(index < 2); return 2 + index; case VertexLayout::Semantic_Texture: RBXASSERT(index < 8); return 4 + index; default: RBXASSERT(false); return -1; } } const char* VertexLayoutGL::getShaderAttributeName(unsigned int id) { static const char* table[] = { "vertex", "normal", "colour", "secondary_colour", "uv0", "uv1", "uv2", "uv3", "uv4", "uv5", "uv6", "uv7", }; return (id < ARRAYSIZE(table)) ? table[id] : NULL; } VertexLayoutGL::VertexLayoutGL(Device* device, const std::vector& elements) : VertexLayout(device, elements) { } VertexLayoutGL::~VertexLayoutGL() { } template GeometryBufferGL::GeometryBufferGL(Device* device, size_t elementSize, size_t elementCount, GeometryBuffer::Usage usage, unsigned int target) : Base(device, elementSize, elementCount, usage) , target(target) , id(0) , locked(0) { } template void GeometryBufferGL::create() { glGenBuffers(1, &id); RBXASSERT(id); glBindBuffer(target, id); glBufferData(target, this->elementSize * this->elementCount, NULL, gBufferUsageGL[this->usage]); } template GeometryBufferGL::~GeometryBufferGL() { RBXASSERT(!locked); glDeleteBuffers(1, &id); } template void* GeometryBufferGL::lock(GeometryBuffer::LockMode mode) { RBXASSERT(!locked); unsigned int size = this->elementSize * this->elementCount; const DeviceCapsGL& caps = static_cast(this->device)->getCapsGL(); if (caps.extMapBuffer) { glBindBuffer(target, id); if (caps.extMapBufferRange) { locked = glMapBufferRange(target, 0, size, gBufferLockGL[mode]); } else { if (mode == GeometryBuffer::Lock_Discard) glBufferData(target, size, NULL, gBufferUsageGL[this->usage]); locked = glMapBuffer(target, GL_WRITE_ONLY); } } else { locked = new char[size]; } RBXASSERT(locked); return locked; } template void GeometryBufferGL::unlock() { RBXASSERT(locked); const DeviceCapsGL& caps = static_cast(this->device)->getCapsGL(); if (caps.extMapBuffer) { glBindBuffer(target, id); glUnmapBuffer(target); } else { glBindBuffer(target, id); glBufferSubData(target, 0, this->elementSize * this->elementCount, locked); delete[] static_cast(locked); } locked = NULL; } template void GeometryBufferGL::upload(unsigned int offset, const void* data, unsigned int size) { RBXASSERT(!locked); RBXASSERT(offset + size <= this->elementSize * this->elementCount); glBindBuffer(target, id); glBufferSubData(target, offset, size, data); } VertexBufferGL::VertexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage) : GeometryBufferGL(device, elementSize, elementCount, usage, GL_ARRAY_BUFFER) { create(); } VertexBufferGL::~VertexBufferGL() { } IndexBufferGL::IndexBufferGL(Device* device, size_t elementSize, size_t elementCount, Usage usage) : GeometryBufferGL(device, elementSize, elementCount, usage, GL_ELEMENT_ARRAY_BUFFER) { const DeviceCapsGL& caps = static_cast(device)->getCapsGL(); if (elementSize != 2 && elementSize != 4) throw RBX::runtime_error("Invalid element size: %d", (int)elementSize); if (elementSize == 4 && !caps.supportsIndex32) throw RBX::runtime_error("Unsupported element size: %d", (int)elementSize); create(); } IndexBufferGL::~IndexBufferGL() { } GeometryGL::GeometryGL(Device* device, const shared_ptr& layout, const std::vector >& vertexBuffers, const shared_ptr& indexBuffer, unsigned int baseVertexIndex) : Geometry(device, layout, vertexBuffers, indexBuffer, baseVertexIndex) , id(0) , indexElementSize(0) { const DeviceCapsGL& caps = static_cast(device)->getCapsGL(); if (caps.extVertexArrayObject) { glGenVertexArrays(1, &id); RBXASSERT(id); glBindVertexArray(id); bindArrays(); glBindVertexArray(0); } if (indexBuffer) { // Cache indexElementSize to reduce cache misses in case we're using VAO indexElementSize = indexBuffer->getElementSize(); } } GeometryGL::~GeometryGL() { if (id) { glDeleteVertexArrays(1, &id); } } void GeometryGL::draw(Primitive primitive, unsigned int offset, unsigned int count) { unsigned int mask = 0; // Setup vertex/index buffers if (id) glBindVertexArray(id); else mask = bindArrays(); // Perform draw call if (indexBuffer) { GLenum indexElementType = indexElementSize == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; glDrawElements(gGeometryPrimitiveGL[primitive], count, indexElementType, reinterpret_cast(offset * indexElementSize)); } else glDrawArrays(gGeometryPrimitiveGL[primitive], offset, count); // Unbind buffers to prevent draw call interference if (id) glBindVertexArray(0); else unbindArrays(mask); } unsigned int GeometryGL::bindArrays() { unsigned int mask = 0; unsigned int vbId = 0; const std::vector& elements = layout->getElements(); for (size_t i = 0; i < elements.size(); ++i) { const VertexLayout::Element& e = elements[i]; int index = getVertexAttributeGL(e.semantic, e.semanticIndex); RBXASSERT(index >= 0); RBXASSERT(e.stream < vertexBuffers.size()); VertexBufferGL* vb = static_cast(vertexBuffers[e.stream].get()); RBXASSERT(e.offset < vb->getElementSize()); size_t stride = vb->getElementSize(); size_t offset = e.offset + stride * baseVertexIndex; const VertexFormatGL& formatGl = gVertexFormatGL[e.format]; if (vbId != vb->getId()) { vbId = vb->getId(); glBindBuffer(GL_ARRAY_BUFFER, vbId); } glEnableVertexAttribArray(index); glVertexAttribPointer(index, formatGl.size, formatGl.type, formatGl.normalized, stride, reinterpret_cast(offset)); mask |= 1 << index; } if (indexBuffer) { IndexBufferGL* ib = static_cast(indexBuffer.get()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->getId()); } return mask; } void GeometryGL::unbindArrays(unsigned int mask) { for (int i = 0; i < 16; ++i) if (mask & (1 << i)) glDisableVertexAttribArray(i); } // Instantiate GeometryBufferGL template template class GeometryBufferGL; template class GeometryBufferGL; } }