diff options
Diffstat (limited to 'intern/gawain/src/gwn_element.c')
-rw-r--r-- | intern/gawain/src/gwn_element.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/intern/gawain/src/gwn_element.c b/intern/gawain/src/gwn_element.c new file mode 100644 index 00000000000..e2fbb657c78 --- /dev/null +++ b/intern/gawain/src/gwn_element.c @@ -0,0 +1,304 @@ + +// Gawain element list (AKA index buffer) +// +// This code is part of the Gawain library, with modifications +// specific to integration with Blender. +// +// Copyright 2016 Mike Erwin +// +// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of +// the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. + +#include "gwn_element.h" +#include "gwn_buffer_id.h" +#include <stdlib.h> + +#define KEEP_SINGLE_COPY 1 + +static GLenum convert_index_type_to_gl(Gwn_IndexBufType type) + { + static const GLenum table[] = { + [GWN_INDEX_U8] = GL_UNSIGNED_BYTE, // GL has this, Vulkan does not + [GWN_INDEX_U16] = GL_UNSIGNED_SHORT, + [GWN_INDEX_U32] = GL_UNSIGNED_INT + }; + return table[type]; + } + +unsigned GWN_indexbuf_size_get(const Gwn_IndexBuf* elem) + { +#if GWN_TRACK_INDEX_RANGE + static const unsigned table[] = { + [GWN_INDEX_U8] = sizeof(GLubyte), // GL has this, Vulkan does not + [GWN_INDEX_U16] = sizeof(GLushort), + [GWN_INDEX_U32] = sizeof(GLuint) + }; + return elem->index_ct * table[elem->index_type]; +#else + return elem->index_ct * sizeof(GLuint); +#endif + } + +void GWN_indexbuf_init_ex(Gwn_IndexBufBuilder* builder, Gwn_PrimType prim_type, unsigned index_ct, unsigned vertex_ct, bool use_prim_restart) + { + builder->use_prim_restart = use_prim_restart; + builder->max_allowed_index = vertex_ct - 1; + builder->max_index_ct = index_ct; + builder->index_ct = 0; // start empty + builder->prim_type = prim_type; + builder->data = calloc(builder->max_index_ct, sizeof(unsigned)); + } + +void GWN_indexbuf_init(Gwn_IndexBufBuilder* builder, Gwn_PrimType prim_type, unsigned prim_ct, unsigned vertex_ct) + { + unsigned verts_per_prim = 0; + switch (prim_type) + { + case GWN_PRIM_POINTS: + verts_per_prim = 1; + break; + case GWN_PRIM_LINES: + verts_per_prim = 2; + break; + case GWN_PRIM_TRIS: + verts_per_prim = 3; + break; + case GWN_PRIM_LINES_ADJ: + verts_per_prim = 4; + break; + default: +#if TRUST_NO_ONE + assert(false); +#endif + return; + } + + GWN_indexbuf_init_ex(builder, prim_type, prim_ct * verts_per_prim, vertex_ct, false); + } + +void GWN_indexbuf_add_generic_vert(Gwn_IndexBufBuilder* builder, unsigned v) + { +#if TRUST_NO_ONE + assert(builder->data != NULL); + assert(builder->index_ct < builder->max_index_ct); + assert(v <= builder->max_allowed_index); +#endif + + builder->data[builder->index_ct++] = v; + } + +void GWN_indexbuf_add_primitive_restart(Gwn_IndexBufBuilder* builder) + { +#if TRUST_NO_ONE + assert(builder->data != NULL); + assert(builder->index_ct < builder->max_index_ct); + assert(builder->use_prim_restart); +#endif + + builder->data[builder->index_ct++] = GWN_PRIM_RESTART; + } + +void GWN_indexbuf_add_point_vert(Gwn_IndexBufBuilder* builder, unsigned v) + { +#if TRUST_NO_ONE + assert(builder->prim_type == GWN_PRIM_POINTS); +#endif + + GWN_indexbuf_add_generic_vert(builder, v); + } + +void GWN_indexbuf_add_line_verts(Gwn_IndexBufBuilder* builder, unsigned v1, unsigned v2) + { +#if TRUST_NO_ONE + assert(builder->prim_type == GWN_PRIM_LINES); + assert(v1 != v2); +#endif + + GWN_indexbuf_add_generic_vert(builder, v1); + GWN_indexbuf_add_generic_vert(builder, v2); + } + +void GWN_indexbuf_add_tri_verts(Gwn_IndexBufBuilder* builder, unsigned v1, unsigned v2, unsigned v3) + { +#if TRUST_NO_ONE + assert(builder->prim_type == GWN_PRIM_TRIS); + assert(v1 != v2 && v2 != v3 && v3 != v1); +#endif + + GWN_indexbuf_add_generic_vert(builder, v1); + GWN_indexbuf_add_generic_vert(builder, v2); + GWN_indexbuf_add_generic_vert(builder, v3); + } + +void GWN_indexbuf_add_line_adj_verts(Gwn_IndexBufBuilder* builder, unsigned v1, unsigned v2, unsigned v3, unsigned v4) + { +#if TRUST_NO_ONE + assert(builder->prim_type == GWN_PRIM_LINES_ADJ); + assert(v2 != v3); /* only the line need diff indices */ +#endif + + GWN_indexbuf_add_generic_vert(builder, v1); + GWN_indexbuf_add_generic_vert(builder, v2); + GWN_indexbuf_add_generic_vert(builder, v3); + GWN_indexbuf_add_generic_vert(builder, v4); + } + +#if GWN_TRACK_INDEX_RANGE +// Everything remains 32 bit while building to keep things simple. +// Find min/max after, then convert to smallest index type possible. + +static unsigned index_range(const unsigned values[], unsigned value_ct, unsigned* min_out, unsigned* max_out) + { + if (value_ct == 0) + { + *min_out = 0; + *max_out = 0; + return 0; + } + unsigned min_value = values[0]; + unsigned max_value = values[0]; + for (unsigned i = 1; i < value_ct; ++i) + { + const unsigned value = values[i]; + if (value == GWN_PRIM_RESTART) + continue; + else if (value < min_value) + min_value = value; + else if (value > max_value) + max_value = value; + } + *min_out = min_value; + *max_out = max_value; + return max_value - min_value; + } + +static void squeeze_indices_byte(Gwn_IndexBufBuilder *builder, Gwn_IndexBuf* elem) + { + const unsigned *values = builder->data; + const unsigned index_ct = elem->index_ct; + + // data will never be *larger* than builder->data... + // converting in place to avoid extra allocation + GLubyte *data = (GLubyte *)builder->data; + + if (elem->max_index > 0xFF) + { + const unsigned base = elem->min_index; + + elem->base_index = base; + elem->min_index = 0; + elem->max_index -= base; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (values[i] == GWN_PRIM_RESTART) ? 0xFF : (GLubyte)(values[i] - base); + } + else + { + elem->base_index = 0; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (GLubyte)(values[i]); + } + } + +static void squeeze_indices_short(Gwn_IndexBufBuilder *builder, Gwn_IndexBuf* elem) + { + const unsigned *values = builder->data; + const unsigned index_ct = elem->index_ct; + + // data will never be *larger* than builder->data... + // converting in place to avoid extra allocation + GLushort *data = (GLushort *)builder->data; + + if (elem->max_index > 0xFFFF) + { + const unsigned base = elem->min_index; + + elem->base_index = base; + elem->min_index = 0; + elem->max_index -= base; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (values[i] == GWN_PRIM_RESTART) ? 0xFFFF : (GLushort)(values[i] - base); + } + else + { + elem->base_index = 0; + + for (unsigned i = 0; i < index_ct; ++i) + data[i] = (GLushort)(values[i]); + } + } + +#endif // GWN_TRACK_INDEX_RANGE + +Gwn_IndexBuf* GWN_indexbuf_build(Gwn_IndexBufBuilder* builder) + { + Gwn_IndexBuf* elem = calloc(1, sizeof(Gwn_IndexBuf)); + GWN_indexbuf_build_in_place(builder, elem); + return elem; + } + +void GWN_indexbuf_build_in_place(Gwn_IndexBufBuilder* builder, Gwn_IndexBuf* elem) + { +#if TRUST_NO_ONE + assert(builder->data != NULL); +#endif + + elem->index_ct = builder->index_ct; + elem->use_prim_restart = builder->use_prim_restart; + +#if GWN_TRACK_INDEX_RANGE + unsigned range = index_range(builder->data, builder->index_ct, &elem->min_index, &elem->max_index); + + // count the primitive restart index. + if (elem->use_prim_restart) + range += 1; + + if (range <= 0xFF) + { + elem->index_type = GWN_INDEX_U8; + squeeze_indices_byte(builder, elem); + } + else if (range <= 0xFFFF) + { + elem->index_type = GWN_INDEX_U16; + squeeze_indices_short(builder, elem); + } + else + { + elem->index_type = GWN_INDEX_U32; + elem->base_index = 0; + } + + elem->gl_index_type = convert_index_type_to_gl(elem->index_type); +#endif + + if (elem->vbo_id == 0) + elem->vbo_id = GWN_buf_id_alloc(); + + // send data to GPU + // GL_ELEMENT_ARRAY_BUFFER changes the state of the last VAO bound, + // so we use the GL_ARRAY_BUFFER here to create a buffer without + // interfering in the VAO state. + glBindBuffer(GL_ARRAY_BUFFER, elem->vbo_id); + glBufferData(GL_ARRAY_BUFFER, GWN_indexbuf_size_get(elem), builder->data, GL_STATIC_DRAW); + + // discard builder (one-time use) + free(builder->data); + builder->data = NULL; + // other fields are safe to leave + } + +void GWN_indexbuf_use(Gwn_IndexBuf* elem) + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elem->vbo_id); + } + +void GWN_indexbuf_discard(Gwn_IndexBuf* elem) + { + if (elem->vbo_id) + GWN_buf_id_free(elem->vbo_id); + + free(elem); + } |