#include "Draw.h" namespace NB{ DrawError::DrawError( const std::string& msg, const std::string& file, int line) : std::runtime_error(NB::formatDebugString(msg, file, line)) {} uint8_t VerticesFromPrimitives(GLenum type, unsigned int num_prims) { switch(type) { case GL_POINTS: return num_prims; case GL_LINES: return num_prims*2; break; case GL_LINE_STRIP: return num_prims+1; break; case GL_LINE_LOOP: return num_prims; break; case GL_TRIANGLES: return num_prims*3; break; case GL_TRIANGLE_STRIP: case GL_TRIANGLE_FAN: return num_prims+2; break; case GL_QUADS: return 4*num_prims; break; case GL_QUAD_STRIP: return 2*num_prims+2; break; default: return num_prims; break; } } uint8_t PrimitivesFromVertices(GLenum type, unsigned int num_verts) { switch(type) { case GL_POINTS: return num_verts; case GL_LINES: return num_verts/2; break; case GL_LINE_STRIP: return num_verts-1; break; case GL_LINE_LOOP: return num_verts; break; case GL_TRIANGLES: return num_verts/3; break; case GL_TRIANGLE_STRIP: case GL_TRIANGLE_FAN: return num_verts-2; break; case GL_QUADS: return num_verts/4; break; case GL_QUAD_STRIP: return num_verts/2 - 1; break; default: return num_verts; break; } } //DrawBuffer class DrawBuffer::DrawBuffer(const Shader& shader, VertexAttributeList vas, GLenum prim_type) : _shader(shader), _vert_data(), _elmt_data(), _gl_primitive_type(prim_type), _VAO() { _VAO.addVBO(&_vert_data); _VAO.changeEBO(&_elmt_data); try { throw _VAO.checkValid(vas); } catch (VAOError vaoe) { if (vaoe.error) { _VAO.addVertexAttributes(DrawBuffer::defaultVAO(vas)); } else { _VAO.addVertexAttributes(vas); } } } VertexAttributeList DrawBuffer::defaultVAO(VertexAttributeList vert_attrs) { GLuint vert_id = _vert_data.id(); unsigned int vert_size = 0; for (const VertexAttribute& va : vert_attrs) { vert_size += va.GLSLSize * GLSLTypeSize(va.GLSLType); } unsigned int curr_byte = 0; for (VertexAttribute& va : vert_attrs) { va.ptr.buffer = vert_id; va.ptr.stride = vert_size; va.ptr.offset = curr_byte; va.ptr.divisor = 0; curr_byte += va.GLSLSize * GLSLTypeSize(va.GLSLType); } return vert_attrs; } VertexAttributeList DrawBuffer::changeVertLayout(unsigned int i, VertexAttributePointer vap) { GLuint vert_id = _vert_data.id(); vap.buffer = (vap.buffer) ? vap.buffer : vert_id; VertexAttributeList vas = _VAO.getLayout(); unsigned int idx = 0; for (const VertexAttribute& va : vas) { if (va.ptr.buffer == vert_id) { if (idx == i) { return _VAO.changeLayout(i, vap); } idx++; } } THROW_DRAW_ERROR("Could not find vertex attribute with index " + std::to_string(i)); } unsigned int DrawBuffer::vertSize() const { return _VAO.vertSize(_vert_data.id()); } void DrawBuffer::draw() const { _shader.use(); _VAO.bind(); glDrawElements(_gl_primitive_type, _elmt_data.capacity(), GL_UNSIGNED_INT, 0); _VAO.unbind(); } const Shader DrawBuffer::shader() const { return _shader; } bool DrawBuffer::keepLocalElement() const { return _elmt_data.keepLocal(); } bool DrawBuffer::keepLocalVertex() const { return _vert_data.keepLocal(); } unsigned int DrawBuffer::elementBufferID() const { return _elmt_data.id(); } unsigned int DrawBuffer::vertexBufferID() const { return _vert_data.id(); } bool DrawBuffer::keepLocalElement(bool keep) { return _elmt_data.keepLocal(keep); } bool DrawBuffer::keepLocalVertex(bool keep) { return _vert_data.keepLocal(keep); } const Shader DrawBuffer::shader(const Shader& new_shader) { _shader = new_shader; return _shader; } void DrawBuffer::generateEBO(unsigned int num_prims) { generateEBO(std::vector(VerticesFromPrimitives(_gl_primitive_type, num_prims))); } void DrawBuffer::generateEBO(const std::vector& elements) { _num_prims = PrimitivesFromVertices(_gl_primitive_type, elements.size()); _elmt_data.keepLocal(false); _elmt_data.setData(elements); } void DrawBuffer::generateVBO(unsigned int num_verts) { generateVBO(RawVec(num_verts * vertSize())); } void DrawBuffer::generateVBO(const RawVec& vert_data) { // Check for uninitialized Vertex Pointers. If so, will fallback to default VAO setup. // Otherwise, set VAO to provided Vertex Atributes. int vert_size = vertSize(); if (vert_data.size() % vert_size != 0) { THROW_DRAW_ERROR("Initializing data does not match vertex size of " + std::to_string(vert_size) + "."); } _num_verts = vert_data.size() / vert_size; // TODO: Make it not static draw all the time _VAO.bind(); _vert_data.keepLocal(false); _vert_data.setData(vert_data); _VAO.unbind(); } void DrawBuffer::loadEBO(const std::vector& elements, unsigned int offset) { _VAO.bind(); if (!_elmt_data.isInitialized()) { std::vector init_elmts(elements); init_elmts.insert(init_elmts.cbegin(), offset, 0); generateEBO(init_elmts); } else { _elmt_data.setSubData(elements, offset); } _VAO.unbind(); } void DrawBuffer::loadVBO(const RawVec& data, unsigned int offset) { _VAO.bind(); if (!_vert_data.isInitialized()) { RawVec load_data(offset); load_data.insert(load_data.end(), data.begin(), data.end()); generateVBO(load_data); } else { try{ _vert_data.setSubData(data, offset); } catch(BufferError& e) { THROW_DRAW_ERROR("NB::BufferError in loadVBO:\n\t" + std::string(e.what())); } } _VAO.unbind(); } //DrawInstanceBufferClass DrawInstanceBuffer::DrawInstanceBuffer( const Shader& shad, VertexAttributeList vert_vas, VertexAttributeList inst_vas, GLenum prim_type ): DrawBuffer(shad, vert_vas, prim_type), _inst_data() { _VAO.addVBO(&_inst_data); try { throw _VAO.checkValid(inst_vas); } catch (VAOError vaoe) { if (vaoe.error) { _VAO.addVertexAttributes(defaultVAO(inst_vas)); } else { _VAO.addVertexAttributes(inst_vas); } } } unsigned int DrawInstanceBuffer::instSize() const { return _VAO.vertSize(_inst_data.id()); } void DrawInstanceBuffer::generateIBO(unsigned int num_inst) { generateIBO(RawVec(instSize() * num_inst)); } void DrawInstanceBuffer::generateIBO(const RawVec& inst_data) { int inst_size = instSize(); if (inst_data.size() % inst_size != 0) { THROW_DRAW_ERROR("Initializing data does not match instance size of " + std::to_string(inst_size) + "."); } _num_inst = inst_data.size() / inst_size; _VAO.bind(); _inst_data.keepLocal(false); _inst_data.setData(inst_data); _VAO.unbind(); } void DrawInstanceBuffer::loadIBO(const RawVec& data, unsigned int offset) { _VAO.bind(); if (!_inst_data.isInitialized()) { RawVec load_data(offset); load_data.insert(load_data.end(), data.begin(), data.end()); generateIBO(load_data); } else { try{ _inst_data.setSubData(data, offset); } catch(BufferError& e) { THROW_DRAW_ERROR("NB::BufferError in loadIBO:\n\t" + std::string(e.what())); } } _VAO.unbind(); } VertexAttributeList DrawInstanceBuffer::defaultVAO(VertexAttributeList inst_attrs) { GLuint inst_id = _inst_data.id(); unsigned int vert_size = 0; for (const VertexAttribute& va : inst_attrs) { vert_size += va.GLSLSize * GLSLTypeSize(va.GLSLType); } unsigned int curr_byte = 0; for (VertexAttribute& va : inst_attrs) { va.ptr.buffer = inst_id; va.ptr.stride = vert_size; va.ptr.offset = curr_byte; va.ptr.divisor = 0; curr_byte += va.GLSLSize * GLSLTypeSize(va.GLSLType); } return inst_attrs; } VertexAttributeList DrawInstanceBuffer::changeInstanceLayout(unsigned int i, VertexAttributePointer vap) { GLuint inst_id = _inst_data.id(); vap.buffer = (vap.buffer) ? vap.buffer : inst_id; VertexAttributeList vas = _VAO.getLayout(); unsigned int vas_size = vas.size(); unsigned int inst_idx = 0; unsigned int idx = 0; for (const VertexAttribute& va : vas) { if (va.ptr.buffer == inst_id) { if (inst_idx == i) { return _VAO.changeLayout(idx, vap); } inst_idx++; } idx++; } THROW_DRAW_ERROR("Could not find instance attribute with index " + std::to_string(i)); } void DrawInstanceBuffer::draw() const { _shader.use(); _VAO.bind(); glDrawElementsInstanced(_gl_primitive_type, _elmt_data.capacity(), GL_UNSIGNED_INT, 0, _num_inst); _VAO.unbind(); } }