199 lines
5.4 KiB
C
199 lines
5.4 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#include "vertex_array.h"
|
|
|
|
#include "../glcommon/debug.h"
|
|
#include "gl33.h"
|
|
#include "index_buffer.h"
|
|
#include "vertex_buffer.h"
|
|
|
|
static GLenum va_type_to_gl_type[] = {
|
|
[VA_FLOAT] = GL_FLOAT,
|
|
[VA_BYTE] = GL_BYTE,
|
|
[VA_UBYTE] = GL_UNSIGNED_BYTE,
|
|
[VA_SHORT] = GL_SHORT,
|
|
[VA_USHORT] = GL_UNSIGNED_SHORT,
|
|
[VA_INT] = GL_INT,
|
|
[VA_UINT] = GL_UNSIGNED_INT,
|
|
};
|
|
|
|
VertexArray* gl33_vertex_array_create(void) {
|
|
auto varr = ALLOC(VertexArray);
|
|
glGenVertexArrays(1, &varr->gl_handle);
|
|
// A VAO must be bound before it's considered valid.
|
|
// https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glIsVertexArray.xhtml
|
|
// https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glIsVertexArray.xhtml
|
|
gl33_bind_vao(varr->gl_handle);
|
|
gl33_sync_vao();
|
|
assert(glIsVertexArray(varr->gl_handle));
|
|
snprintf(varr->debug_label, sizeof(varr->debug_label), "VAO #%i", varr->gl_handle);
|
|
return varr;
|
|
}
|
|
|
|
void gl33_vertex_array_destroy(VertexArray *varr) {
|
|
gl33_vertex_array_deleted(varr);
|
|
glDeleteVertexArrays(1, &varr->gl_handle);
|
|
mem_free(varr->attachments);
|
|
mem_free(varr->attribute_layout);
|
|
mem_free(varr);
|
|
}
|
|
|
|
static void gl33_vertex_array_update_layout(VertexArray *varr) {
|
|
GLuint vao_saved = gl33_vao_current();
|
|
GLuint vbo_saved = gl33_buffer_current(GL33_BUFFER_BINDING_ARRAY);
|
|
|
|
gl33_bind_vao(varr->gl_handle);
|
|
|
|
if(varr->layout_dirty_bits & VAO_INDEX_BIT) {
|
|
gl33_sync_vao();
|
|
if(varr->index_attachment) {
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, varr->index_attachment->cbuf.gl_handle);
|
|
gl33_index_buffer_on_vao_attach(varr->index_attachment, varr->gl_handle);
|
|
} else {
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
|
|
}
|
|
}
|
|
|
|
for(uint i = 0; i < varr->num_attributes; ++i) {
|
|
VertexAttribFormat *a = varr->attribute_layout + i;
|
|
assert((uint)a->spec.type < sizeof(va_type_to_gl_type)/sizeof(GLenum));
|
|
assert(a->attachment < VAO_MAX_BUFFERS);
|
|
|
|
if(!(varr->layout_dirty_bits & (1u << i))) {
|
|
continue;
|
|
}
|
|
|
|
VertexBuffer *vbuf = varr->attachments[a->attachment];
|
|
|
|
if(vbuf == NULL) {
|
|
continue;
|
|
}
|
|
|
|
gl33_sync_vao();
|
|
|
|
gl33_bind_buffer(GL33_BUFFER_BINDING_ARRAY, vbuf->cbuf.gl_handle);
|
|
gl33_sync_buffer(GL33_BUFFER_BINDING_ARRAY);
|
|
|
|
glEnableVertexAttribArray(i);
|
|
|
|
switch(a->spec.coversion) {
|
|
case VA_CONVERT_FLOAT:
|
|
case VA_CONVERT_FLOAT_NORMALIZED:
|
|
glVertexAttribPointer(
|
|
i,
|
|
a->spec.elements,
|
|
va_type_to_gl_type[a->spec.type],
|
|
a->spec.coversion == VA_CONVERT_FLOAT_NORMALIZED,
|
|
a->stride,
|
|
(void*)a->offset
|
|
);
|
|
|
|
break;
|
|
|
|
case VA_CONVERT_INT:
|
|
glVertexAttribIPointer(
|
|
i,
|
|
a->spec.elements,
|
|
va_type_to_gl_type[a->spec.type],
|
|
a->stride,
|
|
(void*)a->offset
|
|
);
|
|
|
|
break;
|
|
|
|
default: UNREACHABLE;
|
|
}
|
|
|
|
DIAGNOSTIC(push)
|
|
DIAGNOSTIC(ignored "-Wunreachable-code")
|
|
|
|
if(HAVE_GL_FUNC(glVertexAttribDivisor)) {
|
|
glVertexAttribDivisor(i, a->spec.divisor);
|
|
} else if(a->spec.divisor != 0) {
|
|
log_fatal("Renderer backend does not support instance attributes");
|
|
}
|
|
|
|
DIAGNOSTIC(pop)
|
|
}
|
|
|
|
for(uint i = varr->num_attributes; i < varr->prev_num_attributes; ++i) {
|
|
gl33_sync_vao();
|
|
glDisableVertexAttribArray(i);
|
|
}
|
|
|
|
gl33_bind_buffer(GL33_BUFFER_BINDING_ARRAY, vbo_saved);
|
|
gl33_bind_vao(vao_saved);
|
|
|
|
varr->prev_num_attributes = varr->num_attributes;
|
|
varr->layout_dirty_bits = 0;
|
|
}
|
|
|
|
void gl33_vertex_array_attach_vertex_buffer(VertexArray *varr, VertexBuffer *vbuf, uint attachment) {
|
|
assert(attachment < VAO_MAX_BUFFERS);
|
|
|
|
// TODO: more efficient way of handling this?
|
|
if(attachment >= varr->num_attachments) {
|
|
varr->attachments = mem_realloc(varr->attachments, (attachment + 1) * sizeof(VertexBuffer*));
|
|
varr->num_attachments = attachment + 1;
|
|
}
|
|
|
|
varr->attachments[attachment] = vbuf;
|
|
varr->layout_dirty_bits |= (1u << attachment);
|
|
}
|
|
|
|
void gl33_vertex_array_attach_index_buffer(VertexArray *varr, IndexBuffer *ibuf) {
|
|
varr->index_attachment = ibuf;
|
|
varr->layout_dirty_bits |= VAO_INDEX_BIT;
|
|
}
|
|
|
|
VertexBuffer* gl33_vertex_array_get_vertex_attachment(VertexArray *varr, uint attachment) {
|
|
if(varr->num_attachments <= attachment) {
|
|
return NULL;
|
|
}
|
|
|
|
return varr->attachments[attachment];
|
|
}
|
|
|
|
IndexBuffer* gl33_vertex_array_get_index_attachment(VertexArray *varr) {
|
|
return varr->index_attachment;
|
|
}
|
|
|
|
void gl33_vertex_array_layout(VertexArray *varr, uint nattribs, VertexAttribFormat attribs[nattribs]) {
|
|
if(varr->num_attributes != nattribs) {
|
|
varr->attribute_layout = mem_realloc(varr->attribute_layout, sizeof(VertexAttribFormat) * nattribs);
|
|
varr->num_attributes = nattribs;
|
|
}
|
|
|
|
memcpy(varr->attribute_layout, attribs, sizeof(VertexAttribFormat) * nattribs);
|
|
varr->layout_dirty_bits |= (1u << nattribs) - 1;
|
|
}
|
|
|
|
const char* gl33_vertex_array_get_debug_label(VertexArray *varr) {
|
|
return varr->debug_label;
|
|
}
|
|
|
|
void gl33_vertex_array_set_debug_label(VertexArray *varr, const char *label) {
|
|
glcommon_set_debug_label(varr->debug_label, "VAO", GL_VERTEX_ARRAY, varr->gl_handle, label);
|
|
}
|
|
|
|
void gl33_vertex_array_flush_buffers(VertexArray *varr) {
|
|
if(varr->layout_dirty_bits) {
|
|
gl33_vertex_array_update_layout(varr);
|
|
}
|
|
|
|
for(uint i = 0; i < varr->num_attachments; ++i) {
|
|
if(varr->attachments[i] != NULL) {
|
|
gl33_vertex_buffer_flush(varr->attachments[i]);
|
|
}
|
|
}
|
|
|
|
if(varr->index_attachment != NULL) {
|
|
gl33_index_buffer_flush(varr->index_attachment);
|
|
}
|
|
}
|