Subversion Repositories Games.Carmageddon

Rev

Rev 1 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include "gl_renderer.h"
  2. #include "brender/brender.h"
  3. #include "harness.h"
  4. #include "harness/trace.h"
  5. #include "resources/3d_frag.glsl.h"
  6. #include "resources/3d_vert.glsl.h"
  7. #include "resources/framebuffer_frag.glsl.h"
  8. #include "resources/framebuffer_vert.glsl.h"
  9. #include "stored_context.h"
  10.  
  11. #include <glad/glad.h>
  12. #include <stdio.h>
  13. #include <string.h>
  14.  
  15. static GLuint screen_buffer_vao, screen_buffer_ebo;
  16. static GLuint fullscreen_quad_texture, palette_texture, depth_texture;
  17.  
  18. static GLuint shader_program_2d;
  19. static GLuint shader_program_3d;
  20. static GLuint framebuffer_id, framebuffer_texture = 0;
  21.  
  22. // holds the latest uploaded version of the colour_buffer. Available in shader for blending
  23. static GLuint current_colourbuffer_texture;
  24.  
  25. static uint8_t gl_palette[4 * 256]; // RGBA
  26. static uint8_t* screen_buffer_flip_pixels;
  27. static uint16_t* depth_buffer_flip_pixels;
  28.  
  29. static int render_width, render_height;
  30. static int vp_x, vp_y, vp_width, vp_height;
  31.  
  32. static br_pixelmap *last_colour_buffer, *last_depth_buffer;
  33. static int dirty_buffers = 0;
  34.  
  35. static tStored_material* current_material;
  36. static br_pixelmap* current_shade_table;
  37.  
  38. static br_matrix4 view_matrix;
  39.  
  40. // Increment flush_counter during flush, and upload_counter when we upload the colour_buffer texture.
  41. // If the counters are equal, we can avoid re-uploading the same thing.
  42. static unsigned int flush_counter = 0, colourbuffer_upload_counter = 0;
  43.  
  44. typedef struct gl_vertex {
  45.     br_vector3 p;
  46.     br_vector2 map;
  47.     br_vector3 n;
  48.     float colour_index; // float to allow interpolation
  49. } gl_vertex;
  50.  
  51. struct {
  52.     GLuint model, view, projection;
  53.     // gl_NormalMatrix replacement
  54.     GLuint normal_matrix;
  55.     GLuint clip_plane_count;
  56.     GLuint clip_planes[6];
  57.     GLuint colour_buffer_texture;
  58.     GLuint viewport_height;
  59.  
  60.     GLuint material_flags;
  61.     GLuint material_texture_enabled;
  62.     GLuint material_texture_pixelmap;
  63.     GLuint material_uv_transform;
  64.     GLuint material_shade_table;
  65.     GLuint material_blend_enabled;
  66.     GLuint material_blend_table;
  67.     GLuint material_index_base;
  68.     // GLuint material_index_range;  TODO: re-add when we add untextured lighting
  69.     GLuint material_shade_table_height;
  70.  
  71. } uniforms_3d;
  72.  
  73. struct {
  74.     GLuint pixels, palette;
  75. } uniforms_2d;
  76.  
  77. static GLuint CreateShaderProgram(char* name, const char* vertex_shader, const int vertex_shader_len, const char* fragment_shader, const int fragment_shader_len) {
  78.     int success;
  79.     char log_buffer[1024];
  80.     GLuint program;
  81.     GLuint v_shader, f_shader;
  82.  
  83.     program = glCreateProgram();
  84.     v_shader = glCreateShader(GL_VERTEX_SHADER);
  85.     const GLchar* vertex_sources[] = { vertex_shader };
  86.     glShaderSource(v_shader, 1, vertex_sources, &vertex_shader_len);
  87.     glCompileShader(v_shader);
  88.     glGetShaderiv(v_shader, GL_COMPILE_STATUS, &success);
  89.     if (!success) {
  90.         glGetShaderInfoLog(v_shader, 1024, NULL, log_buffer);
  91.         LOG_PANIC("shader %s failed to compile: %s", name, log_buffer);
  92.     }
  93.  
  94.     f_shader = glCreateShader(GL_FRAGMENT_SHADER);
  95.     const GLchar* fragment_sources[] = { fragment_shader };
  96.     glShaderSource(f_shader, 1, fragment_sources, &fragment_shader_len);
  97.     glCompileShader(f_shader);
  98.     glGetShaderiv(f_shader, GL_COMPILE_STATUS, &success);
  99.     if (!success) {
  100.         char log_buffer[1024];
  101.         glGetShaderInfoLog(f_shader, 1024, NULL, log_buffer);
  102.         LOG_PANIC("shader %s failed to compile: %s", name, log_buffer);
  103.     }
  104.  
  105.     glAttachShader(program, v_shader);
  106.     glAttachShader(program, f_shader);
  107.     glLinkProgram(program);
  108.     glDeleteShader(v_shader);
  109.     glDeleteShader(f_shader);
  110.  
  111.     GLint link_ok = GL_FALSE;
  112.     glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
  113.     if (!link_ok) {
  114.         glGetShaderInfoLog(program, 1024, NULL, log_buffer);
  115.         LOG_PANIC("shader program %s failed to link: %s", name, log_buffer);
  116.     }
  117.     return program;
  118. }
  119.  
  120. static GLint GetValidatedUniformLocation(GLuint program, char* uniform_name) {
  121.     GLint location;
  122.     location = glGetUniformLocation(program, uniform_name);
  123.     if (location == -1) {
  124.         LOG_PANIC("glGetUniformLocation(%d, %s) failed. Check the shader uniform names.", program, uniform_name);
  125.     }
  126.     return location;
  127. }
  128.  
  129. static void LoadShaders(void) {
  130.     shader_program_2d = CreateShaderProgram("framebuffer", RESOURCES_FRAMEBUFFER_VERT_GLSL, sizeof(RESOURCES_FRAMEBUFFER_VERT_GLSL), RESOURCES_FRAMEBUFFER_FRAG_GLSL, sizeof(RESOURCES_FRAMEBUFFER_FRAG_GLSL));
  131.     glUseProgram(shader_program_2d);
  132.     uniforms_2d.pixels = GetValidatedUniformLocation(shader_program_2d, "u_pixels");
  133.     uniforms_2d.palette = GetValidatedUniformLocation(shader_program_2d, "u_palette");
  134.  
  135.     // bind the uniform samplers to texture units:
  136.     glUniform1i(uniforms_2d.pixels, 0);
  137.     glUniform1i(uniforms_2d.palette, 1);
  138.  
  139.     shader_program_3d = CreateShaderProgram("3d", RESOURCES_3D_VERT_GLSL, sizeof(RESOURCES_3D_VERT_GLSL), RESOURCES_3D_FRAG_GLSL, sizeof(RESOURCES_3D_FRAG_GLSL));
  140.     glUseProgram(shader_program_3d);
  141.     uniforms_3d.clip_plane_count = GetValidatedUniformLocation(shader_program_3d, "u_clip_plane_count");
  142.     for (int i = 0; i < 6; i++) {
  143.         char name[32];
  144.         sprintf(name, "u_clip_planes[%d]", i);
  145.         uniforms_3d.clip_planes[i] = GetValidatedUniformLocation(shader_program_3d, name);
  146.     }
  147.  
  148.     uniforms_3d.model = GetValidatedUniformLocation(shader_program_3d, "u_model");
  149.     uniforms_3d.colour_buffer_texture = GetValidatedUniformLocation(shader_program_3d, "u_colour_buffer");
  150.     uniforms_3d.projection = GetValidatedUniformLocation(shader_program_3d, "u_projection");
  151.     uniforms_3d.view = GetValidatedUniformLocation(shader_program_3d, "u_view");
  152.     uniforms_3d.viewport_height = GetValidatedUniformLocation(shader_program_3d, "u_viewport_height");
  153.     uniforms_3d.normal_matrix = GetValidatedUniformLocation(shader_program_3d, "u_normal_matrix");
  154.  
  155.     uniforms_3d.material_flags = GetValidatedUniformLocation(shader_program_3d, "u_material_flags");
  156.     uniforms_3d.material_texture_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_enabled");
  157.     uniforms_3d.material_texture_pixelmap = GetValidatedUniformLocation(shader_program_3d, "u_material_texture_pixelmap");
  158.     uniforms_3d.material_uv_transform = GetValidatedUniformLocation(shader_program_3d, "u_material_uv_transform");
  159.     uniforms_3d.material_shade_table = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table");
  160.     uniforms_3d.material_shade_table_height = GetValidatedUniformLocation(shader_program_3d, "u_material_shade_table_height");
  161.     uniforms_3d.material_blend_enabled = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_enabled");
  162.     uniforms_3d.material_blend_table = GetValidatedUniformLocation(shader_program_3d, "u_material_blend_table");
  163.     uniforms_3d.material_index_base = GetValidatedUniformLocation(shader_program_3d, "u_material_index_base");
  164.     // TODO: re-add when we support untextured lighting
  165.     // uniforms_3d.material_index_range = GetValidatedUniformLocation(shader_program_3d, "u_material_index_range");
  166.  
  167.     // bind the uniform samplers to texture units
  168.     glUniform1i(uniforms_3d.material_texture_pixelmap, 0);
  169.     // palette occupies texture unit 1 but not required during 3d rendering
  170.     glUniform1i(uniforms_3d.material_shade_table, 2);
  171.     glUniform1i(uniforms_3d.material_blend_table, 3);
  172.     glUniform1i(uniforms_3d.colour_buffer_texture, 4);
  173. }
  174.  
  175. static void SetupFullScreenRectGeometry(void) {
  176.     float vertices[] = {
  177.         // positions          // colors           // texture coords
  178.         1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right
  179.         1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,  // bottom right
  180.         -1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
  181.         -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
  182.     };
  183.     unsigned int indices[] = {
  184.         0, 1, 3, // first triangle
  185.         1, 2, 3  // second triangle
  186.     };
  187.  
  188.     GLuint vbo;
  189.     glGenVertexArrays(1, &screen_buffer_vao);
  190.     glGenBuffers(1, &vbo);
  191.     glGenBuffers(1, &screen_buffer_ebo);
  192.  
  193.     glBindVertexArray(screen_buffer_vao);
  194.  
  195.     glBindBuffer(GL_ARRAY_BUFFER, vbo);
  196.     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
  197.  
  198.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screen_buffer_ebo);
  199.     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
  200.  
  201.     // position attribute
  202.     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
  203.     glEnableVertexAttribArray(0);
  204.     // color attribute
  205.     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
  206.     glEnableVertexAttribArray(1);
  207.     // texture coord attribute
  208.     glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
  209.     glEnableVertexAttribArray(2);
  210.  
  211.     glBindVertexArray(0);
  212. }
  213.  
  214. void GLRenderer_Init(int pRender_width, int pRender_height) {
  215.     render_width = pRender_width;
  216.     render_height = pRender_height;
  217.  
  218.     LOG_INFO("OpenGL vendor string: %s", glGetString(GL_VENDOR));
  219.     LOG_INFO("OpenGL renderer string: %s", glGetString(GL_RENDERER));
  220.     LOG_INFO("OpenGL version string: %s", glGetString(GL_VERSION));
  221.     LOG_INFO("OpenGL shading language version string: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
  222.  
  223.     if (glGetString(GL_SHADING_LANGUAGE_VERSION) == NULL) {
  224.         LOG_PANIC("GL_SHADING_LANGUAGE_VERSION is null");
  225.     }
  226.  
  227.     LoadShaders();
  228.     SetupFullScreenRectGeometry();
  229.  
  230.     // config
  231.     glDisable(GL_BLEND);
  232.     glDepthFunc(GL_LESS);
  233.     glClearColor(0, 0, 0, 1.0f);
  234.     glClear(GL_COLOR_BUFFER_BIT);
  235.     glEnable(GL_CULL_FACE);
  236.     glCullFace(GL_BACK);
  237.  
  238.     // textures
  239.     glGenTextures(1, &fullscreen_quad_texture);
  240.     glGenTextures(1, &palette_texture);
  241.     glGenTextures(1, &framebuffer_texture);
  242.     glGenTextures(1, &depth_texture);
  243.     glGenTextures(1, &current_colourbuffer_texture);
  244.  
  245.     // setup framebuffer
  246.     glGenFramebuffers(1, &framebuffer_id);
  247.     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
  248.  
  249.     // set pixel storage alignment to 1 byte
  250.     glPixelStorei(GL_PACK_ALIGNMENT, 1);
  251.     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  252.  
  253.     glBindTexture(GL_TEXTURE_2D, fullscreen_quad_texture);
  254.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  255.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  256.  
  257.     glBindTexture(GL_TEXTURE_2D, palette_texture);
  258.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  259.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  260.  
  261.     glBindTexture(GL_TEXTURE_2D, framebuffer_texture);
  262.     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);
  263.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  264.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  265.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, framebuffer_texture, 0);
  266.  
  267.     glBindTexture(GL_TEXTURE_2D, current_colourbuffer_texture);
  268.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  269.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  270.  
  271.     glBindTexture(GL_TEXTURE_2D, depth_texture);
  272.     glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, render_width, render_height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, 0);
  273.     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0);
  274.  
  275.     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
  276.         LOG_PANIC("Framebuffer is not complete!");
  277.     }
  278.     glBindFramebuffer(GL_FRAMEBUFFER, 0);
  279.  
  280.     screen_buffer_flip_pixels = malloc(sizeof(uint8_t) * render_width * render_height);
  281.     depth_buffer_flip_pixels = malloc(sizeof(uint16_t) * render_width * render_height);
  282.  
  283.     CHECK_GL_ERROR("initializeOpenGLContext");
  284. }
  285.  
  286. void GLRenderer_SetPalette(uint8_t* rgba_colors) {
  287.     for (int i = 0; i < 256; i++) {
  288.         gl_palette[i * 4] = rgba_colors[i * 4 + 2];
  289.         gl_palette[i * 4 + 1] = rgba_colors[i * 4 + 1];
  290.         gl_palette[i * 4 + 2] = rgba_colors[i * 4];
  291.         // palette index 0 is transparent, so set alpha to 0
  292.         if (i == 0) {
  293.             gl_palette[i * 4 + 3] = 0;
  294.         } else {
  295.             gl_palette[i * 4 + 3] = 0xff;
  296.         }
  297.     }
  298.  
  299.     glActiveTexture(GL_TEXTURE1);
  300.     glBindTexture(GL_TEXTURE_2D, palette_texture);
  301.     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, gl_palette);
  302.  
  303.     // reset active texture back to default
  304.     glActiveTexture(GL_TEXTURE0);
  305.  
  306.     CHECK_GL_ERROR("GLRenderer_SetPalette");
  307. }
  308.  
  309. void GLRenderer_SetShadeTable(br_pixelmap* table) {
  310.     if (current_shade_table == table) {
  311.         return;
  312.     }
  313.  
  314.     // shade table uses texture unit 2
  315.     glActiveTexture(GL_TEXTURE2);
  316.     tStored_pixelmap* stored = table->stored;
  317.     glBindTexture(GL_TEXTURE_2D, stored->id);
  318.  
  319.     // reset active texture back to default
  320.     glActiveTexture(GL_TEXTURE0);
  321.     current_shade_table = table;
  322. }
  323.  
  324. void GLRenderer_SetBlendTable(br_pixelmap* table) {
  325.  
  326.     if (flush_counter != colourbuffer_upload_counter) {
  327.         GLRenderer_FlushBuffer(eFlush_color_buffer);
  328.         glActiveTexture(GL_TEXTURE4);
  329.         glBindTexture(GL_TEXTURE_2D, current_colourbuffer_texture);
  330.         glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, last_colour_buffer->pixels);
  331.         colourbuffer_upload_counter = flush_counter;
  332.     }
  333.  
  334.     // blend table uses texture unit 3
  335.     glActiveTexture(GL_TEXTURE3);
  336.     tStored_pixelmap* stored = table->stored;
  337.     glBindTexture(GL_TEXTURE_2D, stored->id);
  338.  
  339.     // reset active texture back to default
  340.     glActiveTexture(GL_TEXTURE0);
  341. }
  342.  
  343. extern br_v1db_state v1db;
  344.  
  345. void GLRenderer_BeginScene(br_actor* camera, br_pixelmap* colour_buffer, br_pixelmap* depth_buffer) {
  346.     last_colour_buffer = colour_buffer;
  347.     last_depth_buffer = depth_buffer;
  348.     glViewport(colour_buffer->base_x, render_height - colour_buffer->height - colour_buffer->base_y, colour_buffer->width, colour_buffer->height);
  349.     glUseProgram(shader_program_3d);
  350.     glUniform1ui(uniforms_3d.viewport_height, render_height);
  351.  
  352.     br_camera* cam = camera->type_data;
  353.     current_material = NULL;
  354.     current_shade_table = NULL;
  355.  
  356.     // clip planes
  357.     int enabled_clip_planes = 0;
  358.     for (int i = 0; i < v1db.enabled_clip_planes.max; i++) {
  359.         if (v1db.enabled_clip_planes.enabled == NULL || !v1db.enabled_clip_planes.enabled[i]) {
  360.             continue;
  361.         }
  362.         br_vector4* v4 = v1db.enabled_clip_planes.enabled[i]->type_data;
  363.         glUniform4f(uniforms_3d.clip_planes[enabled_clip_planes], v4->v[0], v4->v[1], v4->v[2], v4->v[3]);
  364.         enabled_clip_planes++;
  365.     }
  366.     glUniform1i(uniforms_3d.clip_plane_count, enabled_clip_planes);
  367.  
  368.     // view matrix
  369.     BrMatrix4Copy34(&view_matrix, &v1db.camera_path[0].m);
  370.     BrMatrix4Inverse(&view_matrix, &view_matrix);
  371.     glUniformMatrix4fv(uniforms_3d.view, 1, GL_FALSE, &view_matrix.m[0][0]);
  372.  
  373.     // projection matrix
  374.     br_matrix4 projection;
  375.     BrMatrix4Perspective(&projection, cam->field_of_view, cam->aspect, cam->hither_z, cam->yon_z);
  376.     // hack: not sure why we have to do this, but this makes the result the same as `glm_perspective`
  377.     projection.m[2][2] *= -1;
  378.     glUniformMatrix4fv(uniforms_3d.projection, 1, GL_FALSE, &projection.m[0][0]);
  379.  
  380.     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
  381. }
  382.  
  383. void GLRenderer_EndScene(void) {
  384.     //  switch back to default fb and reset state
  385.     glBindFramebuffer(GL_FRAMEBUFFER, 0);
  386.     glDepthMask(GL_TRUE);
  387.     glDepthMask(GL_TRUE);
  388.     CHECK_GL_ERROR("GLRenderer_EndScene");
  389. }
  390.  
  391. void GLRenderer_FullScreenQuad(uint8_t* screen_buffer) {
  392.  
  393.     glViewport(vp_x, vp_y, vp_width, vp_height);
  394.     glBindFramebuffer(GL_FRAMEBUFFER, 0);
  395.     glDisable(GL_DEPTH_TEST);
  396.     glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  397.  
  398.     glBindTexture(GL_TEXTURE_2D, fullscreen_quad_texture);
  399.     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, render_width, render_height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, screen_buffer);
  400.  
  401.     glBindVertexArray(screen_buffer_vao);
  402.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screen_buffer_ebo);
  403.     glUseProgram(shader_program_2d);
  404.     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
  405.     glBindVertexArray(0);
  406.  
  407.     glEnable(GL_DEPTH_TEST);
  408.     CHECK_GL_ERROR("GLRenderer_RenderFullScreenQuad");
  409. }
  410.  
  411. void GLRenderer_ClearBuffers(void) {
  412.     // clear our virtual framebuffer
  413.     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
  414.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  415.  
  416.     // clear real framebuffer
  417.     glBindFramebuffer(GL_FRAMEBUFFER, 0);
  418.     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  419.  
  420.     CHECK_GL_ERROR("GLRenderer_ClearBuffers");
  421. }
  422.  
  423. void GLRenderer_BufferModel(br_model* model) {
  424.     tStored_model_context* ctx;
  425.     v11model* v11;
  426.  
  427.     if (model->stored != NULL) {
  428.         LOG_PANIC("trying to build a stored model");
  429.     }
  430.  
  431.     v11 = model->prepared;
  432.     ctx = NewStoredModelContext();
  433.  
  434.     int total_verts = 0, total_faces = 0;
  435.     for (int i = 0; i < v11->ngroups; i++) {
  436.         total_verts += v11->groups[i].nvertices;
  437.         total_faces += v11->groups[i].nfaces;
  438.     }
  439.  
  440.     // override normals
  441.     br_vector3 v3 = { { 0, 0, 0 } };
  442.     for (int g = 0; g < v11->ngroups; g++) {
  443.         for (int i = 0; i < v11->groups[g].nvertices; i++) {
  444.             v11->groups[g].vertices[i].n = v3;
  445.         }
  446.     }
  447.     br_vector3 v0v1, v2v1, normal;
  448.     for (int g = 0; g < v11->ngroups; g++) {
  449.         v11group* group = &v11->groups[g];
  450.         for (int i = 0; i < group->nfaces; i++) {
  451.             v11face* f = &group->faces[i];
  452.             fmt_vertex* v0 = &group->vertices[f->vertices[0]];
  453.             fmt_vertex* v1 = &group->vertices[f->vertices[1]];
  454.             fmt_vertex* v2 = &group->vertices[f->vertices[2]];
  455.             BrVector3Sub(&v0v1, &v0->p, &v1->p);
  456.             BrVector3Sub(&v2v1, &v2->p, &v1->p);
  457.             BrVector3Cross(&normal, &v0v1, &v2v1);
  458.             BrVector3Accumulate(&v0->n, &normal);
  459.             BrVector3Accumulate(&v1->n, &normal);
  460.             BrVector3Accumulate(&v2->n, &normal);
  461.         }
  462.     }
  463.     for (int g = 0; g < v11->ngroups; g++) {
  464.         for (int i = 0; i < v11->groups[g].nvertices; i++) {
  465.             BrVector3Normalise(&v11->groups[g].vertices[i].n, &v11->groups[g].vertices[i].n);
  466.         }
  467.     }
  468.  
  469.     gl_vertex* verts = malloc(sizeof(gl_vertex) * total_verts);
  470.     unsigned int* indices = malloc(sizeof(int) * 3 * total_faces);
  471.  
  472.     int v_index = 0;
  473.     int i_index = 0;
  474.     int face_offset = 0;
  475.     for (int g = 0; g < v11->ngroups; g++) {
  476.         for (int i = 0; i < v11->groups[g].nvertices; i++) {
  477.             fmt_vertex* v = &v11->groups[g].vertices[i];
  478.             verts[v_index].p = v->p;
  479.             verts[v_index].n = v->n;
  480.             verts[v_index].map = v->map;
  481.             verts[v_index].colour_index = BR_ALPHA(v11->groups[g].vertex_colours[i]);
  482.             v_index++;
  483.         }
  484.         for (int i = 0; i < v11->groups[g].nfaces; i++) {
  485.             v11face* f = &v11->groups[g].faces[i];
  486.             indices[i_index++] = f->vertices[0] + face_offset;
  487.             indices[i_index++] = f->vertices[1] + face_offset;
  488.             indices[i_index++] = f->vertices[2] + face_offset;
  489.         }
  490.         face_offset += v11->groups[g].nvertices;
  491.     }
  492.  
  493.     glGenVertexArrays(1, &ctx->vao_id);
  494.     glGenBuffers(1, &ctx->vbo_id);
  495.     glGenBuffers(1, &ctx->ebo_id);
  496.  
  497.     // Vertices
  498.     glBindVertexArray(ctx->vao_id);
  499.     glBindBuffer(GL_ARRAY_BUFFER, ctx->vbo_id);
  500.     glBufferData(GL_ARRAY_BUFFER, sizeof(gl_vertex) * total_verts, verts, GL_STATIC_DRAW);
  501.     // pos
  502.     glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, p));
  503.     glEnableVertexAttribArray(0);
  504.     // normal
  505.     glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, n));
  506.     glEnableVertexAttribArray(1);
  507.     // uv coordinates
  508.     glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, map));
  509.     glEnableVertexAttribArray(2);
  510.     // color
  511.     glVertexAttribPointer(3, 1, GL_FLOAT, GL_FALSE, sizeof(gl_vertex), (void*)offsetof(gl_vertex, colour_index));
  512.     glEnableVertexAttribArray(3);
  513.  
  514.     // Indices
  515.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id);
  516.     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int) * 3 * total_faces, indices, GL_STATIC_DRAW);
  517.     glBindVertexArray(0);
  518.  
  519.     free(verts);
  520.     free(indices);
  521.  
  522.     model->stored = ctx;
  523.  
  524.     CHECK_GL_ERROR("after build model");
  525. }
  526.  
  527. void setActiveMaterial(tStored_material* material) {
  528.     if (material == NULL || material == current_material) {
  529.         return;
  530.     }
  531.  
  532.     glUniformMatrix2x3fv(uniforms_3d.material_uv_transform, 1, GL_TRUE, &material->map_transform.m[0][0]);
  533.  
  534.     if (material->pixelmap) {
  535.         tStored_pixelmap* stored_px = material->pixelmap->stored;
  536.         if (stored_px == NULL) {
  537.             LOG_PANIC("stored_px is null for pixelmap %s", material->pixelmap->identifier);
  538.         }
  539.         glBindTexture(GL_TEXTURE_2D, stored_px->id);
  540.         glUniform1ui(uniforms_3d.material_texture_enabled, 1);
  541.     } else {
  542.         glUniform1ui(uniforms_3d.material_texture_enabled, 0);
  543.  
  544.         // index_base and index_range are only used for untextured materials
  545.         glUniform1ui(uniforms_3d.material_index_base, material->index_base);
  546.         // glUniform1ui(uniforms_3d.material_index_range, material->index_range);  TODO: re-add when we support untextured lighting
  547.     }
  548.  
  549.     if (material->shade_table) {
  550.         glUniform1ui(uniforms_3d.material_shade_table_height, material->shade_table->height);
  551.         GLRenderer_SetShadeTable(material->shade_table);
  552.     }
  553.  
  554.     if (material->index_blend) {
  555.         glUniform1ui(uniforms_3d.material_blend_enabled, 1);
  556.         GLRenderer_SetBlendTable(material->index_blend);
  557.         // materials with index_blend do not write to depth buffer (https://www.cwaboard.co.uk/viewtopic.php?p=105846&sid=58ad8910238000ca14b01dad85117175#p105846)
  558.         glDepthMask(GL_FALSE);
  559.     } else {
  560.         glUniform1ui(uniforms_3d.material_blend_enabled, 0);
  561.         glDepthMask(GL_TRUE);
  562.     }
  563.  
  564.     glUniform1ui(uniforms_3d.material_flags, material->flags);
  565.  
  566.     if (material->flags & (BR_MATF_TWO_SIDED | BR_MATF_ALWAYS_VISIBLE)) {
  567.         glDisable(GL_CULL_FACE);
  568.     } else {
  569.         glEnable(GL_CULL_FACE);
  570.     }
  571.  
  572.     CHECK_GL_ERROR("GLRenderer_RenderModelxx");
  573. }
  574.  
  575. void GLRenderer_Model(br_actor* actor, br_model* model, br_material* material, br_token render_type, br_matrix34 model_matrix) {
  576.     tStored_model_context* ctx;
  577.     v11group* group;
  578.     int element_index = 0;
  579.  
  580.     if (model->flags & BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE) {
  581.         if (model->stored) {
  582.             ((br_object*)model->stored)->dispatch->_free((br_object*)model->stored);
  583.             model->stored = NULL;
  584.         }
  585.         GLRenderer_BufferModel(model);
  586.         model->flags &= ~BR_MODF_DETHRACE_FORCE_BUFFER_UPDATE;
  587.     }
  588.  
  589.     ctx = model->stored;
  590.     v11model* v11 = model->prepared;
  591.  
  592.     if (v11 == NULL) {
  593.         // LOG_WARN("No model prepared for %s", model->identifier);
  594.         return;
  595.     }
  596.  
  597.     // model matrix
  598.     br_matrix4 view_model_matrix, model_matrix4, inverse, inverse_transpose;
  599.     BrMatrix4Copy34(&model_matrix4, &model_matrix);
  600.     glUniformMatrix4fv(uniforms_3d.model, 1, GL_FALSE, model_matrix4.m[0]);
  601.  
  602.     // normal matrix (http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/)
  603.     BrMatrix4Mul(&view_model_matrix, &view_matrix, &model_matrix4);
  604.     BrMatrix4Inverse(&inverse, &view_model_matrix);
  605.     // transpose
  606.     for (int i = 0; i < 4; i++) {
  607.         for (int j = 0; j < 4; j++) {
  608.             inverse_transpose.m[i][j] = inverse.m[j][i];
  609.         }
  610.     }
  611.     glUniformMatrix4fv(uniforms_3d.normal_matrix, 1, GL_FALSE, &inverse_transpose.m[0][0]);
  612.  
  613.     glBindVertexArray(ctx->vao_id);
  614.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ctx->ebo_id);
  615.  
  616.     // set default material for this actor/model
  617.     setActiveMaterial(material->stored);
  618.  
  619.     switch (render_type) {
  620.     case BRT_TRIANGLE:
  621.         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  622.         break;
  623.     case BRT_LINE:
  624.         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  625.         glUniform1ui(uniforms_3d.material_index_base, 255);
  626.         glUniform1ui(uniforms_3d.material_flags, 0);
  627.         break;
  628.     default:
  629.         LOG_PANIC("render_type %d is not supported?!", render_type);
  630.     }
  631.  
  632.     for (int g = 0; g < v11->ngroups; g++) {
  633.         group = &v11->groups[g];
  634.         setActiveMaterial(group->stored);
  635.         glDrawElements(GL_TRIANGLES, group->nfaces * 3, GL_UNSIGNED_INT, (void*)(element_index * sizeof(int)));
  636.         element_index += group->nfaces * 3;
  637.     }
  638.  
  639.     glBindVertexArray(0);
  640.     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
  641.     dirty_buffers = 1;
  642.  
  643.     CHECK_GL_ERROR("GLRenderer_RenderModel");
  644. }
  645.  
  646. void GLRenderer_BufferMaterial(br_material* mat) {
  647.     tStored_material* stored = mat->stored;
  648.     if (!stored) {
  649.         stored = NewStoredMaterial();
  650.         mat->stored = stored;
  651.         if (mat->identifier) {
  652.             strcpy(stored->identifier, mat->identifier);
  653.         }
  654.     }
  655.     BrMatrix23Copy(&stored->map_transform, &mat->map_transform);
  656.     stored->pixelmap = mat->colour_map;
  657.     stored->flags = mat->flags;
  658.     stored->shade_table = mat->index_shade;
  659.     stored->index_base = mat->index_base;
  660.     stored->index_range = mat->index_range;
  661.     stored->index_blend = mat->index_blend;
  662. }
  663.  
  664. void GLRenderer_BufferTexture(br_pixelmap* pm) {
  665.     tStored_pixelmap* stored = pm->stored;
  666.     if (pm->stored) {
  667.     } else {
  668.         stored = NewStoredPixelmap();
  669.         glGenTextures(1, &stored->id);
  670.         pm->stored = stored;
  671.     }
  672.  
  673.     // sometimes the pixelmap has row_bytes > width. OpenGL expects linear pixels, so flatten it out
  674.     uint8_t* linear_pixels = malloc(sizeof(uint8_t) * pm->width * pm->height);
  675.     uint8_t* original_pixels = pm->pixels;
  676.     for (int y = 0; y < pm->height; y++) {
  677.         for (int x = 0; x < pm->width; x++) {
  678.             linear_pixels[y * pm->width + x] = original_pixels[y * pm->row_bytes + x];
  679.         }
  680.     }
  681.  
  682.     glBindTexture(GL_TEXTURE_2D, stored->id);
  683.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  684.     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  685.     glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, pm->width, pm->height, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, linear_pixels);
  686.     free(linear_pixels);
  687.  
  688.     CHECK_GL_ERROR("GLRenderer_BufferTexture");
  689. }
  690.  
  691. void GLRenderer_FlushBuffer(tRenderer_flush_type flush_type) {
  692.  
  693.     if (!dirty_buffers) {
  694.         return;
  695.     }
  696.  
  697.     // pull framebuffer into cpu memory to emulate BRender behavior
  698.     glBindTexture(GL_TEXTURE_2D, framebuffer_texture);
  699.     glGetTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, screen_buffer_flip_pixels);
  700.  
  701.     // flip texture to match the expected orientation
  702.     int dest_y = render_height;
  703.     uint8_t* pm_pixels = last_colour_buffer->pixels;
  704.     uint8_t new_pixel;
  705.     for (int y = 0; y < render_height; y++) {
  706.         dest_y--;
  707.         for (int x = 0; x < render_width; x++) {
  708.             new_pixel = screen_buffer_flip_pixels[y * render_width + x];
  709.             if (new_pixel != 0) {
  710.                 pm_pixels[dest_y * render_width + x] = new_pixel;
  711.             }
  712.         }
  713.     }
  714.  
  715.     if (flush_type == eFlush_all) {
  716.  
  717.         // pull depthbuffer into cpu memory to emulate BRender behavior
  718.         glBindTexture(GL_TEXTURE_2D, depth_texture);
  719.         glGetTexImage(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, depth_buffer_flip_pixels);
  720.  
  721.         dest_y = last_colour_buffer->height;
  722.         int src_y = render_height - last_colour_buffer->base_y - last_colour_buffer->height;
  723.         uint16_t* depth_pixels = last_depth_buffer->pixels;
  724.         for (int y = 0; y < last_colour_buffer->height; y++) {
  725.             dest_y--;
  726.             for (int x = 0; x < last_colour_buffer->width; x++) {
  727.                 uint16_t new_depth = depth_buffer_flip_pixels[src_y * render_width + last_colour_buffer->base_x + x];
  728.                 depth_pixels[dest_y * render_width + x] = new_depth;
  729.             }
  730.             src_y++;
  731.         }
  732.     }
  733.     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_id);
  734.     glClear(GL_COLOR_BUFFER_BIT);
  735.     flush_counter++;
  736.     dirty_buffers = 0;
  737. }
  738.  
  739. void GLRenderer_FlushBuffers(void) {
  740.     GLRenderer_FlushBuffer(eFlush_all);
  741. }
  742.  
  743. void GLRenderer_SetViewport(int x, int y, int width, int height) {
  744.     vp_x = x;
  745.     vp_y = y;
  746.     vp_width = width;
  747.     vp_height = height;
  748. }
  749.