This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/gpu/gawain/element.c

263 lines
6.2 KiB
C
Raw Normal View History

// 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 "element.h"
#include <stdlib.h>
#define KEEP_SINGLE_COPY 1
unsigned ElementList_size(const ElementList* elem)
{
#if TRACK_INDEX_RANGE
switch (elem->index_type)
{
case GL_UNSIGNED_BYTE: return elem->index_ct * sizeof(GLubyte);
case GL_UNSIGNED_SHORT: return elem->index_ct * sizeof(GLushort);
case GL_UNSIGNED_INT: return elem->index_ct * sizeof(GLuint);
default:
#if TRUST_NO_ONE
assert(false);
#endif
return 0;
}
#else
return elem->index_ct * sizeof(GLuint);
#endif
}
static void ElementList_prime(ElementList* elem)
{
glGenBuffers(1, &elem->vbo_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elem->vbo_id);
// fill with delicious data & send to GPU the first time only
glBufferData(GL_ELEMENT_ARRAY_BUFFER, ElementList_size(elem), elem->data, GL_STATIC_DRAW);
#if KEEP_SINGLE_COPY
// now that GL has a copy, discard original
free(elem->data);
elem->data = NULL;
#endif
}
void ElementList_use(ElementList* elem)
{
if (elem->vbo_id)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elem->vbo_id);
else
ElementList_prime(elem);
}
void ElementListBuilder_init(ElementListBuilder* builder, GLenum prim_type, unsigned prim_ct, unsigned vertex_ct)
{
unsigned verts_per_prim = 0;
switch (prim_type)
{
case GL_POINTS:
verts_per_prim = 1;
break;
case GL_LINES:
verts_per_prim = 2;
break;
case GL_TRIANGLES:
verts_per_prim = 3;
break;
default:
#if TRUST_NO_ONE
assert(false);
#endif
return;
}
builder->max_allowed_index = vertex_ct - 1;
builder->max_index_ct = prim_ct * verts_per_prim;
builder->index_ct = 0; // start empty
builder->prim_type = prim_type;
builder->data = calloc(builder->max_index_ct, sizeof(unsigned));
}
void add_generic_vertex(ElementListBuilder* 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 add_point_vertex(ElementListBuilder* builder, unsigned v)
{
#if TRUST_NO_ONE
assert(builder->prim_type == GL_POINTS);
#endif
add_generic_vertex(builder, v);
}
void add_line_vertices(ElementListBuilder* builder, unsigned v1, unsigned v2)
{
#if TRUST_NO_ONE
assert(builder->prim_type == GL_LINES);
assert(v1 != v2);
#endif
add_generic_vertex(builder, v1);
add_generic_vertex(builder, v2);
}
void add_triangle_vertices(ElementListBuilder* builder, unsigned v1, unsigned v2, unsigned v3)
{
#if TRUST_NO_ONE
assert(builder->prim_type == GL_TRIANGLES);
assert(v1 != v2 && v2 != v3 && v3 != v1);
#endif
add_generic_vertex(builder, v1);
add_generic_vertex(builder, v2);
add_generic_vertex(builder, v3);
}
#if 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)
{
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 < 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(const unsigned values[], ElementList* elem)
{
const unsigned index_ct = elem->index_ct;
GLubyte* data = malloc(index_ct * sizeof(GLubyte));
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] = (GLubyte)(values[i] - base);
}
else
{
elem->base_index = 0;
for (unsigned i = 0; i < index_ct; ++i)
data[i] = (GLubyte)(values[i]);
}
elem->data = data;
}
static void squeeze_indices_short(const unsigned values[], ElementList* elem)
{
const unsigned index_ct = elem->index_ct;
GLushort* data = malloc(index_ct * sizeof(GLushort));
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] = (GLushort)(values[i] - base);
}
else
{
elem->base_index = 0;
for (unsigned i = 0; i < index_ct; ++i)
data[i] = (GLushort)(values[i]);
}
elem->data = data;
}
#endif // TRACK_INDEX_RANGE
void ElementList_build(ElementListBuilder* builder, ElementList* elem)
{
#if TRUST_NO_ONE
assert(builder->data != NULL);
#endif
elem->index_ct = builder->index_ct;
#if TRACK_INDEX_RANGE
const unsigned range = index_range(builder->data, builder->index_ct, &elem->min_index, &elem->max_index);
if (range <= 0xFF)
{
elem->index_type = GL_UNSIGNED_BYTE;
squeeze_indices_byte(builder->data, elem);
}
else if (range <= 0xFFFF)
{
elem->index_type = GL_UNSIGNED_SHORT;
squeeze_indices_short(builder->data, elem);
}
else
{
elem->index_type = GL_UNSIGNED_INT;
elem->base_index = 0;
if (builder->index_ct < builder->max_index_ct)
{
builder->data = realloc(builder->data, builder->index_ct * sizeof(unsigned));
// TODO: realloc only if index_ct is much smaller than max_index_ct
}
elem->data = builder->data;
}
#else
if (builder->index_ct < builder->max_index_ct)
{
builder->data = realloc(builder->data, builder->index_ct * sizeof(unsigned));
// TODO: realloc only if index_ct is much smaller than max_index_ct
}
elem->data = builder->data;
#endif
// elem->data will never be *larger* than builder->data... how about converting
// in place to avoid extra allocation?
elem->vbo_id = 0;
// TODO: create GL buffer object directly, based on an input flag
// discard builder (one-time use)
if (builder->data != elem->data)
free(builder->data);
builder->data = NULL;
// other fields are safe to leave
}