Rev 1 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 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 | |||
| 14 | pmbaty | 223 | if (glGetString(GL_SHADING_LANGUAGE_VERSION) == NULL) { |
| 224 | LOG_PANIC("GL_SHADING_LANGUAGE_VERSION is null"); |
||
| 225 | } |
||
| 226 | |||
| 1 | pmbaty | 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, ¤t_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 | } |