2026-03-17 01:47:05 -05:00

307 lines
9.2 KiB
C++

#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(VertexAttributeList vas, GLenum prim_type)
: _gl_primitive_type(prim_type), _VAO() {
_vert_data = std::make_shared<Buffer>(GL_ARRAY_BUFFER);
_elmt_data = std::make_shared<Buffer>(GL_ELEMENT_ARRAY_BUFFER);
_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 {
_VAO.bind();
glDrawElements(_gl_primitive_type, _elmt_data->size() / GLSLTypeSize(GL_INT), GL_UNSIGNED_INT, 0);
_VAO.unbind();
}
unsigned int DrawBuffer::elementBufferID() const { return _elmt_data->id(); }
unsigned int DrawBuffer::vertexBufferID() const { return _vert_data->id(); }
RawVec DrawBuffer::vertAttrData(unsigned int i) {
if (!_vert_data->isInitialized()) {
throw DrawError("Vertex buffer is not initialized.");
}
return _VAO.attributeData(i);
}
RawVec DrawBuffer::vertAttrData(unsigned int i, const RawVec& newdata) {
if (!(_vert_data->isInitialized())) {
VertexAttribute va = _VAO.getLayout()[i];
vertexData(newdata.size() / (va.GLSLSize*GLSLTypeSize(va.GLSLType)));
std::cout << "From DrawBuffer::vertAttrData(int, RawVec) - vert buf size: " << _vert_data->size() << "\n";
}
return _VAO.attributeData(i, newdata);
}
void DrawBuffer::elementData(unsigned int num_prims) {
elementData(std::vector<unsigned int>(VerticesFromPrimitives(_gl_primitive_type, num_prims)));
}
void DrawBuffer::elementData(const std::vector<unsigned int>& elements) {
_num_prims = PrimitivesFromVertices(_gl_primitive_type, elements.size());
_elmt_data->data(vectorToRaw(elements));
}
void DrawBuffer::elementData(const std::vector<unsigned int>& elements, unsigned int offset) {
_VAO.bind();
if (!_elmt_data->isInitialized()) {
std::vector<unsigned int> init_elmts(elements);
init_elmts.insert(init_elmts.cbegin(), offset, 0);
elementData(init_elmts, 0);
} else {
_elmt_data->data(vectorToRaw(elements), offset);
}
_VAO.unbind();
}
void DrawBuffer::vertexData(unsigned int num_verts) {
vertexData(RawVec(num_verts * vertSize()));
}
void DrawBuffer::vertexData(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->data(vert_data);
_VAO.unbind();
}
void DrawBuffer::vertexData(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());
vertexData(load_data, 0);
} else {
try{
_vert_data->data(data, offset);
} catch(BufferError& e) {
THROW_DRAW_ERROR("NB::BufferError in loadVBO:\n\t" + std::string(e.what()));
}
}
_VAO.unbind();
}
// DrawInstanceBufferClass
DrawInstanceBuffer::DrawInstanceBuffer(
VertexAttributeList vert_vas,
VertexAttributeList inst_vas,
GLenum prim_type
): DrawBuffer(vert_vas, prim_type) {
_inst_data = std::make_shared<Buffer>(GL_ARRAY_BUFFER);
_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()); }
unsigned int DrawInstanceBuffer::instanceBufferID() const { return _inst_data->id(); }
void DrawInstanceBuffer::instanceData(unsigned int num_inst) {
instanceData(RawVec(instSize() * num_inst));
}
void DrawInstanceBuffer::instanceData(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->data(inst_data);
_VAO.unbind();
}
void DrawInstanceBuffer::instanceData(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());
instanceData(load_data, 0);
} else {
try{
_inst_data->data(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 = 1;
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 {
_VAO.bind();
glDrawElementsInstanced(_gl_primitive_type, _elmt_data->size() / GLSLTypeSize(GL_UNSIGNED_INT), GL_UNSIGNED_INT, (void*)_elmt_data->id(), _num_inst);
_VAO.unbind();
}
}