Created
March 15, 2010 05:28
-
-
Save nilium/332553 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| Copyright (c) 2010 Noel R. Cower | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| THE SOFTWARE. | |
| */ | |
| #include <pub.mod/glew.mod/GL/glew.h> | |
| #include <math.h> | |
| #include <float.h> | |
| #include <string.h> | |
| #include <stdio.h> | |
| #include <deque> | |
| #include <iostream> | |
| extern "C" { | |
| #pragma mark Utility | |
| inline bool floats_differ(float l, float r) { | |
| return (FLT_EPSILON<=fabsf(l-r)); | |
| } | |
| #pragma mark brl.graphics imports | |
| extern int brl_graphics_GraphicsSeq; | |
| #pragma mark Constants | |
| const size_t RENDER_BUFFER_INIT_ELEMENTS = 512; | |
| const double RENDER_BUFFER_SCALE = 2.0; | |
| #pragma mark Types | |
| typedef struct s_blend_factors { | |
| GLenum source; | |
| GLenum dest; | |
| } blend_factors_t; | |
| typedef struct s_alpha_test { | |
| GLenum func; | |
| GLclampf ref; | |
| } alpha_test_t; | |
| typedef struct s_render_indices { | |
| uint32_t index_from; | |
| uint32_t indices; | |
| uint32_t num_indices; | |
| } render_indices_t; | |
| typedef std::deque<render_indices_t> render_indices_deque_t; | |
| typedef struct s_render_state { | |
| GLuint texture_name; | |
| GLenum render_mode; | |
| blend_factors_t blend; | |
| alpha_test_t alpha; | |
| GLfloat line_width; | |
| } render_state_t; | |
| typedef std::deque<render_state_t> render_state_deque_t; | |
| typedef struct s_render_buffer { | |
| GLfloat *vertices, *texcoords; | |
| GLubyte *colors; | |
| size_t vertices_length, texcoords_length, colors_length; | |
| uint32_t index; | |
| uint32_t sets; | |
| GLint *indices; | |
| GLsizei *counts; | |
| uint32_t indices_length; | |
| uint32_t lock; | |
| render_indices_deque_t *render_indices; | |
| render_state_deque_t *render_states; | |
| } render_buffer_t; | |
| #pragma mark Globals | |
| static struct { | |
| bool state_bound; | |
| bool texture2D_enabled; | |
| GLuint texture2D_binding; | |
| int sequence; | |
| bool blend_enabled; | |
| bool alpha_test_enabled; | |
| render_state_t active; | |
| } rs_globals = { | |
| false, false, (GLuint)0, 0, false, false, | |
| }; | |
| #pragma mark Prototypes | |
| // render_state_t | |
| render_state_t *rs_init(render_state_t *rs); | |
| render_state_t *rs_copy(render_state_t *rs, render_state_t *to); | |
| void rs_bind(render_state_t *rs); | |
| void rs_restore(render_state_t *rs); | |
| void rs_set_texture(GLuint name); | |
| // render_buffer_t | |
| render_buffer_t *rb_new(); | |
| render_buffer_t *rb_init(render_buffer_t *rb); | |
| void rb_destroy(render_buffer_t *rb, int free_rb); | |
| //inline void rb_new_state(render_buffer_t *rb); | |
| void rb_set_texture(render_buffer_t *rb, GLuint name); | |
| void rb_set_mode(render_buffer_t *rb, GLenum mode); | |
| void rb_set_blend_func(render_buffer_t *rb, GLenum source, GLenum dest); | |
| void rb_set_alpha_func(render_buffer_t *rb, GLenum func, GLclampf ref); | |
| void rb_set_line_width(render_buffer_t *rb, GLfloat width); | |
| void rb_add_vertices(render_buffer_t *rb, int elements, GLfloat *points, GLfloat *texcoords, GLubyte *colors); | |
| void rb_lock_buffers(render_buffer_t *rb); | |
| void rb_unlock_buffers(render_buffer_t *rb); | |
| void rb_render(render_buffer_t *rb); | |
| void rb_reset(render_buffer_t *rb); | |
| #pragma mark Implementations | |
| // render_state_t | |
| render_state_t *rs_init(render_state_t *rs) { | |
| if (rs) { | |
| rs->texture_name = (GLuint)0; | |
| rs->render_mode = GL_POLYGON; | |
| rs->blend.source = GL_ONE; | |
| rs->blend.dest = GL_ZERO; | |
| rs->alpha.func = GL_ALWAYS; | |
| rs->alpha.ref = (GLclampf)0.0f; | |
| rs->line_width = 1.0f; | |
| } | |
| return rs; | |
| } | |
| void rs_destroy(render_state_t *rs, int free) { | |
| if (rs != NULL && free != 0) { | |
| delete rs; | |
| } | |
| } | |
| render_state_t *rs_copy(render_state_t *rs, render_state_t *to) { | |
| *to = *rs; | |
| return to; | |
| } | |
| void rs_bind(render_state_t *rs) { | |
| if (!rs_globals.state_bound) { | |
| rs_init(&rs_globals.active); | |
| } | |
| render_state_t active = rs_globals.active; | |
| rs_set_texture(rs->texture_name); | |
| if (!rs_globals.state_bound || rs->blend.dest != active.blend.dest || | |
| rs->blend.source != active.blend.source) { | |
| if (rs->blend.dest == GL_ONE && rs->blend.source == GL_ZERO && rs_globals.blend_enabled) { | |
| glDisable(GL_BLEND); | |
| rs_globals.blend_enabled = false; | |
| } else { | |
| if (!rs_globals.blend_enabled) { | |
| glEnable(GL_BLEND); | |
| rs_globals.blend_enabled = true; | |
| } | |
| glBlendFunc(rs->blend.source, rs->blend.dest); | |
| } | |
| } | |
| if (!rs_globals.state_bound || rs->alpha.func != active.alpha.func || floats_differ(rs->alpha.ref, active.alpha.ref)) { | |
| if (rs->alpha.func == GL_ALWAYS && rs_globals.alpha_test_enabled) { | |
| glDisable(GL_ALPHA_TEST); | |
| rs_globals.alpha_test_enabled = false; | |
| } else { | |
| if (!rs_globals.alpha_test_enabled) { | |
| glEnable(GL_ALPHA_TEST); | |
| rs_globals.alpha_test_enabled = true; | |
| } | |
| glAlphaFunc(rs->alpha.func, rs->alpha.ref); | |
| } | |
| } | |
| if (rs->render_mode == GL_LINES && floats_differ(rs->line_width, active.line_width)) { | |
| glLineWidth(rs->line_width); | |
| } | |
| rs_globals.active = *rs; | |
| rs_globals.state_bound = true; | |
| } | |
| void rs_restore(render_state_t *rs) { | |
| render_state_t restore; | |
| if (rs) { | |
| restore = *rs; | |
| } else { | |
| if (!rs_globals.state_bound) { | |
| rs_init(&restore); | |
| } else { | |
| restore = rs_globals.active; | |
| } | |
| } | |
| if (rs_globals.alpha_test_enabled) { | |
| glEnable(GL_ALPHA_TEST); | |
| } else { | |
| glDisable(GL_ALPHA_TEST); | |
| } | |
| if (rs_globals.blend_enabled) { | |
| glEnable(GL_BLEND); | |
| } else { | |
| glDisable(GL_BLEND); | |
| } | |
| if (rs_globals.sequence == brl_graphics_GraphicsSeq && rs_globals.texture2D_enabled && | |
| rs_globals.texture2D_binding) { | |
| glBindTexture(GL_TEXTURE_2D, rs_globals.texture2D_binding); | |
| } else { | |
| rs_globals.texture2D_binding = 0; | |
| } | |
| if (rs_globals.texture2D_enabled) { | |
| glEnable(GL_TEXTURE_2D); | |
| } else { | |
| glDisable(GL_TEXTURE_2D); | |
| } | |
| rs_bind(&restore); | |
| } | |
| void rs_set_texture(GLuint name) { | |
| int cur_seq = brl_graphics_GraphicsSeq; | |
| int active_seq = rs_globals.sequence; | |
| if (name == rs_globals.texture2D_binding && cur_seq == active_seq ) { | |
| return; | |
| } | |
| if (name) { | |
| if (!rs_globals.texture2D_enabled || cur_seq != active_seq) { | |
| glEnable(GL_TEXTURE_2D); | |
| rs_globals.texture2D_enabled = true; | |
| } | |
| glBindTexture(GL_TEXTURE_2D, name); | |
| } else if (rs_globals.texture2D_enabled || cur_seq == active_seq) { | |
| glDisable(GL_TEXTURE_2D); | |
| rs_globals.texture2D_enabled = false; | |
| } | |
| rs_globals.sequence = cur_seq; | |
| rs_globals.texture2D_binding = name; | |
| } | |
| // render_buffer_t | |
| /* // UNUSED | |
| render_buffer_t *rb_new() { | |
| return rb_init(new render_buffer_t()); | |
| } | |
| */ | |
| render_buffer_t *rb_init(render_buffer_t *rb) { | |
| if (rb) { | |
| rb->vertices_length = RENDER_BUFFER_INIT_ELEMENTS*3; | |
| rb->texcoords_length = RENDER_BUFFER_INIT_ELEMENTS*2; | |
| rb->colors_length = RENDER_BUFFER_INIT_ELEMENTS*4; | |
| rb->vertices = (GLfloat*)malloc(rb->vertices_length*sizeof(GLfloat)); | |
| rb->texcoords = (GLfloat*)malloc(rb->texcoords_length*sizeof(GLfloat)); | |
| rb->colors = (GLubyte*)malloc(rb->colors_length*sizeof(GLubyte)); | |
| rb->index = 0; | |
| rb->sets = 0; | |
| rb->indices_length = RENDER_BUFFER_INIT_ELEMENTS; | |
| rb->indices = (GLint*)malloc(RENDER_BUFFER_INIT_ELEMENTS*sizeof(GLint)); | |
| rb->counts = (GLsizei*)malloc(RENDER_BUFFER_INIT_ELEMENTS*sizeof(GLsizei)); | |
| rb->lock = 0; | |
| rb->render_indices = new render_indices_deque_t(); | |
| rb->render_indices->push_back((render_indices_t){0, 0, 0}); | |
| render_state_t init_state; | |
| rs_init(&init_state); | |
| rb->render_states = new render_state_deque_t(); | |
| rb->render_states->push_back(init_state); | |
| } | |
| return rb; | |
| } | |
| void rb_destroy(render_buffer_t *rb, int free_rb) { | |
| if (rb) { | |
| free(rb->vertices); | |
| free(rb->texcoords); | |
| free(rb->colors); | |
| free(rb->indices); | |
| free(rb->counts); | |
| delete rb->render_states; | |
| delete rb->render_indices; | |
| if (free_rb != 0) { | |
| delete rb; | |
| } | |
| } | |
| } | |
| inline void rb_new_state(render_buffer_t *rb) { | |
| if (0 < rb->render_indices->back().indices) { | |
| render_indices_t indices; | |
| indices.index_from = rb->sets; | |
| indices.indices = indices.num_indices = 0; | |
| rb->render_indices->push_back(indices); | |
| rb->render_states->push_back(rb->render_states->back()); | |
| } | |
| } | |
| void rb_set_texture(render_buffer_t *rb, GLuint name) { | |
| if (rb->render_states->back().texture_name != name) { | |
| rb_new_state(rb); | |
| rb->render_states->back().texture_name = name; | |
| } | |
| } | |
| void rb_set_mode(render_buffer_t *rb, GLenum mode) { | |
| if (rb->render_states->back().render_mode != mode) { | |
| rb_new_state(rb); | |
| rb->render_states->back().render_mode = mode; | |
| } | |
| } | |
| void rb_set_blend_func(render_buffer_t *rb, GLenum source, GLenum dest) { | |
| blend_factors_t orig = rb->render_states->back().blend; | |
| if (orig.source != source || orig.dest != dest) { | |
| rb_new_state(rb); | |
| rb->render_states->back().blend = (blend_factors_t){source, dest}; | |
| } | |
| } | |
| void rb_set_alpha_func(render_buffer_t *rb, GLenum func, GLclampf ref) { | |
| alpha_test_t orig = rb->render_states->back().alpha; | |
| if (orig.func != func || floats_differ(orig.ref, ref)) { | |
| rb_new_state(rb); | |
| render_state_t &state = rb->render_states->back(); | |
| state.alpha.func = func; | |
| state.alpha.ref = ref; | |
| } | |
| } | |
| void rb_set_line_width(render_buffer_t *rb, GLfloat width) { | |
| if (floats_differ(rb->render_states->back().line_width, width)) { | |
| rb_new_state(rb); | |
| rb->render_states->back().line_width = width; | |
| } | |
| } | |
| void rb_add_vertices(render_buffer_t *rb, int elements, GLfloat *vertices, GLfloat *texcoords, GLubyte *colors) { | |
| if (rb->lock != 0) { | |
| fprintf(stderr, "attempt to add vertices to buffer when locked\n"); | |
| return; | |
| } | |
| if (rb->indices_length <= rb->sets) { | |
| size_t new_size = (size_t)(rb->indices_length*RENDER_BUFFER_SCALE); | |
| rb->indices = (GLint*)realloc(rb->indices, new_size*sizeof(GLint)); | |
| rb->counts = (GLsizei*)realloc(rb->counts, new_size*sizeof(GLsizei)); | |
| } | |
| uint32_t index = rb->index; | |
| uint32_t set = rb->sets++; | |
| rb->indices[set] = index; | |
| rb->counts[set] = elements; | |
| { | |
| size_t sizereq = (size_t)(index+elements); | |
| if (rb->vertices_length < sizereq*3) { | |
| size_t new_size = (size_t)(rb->vertices_length*RENDER_BUFFER_SCALE); | |
| if (new_size < sizereq*3) { | |
| new_size = sizereq*3; | |
| } | |
| rb->vertices = (GLfloat*)realloc(rb->vertices, new_size*sizeof(GLfloat)); | |
| } | |
| sizereq *= 2; | |
| if (rb->texcoords_length < sizereq) { | |
| size_t new_size = (size_t)(rb->texcoords_length*RENDER_BUFFER_SCALE); | |
| if (new_size < sizereq) { | |
| new_size = sizereq; | |
| } | |
| rb->texcoords = (GLfloat*)realloc(rb->texcoords, new_size*sizeof(GLfloat)); | |
| } | |
| sizereq *= 2; | |
| if (rb->colors_length < sizereq) { | |
| size_t new_size = (size_t)(rb->colors_length*RENDER_BUFFER_SCALE); | |
| if (new_size < sizereq) { | |
| new_size = sizereq; | |
| } | |
| rb->colors = (GLubyte*)realloc(rb->colors, new_size*sizeof(GLubyte)); | |
| } | |
| } | |
| memcpy(rb->vertices+(index*3), vertices, elements*3*sizeof(GLfloat)); | |
| if (texcoords != NULL) { | |
| memcpy(rb->texcoords+(index*2), texcoords, elements*2*sizeof(GLfloat)); | |
| } else { | |
| memset(rb->texcoords+(index*2), 0, elements*2*sizeof(GLfloat)); | |
| } | |
| if (colors != NULL) { | |
| memcpy(rb->colors+(index*4), colors, elements*4*sizeof(GLubyte)); | |
| } else { | |
| memset(rb->colors+(index*4), 255, elements*4*sizeof(GLubyte)); | |
| } | |
| rb->index += elements; | |
| rb->render_indices->back().indices += 1; | |
| rb->render_indices->back().num_indices += elements; | |
| } | |
| void rb_lock_buffers(render_buffer_t *rb) { | |
| if (rb->lock == 0 && rb->index) { | |
| glVertexPointer(3, GL_FLOAT, 0, rb->vertices); | |
| glColorPointer(4, GL_UNSIGNED_BYTE, 0, rb->colors); | |
| glTexCoordPointer(2, GL_FLOAT, 0, rb->texcoords); | |
| if (GLEW_EXT_compiled_vertex_array) { | |
| glLockArraysEXT(0, rb->index); | |
| } | |
| } | |
| rb->lock += 1; | |
| } | |
| void rb_unlock_buffers(render_buffer_t *rb) { | |
| if (rb->lock == 0) { | |
| fprintf(stderr, "woops - unlock underflow\n"); | |
| exit(1); | |
| return; | |
| } | |
| rb->lock -= 1; | |
| if (rb->lock == 0 && rb->index) { | |
| if (GLEW_EXT_compiled_vertex_array) { | |
| glUnlockArraysEXT(); | |
| } | |
| glVertexPointer(4, GL_FLOAT, 0, NULL); | |
| glTexCoordPointer(4, GL_FLOAT, 0, NULL); | |
| glColorPointer(4, GL_FLOAT, 0, NULL); | |
| } | |
| } | |
| void rb_render(render_buffer_t *rb) { | |
| if (rb->sets == 0) { | |
| return; | |
| } | |
| rb_lock_buffers(rb); | |
| GLint *indices_ptr = rb->indices; | |
| GLsizei *counts_ptr = rb->counts; | |
| render_indices_deque_t::const_iterator index_iter = rb->render_indices->begin(); | |
| render_state_deque_t::const_iterator state_iter = rb->render_states->begin(); | |
| render_indices_deque_t::const_iterator index_end = rb->render_indices->end(); | |
| render_state_deque_t::const_iterator state_end = rb->render_states->end(); | |
| render_state_t state; | |
| if (GLEW_VERSION_1_4) { | |
| while (index_iter != index_end && state_iter != state_end) { | |
| GLint indices = index_iter->indices; | |
| state = *state_iter; | |
| if (0 < indices) { | |
| rs_bind(&state); | |
| uint32_t index_from = index_iter->index_from; | |
| if (1 < indices) { | |
| glMultiDrawArrays(state_iter->render_mode, &indices_ptr[index_from], &counts_ptr[index_from], indices); | |
| } else { | |
| glDrawArrays(state_iter->render_mode, indices_ptr[index_from], counts_ptr[index_from]); | |
| } | |
| } | |
| ++index_iter; | |
| ++state_iter; | |
| } | |
| } else { | |
| while (index_iter != index_end && state_iter != state_end) { | |
| uint32_t indices = index_iter->indices; | |
| if (0 < indices) { | |
| rs_bind(&state); | |
| uint32_t index_from = index_iter->index_from; | |
| for(; index_from < indices; ++index_from) { | |
| glDrawArrays(state_iter->render_mode, indices_ptr[index_from], counts_ptr[index_from]); | |
| } | |
| } | |
| ++index_iter; | |
| ++state_iter; | |
| } | |
| } | |
| rb_unlock_buffers(rb); | |
| } | |
| void rb_reset(render_buffer_t *rb) { | |
| if (rb->lock != 0) { | |
| // TODO: error? | |
| return; | |
| } | |
| rb->index = 0; | |
| rb->sets = 0; | |
| rb->render_indices->clear(); | |
| render_indices_t indices; | |
| indices.index_from = indices.indices = indices.num_indices = 0; | |
| rb->render_indices->push_back(indices); | |
| render_state_t state = rb->render_states->back(); | |
| rb->render_states->clear(); | |
| rb->render_states->push_back(state); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment