- #include "gl_renderer.h" 
- #include "brender/brender.h" 
- #include "harness.h" 
- #include "harness/trace.h" 
- #include "resources/3d_frag.glsl.h" 
- #include "resources/3d_vert.glsl.h" 
- #include "resources/framebuffer_frag.glsl.h" 
- #include "resources/framebuffer_vert.glsl.h" 
- #include "stored_context.h" 
-   
- #include <glad/glad.h> 
- #include <stdio.h> 
- #include <string.h> 
-   
- static GLuint screen_buffer_vao, screen_buffer_ebo; 
- static GLuint fullscreen_quad_texture, palette_texture, depth_texture; 
-   
- static GLuint shader_program_2d; 
- static GLuint shader_program_3d; 
- static GLuint framebuffer_id, framebuffer_texture = 0; 
-   
- // holds the latest uploaded version of the colour_buffer. Available in shader for blending 
- static GLuint current_colourbuffer_texture; 
-   
- static uint8_t gl_palette[4 * 256]; // RGBA 
- static uint8_t* screen_buffer_flip_pixels; 
- static uint16_t* depth_buffer_flip_pixels; 
-   
- static int render_width, render_height; 
- static int vp_x, vp_y, vp_width, vp_height; 
-   
- static br_pixelmap *last_colour_buffer, *last_depth_buffer; 
- static int dirty_buffers = 0; 
-   
- static tStored_material* current_material; 
- static br_pixelmap* current_shade_table; 
-   
- static br_matrix4 view_matrix; 
-   
- // Increment flush_counter during flush, and upload_counter when we upload the colour_buffer texture. 
- // If the counters are equal, we can avoid re-uploading the same thing. 
- static unsigned int flush_counter = 0, colourbuffer_upload_counter = 0; 
-   
- typedef struct gl_vertex { 
-     br_vector3 p; 
-     br_vector2 map; 
-     br_vector3 n; 
-     float colour_index; // float to allow interpolation 
- } gl_vertex; 
-   
- struct { 
-     GLuint model, view, projection; 
-     // gl_NormalMatrix replacement 
-     GLuint normal_matrix; 
-     GLuint clip_plane_count; 
-     GLuint clip_planes[6]; 
-     GLuint colour_buffer_texture; 
-     GLuint viewport_height; 
-   
-     GLuint material_flags; 
-     GLuint material_texture_enabled; 
-     GLuint material_texture_pixelmap; 
-     GLuint material_uv_transform; 
-     GLuint material_shade_table; 
-     GLuint material_blend_enabled; 
-     GLuint material_blend_table; 
-     GLuint material_index_base; 
-     // GLuint material_index_range;  TODO: re-add when we add untextured lighting 
-     GLuint material_shade_table_height; 
-   
- } uniforms_3d; 
-   
- struct { 
-     GLuint pixels, palette; 
- } uniforms_2d; 
-   
- static GLuint CreateShaderProgram(char* name, const char* vertex_shader, const int vertex_shader_len, const char* fragment_shader, const int fragment_shader_len) { 
-     int success; 
-     char log_buffer[1024]; 
-     GLuint program; 
-     GLuint v_shader, f_shader; 
-   
-     program = glCreateProgram(); 
-     v_shader = glCreateShader(GL_VERTEX_SHADER); 
-     const GLchar* vertex_sources[] = { vertex_shader }; 
-     glShaderSource(v_shader, 1, vertex_sources, &vertex_shader_len); 
-     glCompileShader(v_shader); 
-     glGetShaderiv(v_shader, GL_COMPILE_STATUS, &success); 
-     if (!success) { 
-         glGetShaderInfoLog(v_shader, 1024, NULL, log_buffer); 
-         LOG_PANIC("shader %s failed to compile: %s", name, log_buffer); 
-     } 
-   
-     f_shader = glCreateShader(GL_FRAGMENT_SHADER); 
-     const GLchar* fragment_sources[] = { fragment_shader }; 
-     glShaderSource(f_shader, 1, fragment_sources, &fragment_shader_len); 
-     glCompileShader(f_shader); 
-     glGetShaderiv(f_shader, GL_COMPILE_STATUS, &success); 
-     if (!success) { 
-         char log_buffer[1024]; 
-         glGetShaderInfoLog(f_shader, 1024, NULL, log_buffer); 
-         LOG_PANIC("shader %s failed to compile: %s", name, log_buffer); 
-     } 
-   
-     glAttachShader(program, v_shader); 
-     glAttachShader(program, f_shader); 
-     glLinkProgram(program); 
-     glDeleteShader(v_shader); 
-     glDeleteShader(f_shader); 
-   
-     GLint link_ok = GL_FALSE; 
-     glGetProgramiv(program, GL_LINK_STATUS, &link_ok); 
-     if (!link_ok) { 
-         glGetShaderInfoLog(program, 1024, NULL, log_buffer); 
-         LOG_PANIC("shader program %s failed to link: %s", name, log_buffer); 
-     } 
-     return program; 
- } 
-   
- static GLint GetValidatedUniformLocation(GLuint program, char* uniform_name) { 
-     GLint location; 
-     location = glGetUniformLocation(program, uniform_name); 
-     if (location == -1) { 
-         LOG_PANIC("glGetUniformLocation(%d, %s) failed. Check the shader uniform names.", program, uniform_name); 
-     } 
-     return location; 
- } 
-   
- static void LoadShaders(void) { 
-     shader_program_2d = CreateShaderProgram("framebuffer", RESOURCES_FRAMEBUFFER_VERT_GLSL, sizeof(RESOURCES_FRAMEBUFFER_VERT_GLSL), RESOURCES_FRAMEBUFFER_FRAG_GLSL, sizeof(RESOURCES_FRAMEBUFFER_FRAG_GLSL)); 
-     glUseProgram(shader_program_2d); 
-     uniforms_2d.pixels = GetValidatedUniformLocation(shader_program_2d, "u_pixels"); 
-     uniforms_2d.palette = GetValidatedUniformLocation(shader_program_2d, "u_palette"); 
-   
-     // bind the uniform samplers to texture units: 
-     glUniform1i(uniforms_2d.pixels, 0); 
-     glUniform1i(uniforms_2d.palette, 1); 
-   
-     shader_program_3d = CreateShaderProgram("3d", RESOURCES_3D_VERT_GLSL, sizeof(RESOURCES_3D_VERT_GLSL), RESOURCES_3D_FRAG_GLSL, sizeof(RESOURCES_3D_FRAG_GLSL)); 
-     glUseProgram(shader_program_3d); 
-     uniforms_3d.clip_plane_count = GetValidatedUniformLocation(shader_program_3d, "u_clip_plane_count"); 
-     for (int i = 0; i < 6; i++) { 
-         char name[32]; 
-         sprintf(- name , "u_clip_planes[%d]",-  i );
 
-         uniforms_3d.clip_planes[i] = GetValidatedUniformLocation(shader_program_3d, name); 
-     } 
-   
-     uniforms_3d.model = GetValidatedUniformLocation(shader_program_3d, "u_model"); 
-     uniforms_3d.colour_buffer_texture = GetValidatedUniformLocation(shader_program_3d, "u_colour_buffer"); 
-     uniforms_3d.projection = GetValidatedUniformLocation(shader_program_3d, "u_projection"); 
-     uniforms_3d.view = GetValidatedUniformLocation(shader_program_3d, "u_view"); 
-     uniforms_3d.viewport_height = GetValidatedUniformLocation(shader_program_3d, "u_viewport_height"); 
-     uniforms_3d.normal_matrix = GetValidatedUniformLocation(shader_program_3d, "u_normal_matrix"); 
-   
-     uniforms_3d.material_flags = GetValidatedUniformLocation(shader_program_3d, "u_material_flags"); 
-     uniforms_3d.material_texture_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_enabled"); 
-     uniforms_3d.material_texture_pixelmap = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_pixelmap"); 
-     uniforms_3d.material_uv_transform = GetValidatedUniformLocation(shader_program_3d, "u_material_uv_transform"); 
-     uniforms_3d.material_shade_table = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table"); 
-     uniforms_3d.material_shade_table_height = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table_height"); 
-     uniforms_3d.material_blend_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_enabled"); 
-     uniforms_3d.material_blend_table = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_table"); 
-     uniforms_3d.material_index_base = GetValidatedUniformLocation(shader_program_3d, "u_material_index_base"); 
-     // TODO: re-add when we support untextured lighting 
-     // uniforms_3d.material_index_range = GetValidatedUniformLocation(shader_program_3d, "u_material_index_range"); 
-   
-     // bind the uniform samplers to texture units 
-     glUniform1i(uniforms_3d.material_texture_pixelmap, 0); 
-     // palette occupies texture unit 1 but not required during 3d rendering 
-     glUniform1i(uniforms_3d.material_shade_table, 2); 
-     glUniform1i(uniforms_3d.material_blend_table, 3); 
-     glUniform1i(uniforms_3d.colour_buffer_texture, 4); 
- } 
-   
- static void SetupFullScreenRectGeometry(void) { 
-     float vertices[] = { 
-         // positions          // colors           // texture coords 
-         1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right 
-         1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,  // bottom right 
-         -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left 
-         -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left 
-     }; 
-     unsigned int indices[] = { 
-         0, 1, 3, // first triangle 
-         1, 2, 3  // second triangle 
-     }; 
-   
-     GLuint vbo; 
-     glGenVertexArrays(1, &screen_buffer_vao); 
-     glGenBuffers(1, &vbo); 
-     glGenBuffers(1, &screen_buffer_ebo); 
-   
-     glBindVertexArray(screen_buffer_vao); 
-   
-     glBindBuffer(GL_ARRAY_BUFFER, vbo); 
-     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 
-   
-     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screen_buffer_ebo); 
-     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); 
-   
-     // position attribute 
-     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); 
-     glEnableVertexAttribArray(0); 
-     // color attribute 
-     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); 
-     glEnableVertexAttribArray(1); 
-     // texture coord attribute 
-     glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); 
-     glEnableVertexAttribArray(2); 
-   
-     glBindVertexArray(0); 
- } 
-   
- void GLRenderer_Init(int pRender_width, int pRender_height) { 
-     render_width = pRender_width; 
-     render_height = pRender_height; 
-   
-     LOG_INFO("OpenGL vendor string: %s", glGetString(GL_VENDOR)); 
-     LOG_INFO("OpenGL renderer string: %s", glGetString(GL_RENDERER)); 
-     LOG_INFO("OpenGL version string: %s", glGetString(GL_VERSION)); 
-     LOG_INFO("OpenGL shading language version string: %s", glGetString(GL_SHADING_LANGUAGE_VERSION)); 
-   
-     LoadShaders(); 
-     SetupFullScreenRectGeometry(); 
-   
-     // config 
-     glDisable(GL_BLEND); 
-     glDepthFunc(GL_LESS); 
-     glClearColor(0, 0, 0, 1.0f); 
-     glClear(GL_COLOR_BUFFER_BIT); 
-     glEnable(GL_CULL_FACE); 
-     glCullFace(GL_BACK); 
-   
-     // textures 
-     glGenTextures(1, &fullscreen_quad_texture); 
-     glGenTextures(1, &palette_texture); 
-     glGenTextures(1, &framebuffer_texture); 
-     glGenTextures(1, &depth_texture); 
-     glGenTextures(1, ¤t_colourbuffer_texture); 
-   
-     // setup framebuffer 
-     glGenFramebuffers(1, &framebuffer_id); 
-     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); 
-   
-     // set pixel storage alignment to 1 byte 
-     glPixelStorei(GL_PACK_ALIGNMENT, 1); 
-     glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 
-   
-     glBindTexture(GL_TEXTURE_2D, fullscreen_quad_texture); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
-   
-     glBindTexture(GL_TEXTURE_2D, palette_texture); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
-   
-     glBindTexture(GL_TEXTURE_2D, framebuffer_texture); 
-     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
-     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_texture, 0); 
-   
-     glBindTexture(GL_TEXTURE_2D, current_colourbuffer_texture); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
-   
-     glBindTexture(GL_TEXTURE_2D, depth_texture); 
-     glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, render_width, render_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0); 
-     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); 
-   
-     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 
-         LOG_PANIC("Framebuffer is not complete!"); 
-     } 
-     glBindFramebuffer(GL_FRAMEBUFFER, 0); 
-   
-     screen_buffer_flip_pixels  = malloc(sizeof(uint8_t) *-  render_width  *-  render_height );
-     depth_buffer_flip_pixels  = malloc(sizeof(uint16_t) *-  render_width  *-  render_height );
-   
-     CHECK_GL_ERROR("initializeOpenGLContext"); 
- } 
-   
- void GLRenderer_SetPalette(uint8_t* rgba_colors) { 
-     for (int i = 0; i < 256; i++) { 
-         gl_palette[i * 4] = rgba_colors[i * 4 + 2]; 
-         gl_palette[i * 4 + 1] = rgba_colors[i * 4 + 1]; 
-         gl_palette[i * 4 + 2] = rgba_colors[i * 4]; 
-         // palette index 0 is transparent, so set alpha to 0 
-         if (i == 0) { 
-             gl_palette[i * 4 + 3] = 0; 
-         } else { 
-             gl_palette[i * 4 + 3] = 0xff; 
-         } 
-     } 
-   
-     glActiveTexture(GL_TEXTURE1); 
-     glBindTexture(GL_TEXTURE_2D, palette_texture); 
-     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, gl_palette); 
-   
-     // reset active texture back to default 
-     glActiveTexture(GL_TEXTURE0); 
-   
-     CHECK_GL_ERROR("GLRenderer_SetPalette"); 
- } 
-   
- void GLRenderer_SetShadeTable(br_pixelmap* table) { 
-     if (current_shade_table == table) { 
-         return; 
-     } 
-   
-     // shade table uses texture unit 2 
-     glActiveTexture(GL_TEXTURE2); 
-     tStored_pixelmap* stored = table->stored; 
-     glBindTexture(GL_TEXTURE_2D, stored->id); 
-   
-     // reset active texture back to default 
-     glActiveTexture(GL_TEXTURE0); 
-     current_shade_table = table; 
- } 
-   
- void GLRenderer_SetBlendTable(br_pixelmap* table) { 
-   
-     if (flush_counter != colourbuffer_upload_counter) { 
-         GLRenderer_FlushBuffer(eFlush_color_buffer); 
-         glActiveTexture(GL_TEXTURE4); 
-         glBindTexture(GL_TEXTURE_2D, current_colourbuffer_texture); 
-         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, last_colour_buffer->pixels); 
-         colourbuffer_upload_counter = flush_counter; 
-     } 
-   
-     // blend table uses texture unit 3 
-     glActiveTexture(GL_TEXTURE3); 
-     tStored_pixelmap* stored = table->stored; 
-     glBindTexture(GL_TEXTURE_2D, stored->id); 
-   
-     // reset active texture back to default 
-     glActiveTexture(GL_TEXTURE0); 
- } 
-   
- extern br_v1db_state v1db; 
-   
- void GLRenderer_BeginScene(br_actor* camera, br_pixelmap* colour_buffer, br_pixelmap* depth_buffer) { 
-     last_colour_buffer = colour_buffer; 
-     last_depth_buffer = depth_buffer; 
-     glViewport(colour_buffer->base_x, render_height - colour_buffer->height - colour_buffer->base_y, colour_buffer->width, colour_buffer->height); 
-     glUseProgram(shader_program_3d); 
-     glUniform1ui(uniforms_3d.viewport_height, render_height); 
-   
-     br_camera* cam = camera->type_data; 
-     current_material = NULL; 
-     current_shade_table = NULL; 
-   
-     // clip planes 
-     int enabled_clip_planes = 0; 
-     for (int i = 0; i < v1db.enabled_clip_planes.max; i++) { 
-         if (v1db.enabled_clip_planes.enabled == NULL || !v1db.enabled_clip_planes.enabled[i]) { 
-             continue; 
-         } 
-         br_vector4* v4 = v1db.enabled_clip_planes.enabled[i]->type_data; 
-         glUniform4f(uniforms_3d.clip_planes[enabled_clip_planes], v4->v[0], v4->v[1], v4->v[2], v4->v[3]); 
-         enabled_clip_planes++; 
-     } 
-     glUniform1i(uniforms_3d.clip_plane_count, enabled_clip_planes); 
-   
-     // view matrix 
-     BrMatrix4Copy34(&view_matrix, &v1db.camera_path[0].m); 
-     BrMatrix4Inverse(&view_matrix, &view_matrix); 
-     glUniformMatrix4fv(uniforms_3d.view, 1, GL_FALSE, &view_matrix.m[0][0]); 
-   
-     // projection matrix 
-     br_matrix4 projection; 
-     BrMatrix4Perspective(&projection, cam->field_of_view, cam->aspect, cam->hither_z, cam->yon_z); 
-     // hack: not sure why we have to do this, but this makes the result the same as `glm_perspective` 
-     projection.m[2][2] *= -1; 
-     glUniformMatrix4fv(uniforms_3d.projection, 1, GL_FALSE, &projection.m[0][0]); 
-   
-     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); 
- } 
-   
- void GLRenderer_EndScene(void) { 
-     //  switch back to default fb and reset state 
-     glBindFramebuffer(GL_FRAMEBUFFER, 0); 
-     glDepthMask(GL_TRUE); 
-     glDepthMask(GL_TRUE); 
-     CHECK_GL_ERROR("GLRenderer_EndScene"); 
- } 
-   
- void GLRenderer_FullScreenQuad(uint8_t* screen_buffer) { 
-   
-     glViewport(vp_x, vp_y, vp_width, vp_height); 
-     glBindFramebuffer(GL_FRAMEBUFFER, 0); 
-     glDisable(GL_DEPTH_TEST); 
-     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 
-   
-     glBindTexture(GL_TEXTURE_2D, fullscreen_quad_texture); 
-     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, screen_buffer); 
-   
-     glBindVertexArray(screen_buffer_vao); 
-     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screen_buffer_ebo); 
-     glUseProgram(shader_program_2d); 
-     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 
-     glBindVertexArray(0); 
-   
-     glEnable(GL_DEPTH_TEST); 
-     CHECK_GL_ERROR("GLRenderer_RenderFullScreenQuad"); 
- } 
-   
- void GLRenderer_ClearBuffers(void) { 
-     // clear our virtual framebuffer 
-     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); 
-     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
-   
-     // clear real framebuffer 
-     glBindFramebuffer(GL_FRAMEBUFFER, 0); 
-     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
-   
-     CHECK_GL_ERROR("GLRenderer_ClearBuffers"); 
- } 
-   
- void GLRenderer_BufferModel(br_model* model) { 
-     tStored_model_context* ctx; 
-     v11model* v11; 
-   
-     if (model->stored != NULL) { 
-         LOG_PANIC("trying to build a stored model"); 
-     } 
-   
-     v11 = model->prepared; 
-     ctx = NewStoredModelContext(); 
-   
-     int total_verts = 0, total_faces = 0; 
-     for (int i = 0; i < v11->ngroups; i++) { 
-         total_verts += v11->groups[i].nvertices; 
-         total_faces += v11->groups[i].nfaces; 
-     } 
-   
-     // override normals 
-     br_vector3 v3 = { { 0, 0, 0 } }; 
-     for (int g = 0; g < v11->ngroups; g++) { 
-         for (int i = 0; i < v11->groups[g].nvertices; i++) { 
-             v11->groups[g].vertices[i].n = v3; 
-         } 
-     } 
-     br_vector3 v0v1, v2v1, normal; 
-     for (int g = 0; g < v11->ngroups; g++) { 
-         v11group* group = &v11->groups[g]; 
-         for (int i = 0; i < group->nfaces; i++) { 
-             v11face* f = &group->faces[i]; 
-             fmt_vertex* v0 = &group->vertices[f->vertices[0]]; 
-             fmt_vertex* v1 = &group->vertices[f->vertices[1]]; 
-             fmt_vertex* v2 = &group->vertices[f->vertices[2]]; 
-             BrVector3Sub(&v0v1, &v0->p, &v1->p); 
-             BrVector3Sub(&v2v1, &v2->p, &v1->p); 
-             BrVector3Cross(&normal, &v0v1, &v2v1); 
-             BrVector3Accumulate(&v0->n, &normal); 
-             BrVector3Accumulate(&v1->n, &normal); 
-             BrVector3Accumulate(&v2->n, &normal); 
-         } 
-     } 
-     for (int g = 0; g < v11->ngroups; g++) { 
-         for (int i = 0; i < v11->groups[g].nvertices; i++) { 
-             BrVector3Normalise(&v11->groups[g].vertices[i].n, &v11->groups[g].vertices[i].n); 
-         } 
-     } 
-   
-     gl_vertex *-  verts  = malloc(sizeof(- gl_vertex ) *-  total_verts );
-     unsigned int*-  indices  = malloc(sizeof(int) * 3 *-  total_faces );
 
-   
-     int v_index = 0; 
-     int i_index = 0; 
-     int face_offset = 0; 
-     for (int g = 0; g < v11->ngroups; g++) { 
-         for (int i = 0; i < v11->groups[g].nvertices; i++) { 
-             fmt_vertex* v = &v11->groups[g].vertices[i]; 
-             verts[v_index].p = v->p; 
-             verts[v_index].n = v->n; 
-             verts[v_index].map = v->map; 
-             verts[v_index].colour_index = BR_ALPHA(v11->groups[g].vertex_colours[i]); 
-             v_index++; 
-         } 
-         for (int i = 0; i < v11->groups[g].nfaces; i++) { 
-             v11face* f = &v11->groups[g].faces[i]; 
-             indices[i_index++] = f->vertices[0] + face_offset; 
-             indices[i_index++] = f->vertices[1] + face_offset; 
-             indices[i_index++] = f->vertices[2] + face_offset; 
-         } 
-         face_offset += v11->groups[g].nvertices; 
-     } 
-   
-     glGenVertexArrays(1, &ctx->vao_id); 
-     glGenBuffers(1, &ctx->vbo_id); 
-     glGenBuffers(1, &ctx->ebo_id); 
-   
-     // Vertices 
-     glBindVertexArray(ctx->vao_id); 
-     glBindBuffer(GL_ARRAY_BUFFER, ctx->vbo_id); 
-     glBufferData(GL_ARRAY_BUFFER, sizeof(gl_vertex) * total_verts, verts, GL_STATIC_DRAW); 
-     // pos 
-     glVertexAttribPointer (0, 3,-  GL_FLOAT ,-  GL_FALSE , sizeof(- gl_vertex ), (void*)offsetof(- gl_vertex ,-  p ));
-     glEnableVertexAttribArray(0); 
-     // normal 
-     glVertexAttribPointer (1, 3,-  GL_FLOAT ,-  GL_FALSE , sizeof(- gl_vertex ), (void*)offsetof(- gl_vertex ,-  n ));
-     glEnableVertexAttribArray(1); 
-     // uv coordinates 
-     glVertexAttribPointer (2, 2,-  GL_FLOAT ,-  GL_FALSE , sizeof(- gl_vertex ), (void*)offsetof(- gl_vertex ,-  map ));
-     glEnableVertexAttribArray(2); 
-     // color 
-     glVertexAttribPointer (3, 1,-  GL_FLOAT ,-  GL_FALSE , sizeof(- gl_vertex ), (void*)offsetof(- gl_vertex ,-  colour_index ));
-     glEnableVertexAttribArray(3); 
-   
-     // Indices 
-     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id); 
-     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * 3 * total_faces, indices, GL_STATIC_DRAW); 
-     glBindVertexArray(0); 
-   
-   
-     model->stored = ctx; 
-   
-     CHECK_GL_ERROR("after build model"); 
- } 
-   
- void setActiveMaterial(tStored_material* material) { 
-     if (material == NULL || material == current_material) { 
-         return; 
-     } 
-   
-     glUniformMatrix2x3fv(uniforms_3d.material_uv_transform, 1, GL_TRUE, &material->map_transform.m[0][0]); 
-   
-     if (material->pixelmap) { 
-         tStored_pixelmap* stored_px = material->pixelmap->stored; 
-         if (stored_px == NULL) { 
-             LOG_PANIC("stored_px is null for pixelmap %s", material->pixelmap->identifier); 
-         } 
-         glBindTexture(GL_TEXTURE_2D, stored_px->id); 
-         glUniform1ui(uniforms_3d.material_texture_enabled, 1); 
-     } else { 
-         glUniform1ui(uniforms_3d.material_texture_enabled, 0); 
-   
-         // index_base and index_range are only used for untextured materials 
-         glUniform1ui(uniforms_3d.material_index_base, material->index_base); 
-         // glUniform1ui(uniforms_3d.material_index_range, material->index_range);  TODO: re-add when we support untextured lighting 
-     } 
-   
-     if (material->shade_table) { 
-         glUniform1ui(uniforms_3d.material_shade_table_height, material->shade_table->height); 
-         GLRenderer_SetShadeTable(material->shade_table); 
-     } 
-   
-     if (material->index_blend) { 
-         glUniform1ui(uniforms_3d.material_blend_enabled, 1); 
-         GLRenderer_SetBlendTable(material->index_blend); 
-         // materials with index_blend do not write to depth buffer (https://www.cwaboard.co.uk/viewtopic.php?p=105846&sid=58ad8910238000ca14b01dad85117175#p105846) 
-         glDepthMask(GL_FALSE); 
-     } else { 
-         glUniform1ui(uniforms_3d.material_blend_enabled, 0); 
-         glDepthMask(GL_TRUE); 
-     } 
-   
-     glUniform1ui(uniforms_3d.material_flags, material->flags); 
-   
-     if (material->flags & (BR_MATF_TWO_SIDED | BR_MATF_ALWAYS_VISIBLE)) { 
-         glDisable(GL_CULL_FACE); 
-     } else { 
-         glEnable(GL_CULL_FACE); 
-     } 
-   
-     CHECK_GL_ERROR("GLRenderer_RenderModelxx"); 
- } 
-   
- void GLRenderer_Model(br_actor* actor, br_model* model, br_material* material, br_token render_type, br_matrix34 model_matrix) { 
-     tStored_model_context* ctx; 
-     v11group* group; 
-     int element_index = 0; 
-   
-     if (model->flags & BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE) { 
-         if (model->stored) { 
-             ((br_object*)model->stored)->dispatch->_free((br_object*)model->stored); 
-             model->stored = NULL; 
-         } 
-         GLRenderer_BufferModel(model); 
-         model->flags &= ~BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE; 
-     } 
-   
-     ctx = model->stored; 
-     v11model* v11 = model->prepared; 
-   
-     if (v11 == NULL) { 
-         // LOG_WARN("No model prepared for %s", model->identifier); 
-         return; 
-     } 
-   
-     // model matrix 
-     br_matrix4 view_model_matrix, model_matrix4, inverse, inverse_transpose; 
-     BrMatrix4Copy34(&model_matrix4, &model_matrix); 
-     glUniformMatrix4fv(uniforms_3d.model, 1, GL_FALSE, model_matrix4.m[0]); 
-   
-     // normal matrix (http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/) 
-     BrMatrix4Mul(&view_model_matrix, &view_matrix, &model_matrix4); 
-     BrMatrix4Inverse(&inverse, &view_model_matrix); 
-     // transpose 
-     for (int i = 0; i < 4; i++) { 
-         for (int j = 0; j < 4; j++) { 
-             inverse_transpose.m[i][j] = inverse.m[j][i]; 
-         } 
-     } 
-     glUniformMatrix4fv(uniforms_3d.normal_matrix, 1, GL_FALSE, &inverse_transpose.m[0][0]); 
-   
-     glBindVertexArray(ctx->vao_id); 
-     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id); 
-   
-     // set default material for this actor/model 
-     setActiveMaterial(material->stored); 
-   
-     switch (render_type) { 
-     case BRT_TRIANGLE: 
-         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 
-         break; 
-     case BRT_LINE: 
-         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 
-         glUniform1ui(uniforms_3d.material_index_base, 255); 
-         glUniform1ui(uniforms_3d.material_flags, 0); 
-         break; 
-     default: 
-         LOG_PANIC("render_type %d is not supported?!", render_type); 
-     } 
-   
-     for (int g = 0; g < v11->ngroups; g++) { 
-         group = &v11->groups[g]; 
-         setActiveMaterial(group->stored); 
-         glDrawElements(GL_TRIANGLES, group->nfaces * 3, GL_UNSIGNED_INT, (void*)(element_index * sizeof(int))); 
-         element_index += group->nfaces * 3; 
-     } 
-   
-     glBindVertexArray(0); 
-     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 
-     dirty_buffers = 1; 
-   
-     CHECK_GL_ERROR("GLRenderer_RenderModel"); 
- } 
-   
- void GLRenderer_BufferMaterial(br_material* mat) { 
-     tStored_material* stored = mat->stored; 
-     if (!stored) { 
-         stored = NewStoredMaterial(); 
-         mat->stored = stored; 
-         if (mat->identifier) { 
-             strcpy(- stored ->- identifier ,-  mat ->- identifier );
 
-         } 
-     } 
-     BrMatrix23Copy(&stored->map_transform, &mat->map_transform); 
-     stored->pixelmap = mat->colour_map; 
-     stored->flags = mat->flags; 
-     stored->shade_table = mat->index_shade; 
-     stored->index_base = mat->index_base; 
-     stored->index_range = mat->index_range; 
-     stored->index_blend = mat->index_blend; 
- } 
-   
- void GLRenderer_BufferTexture(br_pixelmap* pm) { 
-     tStored_pixelmap* stored = pm->stored; 
-     if (pm->stored) { 
-     } else { 
-         stored = NewStoredPixelmap(); 
-         glGenTextures(1, &stored->id); 
-         pm->stored = stored; 
-     } 
-   
-     // sometimes the pixelmap has row_bytes > width. OpenGL expects linear pixels, so flatten it out 
-     uint8_t*-  linear_pixels  = malloc(sizeof(uint8_t) *-  pm ->- width  *-  pm ->- height );
 
-     uint8_t* original_pixels = pm->pixels; 
-     for (int y = 0; y < pm->height; y++) { 
-         for (int x = 0; x < pm->width; x++) { 
-             linear_pixels[y * pm->width + x] = original_pixels[y * pm->row_bytes + x]; 
-         } 
-     } 
-   
-     glBindTexture(GL_TEXTURE_2D, stored->id); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
-     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
-     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, pm->width, pm->height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, linear_pixels); 
-   
-     CHECK_GL_ERROR("GLRenderer_BufferTexture"); 
- } 
-   
- void GLRenderer_FlushBuffer(tRenderer_flush_type flush_type) { 
-   
-     if (!dirty_buffers) { 
-         return; 
-     } 
-   
-     // pull framebuffer into cpu memory to emulate BRender behavior 
-     glBindTexture(GL_TEXTURE_2D, framebuffer_texture); 
-     glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, screen_buffer_flip_pixels); 
-   
-     // flip texture to match the expected orientation 
-     int dest_y = render_height; 
-     uint8_t* pm_pixels = last_colour_buffer->pixels; 
-     uint8_t new_pixel; 
-     for (int y = 0; y < render_height; y++) { 
-         dest_y--; 
-         for (int x = 0; x < render_width; x++) { 
-             new_pixel = screen_buffer_flip_pixels[y * render_width + x]; 
-             if (new_pixel != 0) { 
-                 pm_pixels[dest_y * render_width + x] = new_pixel; 
-             } 
-         } 
-     } 
-   
-     if (flush_type == eFlush_all) { 
-   
-         // pull depthbuffer into cpu memory to emulate BRender behavior 
-         glBindTexture(GL_TEXTURE_2D, depth_texture); 
-         glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, depth_buffer_flip_pixels); 
-   
-         dest_y = last_colour_buffer->height; 
-         int src_y = render_height - last_colour_buffer->base_y - last_colour_buffer->height; 
-         uint16_t* depth_pixels = last_depth_buffer->pixels; 
-         for (int y = 0; y < last_colour_buffer->height; y++) { 
-             dest_y--; 
-             for (int x = 0; x < last_colour_buffer->width; x++) { 
-                 uint16_t new_depth = depth_buffer_flip_pixels[src_y * render_width + last_colour_buffer->base_x + x]; 
-                 depth_pixels[dest_y * render_width + x] = new_depth; 
-             } 
-             src_y++; 
-         } 
-     } 
-     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id); 
-     glClear(GL_COLOR_BUFFER_BIT); 
-     flush_counter++; 
-     dirty_buffers = 0; 
- } 
-   
- void GLRenderer_FlushBuffers(void) { 
-     GLRenderer_FlushBuffer(eFlush_all); 
- } 
-   
- void GLRenderer_SetViewport(int x, int y, int width, int height) { 
-     vp_x = x; 
-     vp_y = y; 
-     vp_width = width; 
-     vp_height = height; 
- } 
-