generalization of deferred commands and any_gl rendering where appropriate
This commit is contained in:
		| @@ -109,4 +109,7 @@ typedef struct { | ||||
|     }; | ||||
| } DeferredCommand; | ||||
|  | ||||
|  | ||||
| extern DeferredCommand *deferred_commands; | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -4,6 +4,7 @@ | ||||
| #include "twn_camera_c.h" | ||||
| #include "twn_types.h" | ||||
| #include "twn_vec.h" | ||||
| #include "twn_deferred_commands.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
| #include <stb_ds.h> | ||||
| @@ -18,6 +19,8 @@ | ||||
| #include <tgmath.h> | ||||
|  | ||||
|  | ||||
| DeferredCommand *deferred_commands; | ||||
|  | ||||
| /* TODO: have a default initialized one */ | ||||
| Matrix4 camera_projection_matrix; | ||||
| Matrix4 camera_look_at_matrix; | ||||
| @@ -401,3 +404,125 @@ DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 position, | ||||
|         .up = camera.up, | ||||
|     }; | ||||
| } | ||||
|  | ||||
|  | ||||
| void set_depth_range(double low, double high) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_DEPTH_RANGE, | ||||
|         .depth_range = { | ||||
|             .low = low, | ||||
|             .high = high | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| void clear_draw_buffer(void) { | ||||
|     /* TODO: we can optimize a rectangle drawn over whole window to a clear color call*/ | ||||
|  | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_CLEAR, | ||||
|         .clear = (DeferredCommandClear) { | ||||
|             .clear_color = true, | ||||
|             .clear_depth = true, | ||||
|             .clear_stencil = true, | ||||
|             .color = (Color) { 230, 230, 230, 1 } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| void use_texture_mode(TextureMode mode) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_USE_TEXTURE_MODE, | ||||
|         .use_texture_mode = { mode } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| void use_2d_pipeline(void) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_USE_PIPIELINE, | ||||
|         .use_pipeline = { PIPELINE_2D } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| void use_space_pipeline(void) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_USE_PIPIELINE, | ||||
|         .use_pipeline = { PIPELINE_SPACE } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| void issue_deferred_draw_commands(void) { | ||||
|     for (size_t i = 0; i < arrlenu(deferred_commands); ++i) { | ||||
|         switch (deferred_commands[i].type) { | ||||
|             case DEFERRED_COMMAND_TYPE_DEPTH_RANGE: { | ||||
|                 finally_set_depth_range(deferred_commands[i].depth_range); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_CLEAR: { | ||||
|                 finally_clear_draw_buffer(deferred_commands[i].clear); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_DRAW: { | ||||
|                 finally_draw_command(deferred_commands[i].draw); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_DRAW_SKYBOX: { | ||||
|                 finally_render_skybox(deferred_commands[i].draw_skybox); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_USE_PIPIELINE: { | ||||
|                 switch (deferred_commands[i].use_pipeline.pipeline) { | ||||
|                     case PIPELINE_2D: | ||||
|                         finally_use_2d_pipeline(); | ||||
|                         break; | ||||
|                     case PIPELINE_SPACE: | ||||
|                         finally_use_space_pipeline(); | ||||
|                         break; | ||||
|                     case PIPELINE_NO: | ||||
|                     default: | ||||
|                         SDL_assert(false); | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_USE_TEXTURE_MODE: { | ||||
|                 finally_use_texture_mode(deferred_commands[i].use_texture_mode.mode); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_APPLY_FOG: { | ||||
|                 finally_apply_fog(deferred_commands[i].apply_fog); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_POP_FOG: { | ||||
|                 finally_pop_fog(); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             default: | ||||
|                 SDL_assert(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,9 +1,12 @@ | ||||
| #ifndef TWN_DRAW_C_H | ||||
| #define TWN_DRAW_C_H | ||||
|  | ||||
| /* TODO: structure more categorically */ | ||||
|  | ||||
| #include "twn_textures_c.h" | ||||
| #include "twn_text_c.h" | ||||
| #include "twn_option.h" | ||||
| #include "twn_deferred_commands.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
| #include <stb_truetype.h> | ||||
| @@ -173,10 +176,12 @@ bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder, | ||||
| void setup_viewport(int x, int y, int width, int height); | ||||
|  | ||||
| void clear_draw_buffer(void); | ||||
| void finally_clear_draw_buffer(DeferredCommandClear command); | ||||
|  | ||||
| void swap_buffers(void); | ||||
|  | ||||
| void set_depth_range(double low, double high); | ||||
| void finally_set_depth_range(DeferredCommandDepthRange command); | ||||
|  | ||||
| VertexBuffer get_quad_element_buffer(void); | ||||
|  | ||||
| @@ -187,10 +192,13 @@ void render_circle(const CirclePrimitive *circle); | ||||
| void render_rectangle(const RectPrimitive *rectangle); | ||||
|  | ||||
| void use_space_pipeline(void); | ||||
| void finally_use_space_pipeline(void); | ||||
|  | ||||
| void use_2d_pipeline(void); | ||||
| void finally_use_2d_pipeline(void); | ||||
|  | ||||
| void use_texture_mode(TextureMode mode); | ||||
| void finally_use_texture_mode(TextureMode mode); | ||||
|  | ||||
| void finally_render_quads(Primitive2D const primitives[], | ||||
|                           struct QuadBatch batch, | ||||
| @@ -220,19 +228,18 @@ void finally_draw_text(FontData const *font_data, | ||||
|                        VertexBuffer buffer); | ||||
|  | ||||
| void render_skybox(void); | ||||
|  | ||||
| void finally_render_skybox(char *paths_in_use); | ||||
| void finally_render_skybox(DeferredCommandDrawSkybox); | ||||
|  | ||||
| void apply_fog(void); | ||||
|  | ||||
| void finally_apply_fog(float start, float end, float density, Color color); | ||||
|  | ||||
| void finally_apply_fog(DeferredCommandApplyFog); | ||||
| void pop_fog(void); | ||||
|  | ||||
| void finally_pop_fog(void); | ||||
|  | ||||
| void start_render_frame(void); | ||||
|  | ||||
| void end_render_frame(void); | ||||
|  | ||||
| void finally_draw_command(DeferredCommandDraw command); | ||||
|  | ||||
| void issue_deferred_draw_commands(void); | ||||
|  | ||||
| #endif | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| #include "twn_draw.h" | ||||
| #include "twn_draw_c.h" | ||||
|  | ||||
| #include <stb_ds.h> | ||||
|  | ||||
| #include <stdbool.h> | ||||
|  | ||||
| static float start_cache, end_cache, density_cache; | ||||
| @@ -21,7 +23,17 @@ void apply_fog(void) { | ||||
|     if (!fog_used) | ||||
|         return; | ||||
|  | ||||
|     finally_apply_fog(start_cache, end_cache, density_cache, color_cache); | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_APPLY_FOG, | ||||
|         .apply_fog = (DeferredCommandApplyFog){ | ||||
|             .start = start_cache, | ||||
|             .end = end_cache, | ||||
|             .density = density_cache, | ||||
|             .color = color_cache | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -29,5 +41,9 @@ void pop_fog(void) { | ||||
|     if (!fog_used) | ||||
|         return; | ||||
|  | ||||
|     finally_pop_fog(); | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_POP_FOG, | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "twn_text_c.h" | ||||
| #include "twn_types.h" | ||||
| #include "twn_deferred_commands.h" | ||||
| #include "twn_gl_any_rendering_c.h" | ||||
|  | ||||
| #include <glad/glad.h> | ||||
| #include <stb_ds.h> | ||||
| @@ -81,219 +82,6 @@ static TextureMode texture_mode_last_used = TEXTURE_MODE_UNKNOWN; | ||||
| static Pipeline pipeline_last_used = PIPELINE_NO; | ||||
|  | ||||
|  | ||||
| /* potentially double buffered array of vertex array handles */ | ||||
| /* we assume they will be refilled fully each frame */ | ||||
| static size_t scratch_va_front_used, scratch_va_back_used; | ||||
| static GLuint *front_scratch_vertex_arrays, *back_scratch_vertex_arrays; | ||||
| static GLuint **current_scratch_vertex_array = &front_scratch_vertex_arrays; | ||||
|  | ||||
| static void restart_scratch_vertex_arrays(void) { | ||||
|     scratch_va_front_used = 0; | ||||
|     scratch_va_back_used = 0; | ||||
|  | ||||
|     if (ctx.render_double_buffered) { | ||||
|         current_scratch_vertex_array = current_scratch_vertex_array == &front_scratch_vertex_arrays ? | ||||
|                 &back_scratch_vertex_arrays : &front_scratch_vertex_arrays; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| GLuint get_scratch_vertex_array(void) { | ||||
|     size_t *used = current_scratch_vertex_array == &front_scratch_vertex_arrays ? | ||||
|             &scratch_va_front_used : &scratch_va_back_used; | ||||
|  | ||||
|     if (arrlenu(*current_scratch_vertex_array) <= *used) { | ||||
|         GLuint handle; | ||||
|         glGenBuffers(1, &handle); | ||||
|         arrpush(*current_scratch_vertex_array, handle); | ||||
|     } | ||||
|  | ||||
|     (*used)++; | ||||
|     return (*current_scratch_vertex_array)[*used - 1]; | ||||
| } | ||||
|  | ||||
|  | ||||
| static void finally_use_2d_pipeline(void); | ||||
| static void finally_use_space_pipeline(void); | ||||
| static void finally_use_texture_mode(TextureMode mode); | ||||
| static void deferred_render_skybox(char *paths); | ||||
| static void deferred_apply_fog(float start, float end, float density, Color color); | ||||
| static void deferred_pop_fog(void); | ||||
|  | ||||
| static DeferredCommand *deferred_commands; | ||||
|  | ||||
| static void issue_deferred_draw_commands(void) { | ||||
|     for (size_t i = 0; i < arrlenu(deferred_commands); ++i) { | ||||
|         switch (deferred_commands[i].type) { | ||||
|             case DEFERRED_COMMAND_TYPE_DEPTH_RANGE: { | ||||
|                 glDepthRange(deferred_commands[i].depth_range.low, deferred_commands[i].depth_range.high); | ||||
|                 break;    | ||||
|             } | ||||
|             case DEFERRED_COMMAND_TYPE_CLEAR: { | ||||
|                 glClearColor((1.0f / 255) * deferred_commands[i].clear.color.r, | ||||
|                              (1.0f / 255) * deferred_commands[i].clear.color.g, | ||||
|                              (1.0f / 255) * deferred_commands[i].clear.color.b, | ||||
|                              (1.0f / 255) * deferred_commands[i].clear.color.a); | ||||
|  | ||||
|                 /* needed as we might mess with it */ | ||||
|                 glDepthRange(0.0, 1.0); | ||||
|                 glDepthMask(GL_TRUE); | ||||
|  | ||||
|                 glClear((deferred_commands[i].clear.clear_color   ? GL_COLOR_BUFFER_BIT   : 0) | | ||||
|                         (deferred_commands[i].clear.clear_depth   ? GL_DEPTH_BUFFER_BIT   : 0) | | ||||
|                         (deferred_commands[i].clear.clear_stencil ? GL_STENCIL_BUFFER_BIT : 0) ); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_DRAW: { | ||||
|                 DeferredCommandDraw const command = deferred_commands[i].draw; | ||||
|  | ||||
|                 /* TODO: don't assume a single vertex array ? */ | ||||
|                 SDL_assert(command.vertices.arity != 0); | ||||
|                 SDL_assert(command.vertices.buffer); | ||||
|                 SDL_assert((command.element_buffer && command.element_count != 0) || command.primitive_count != 0); | ||||
|  | ||||
|                 glBindBuffer(GL_ARRAY_BUFFER, command.vertices.buffer); | ||||
|                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, command.element_buffer); | ||||
|  | ||||
|                 glEnableClientState(GL_VERTEX_ARRAY); | ||||
|                 glVertexPointer(command.vertices.arity, | ||||
|                                 command.vertices.type, | ||||
|                                 command.vertices.stride, | ||||
|                                 (void *)command.vertices.offset); | ||||
|  | ||||
|                 if (command.texcoords.arity != 0) { | ||||
|                     SDL_assert(command.texcoords.buffer == command.vertices.buffer); | ||||
|  | ||||
|                     glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||||
|                     glClientActiveTexture(GL_TEXTURE0); | ||||
|                     glTexCoordPointer(command.texcoords.arity, | ||||
|                                       command.texcoords.type, | ||||
|                                       command.texcoords.stride, | ||||
|                                       (void *)command.texcoords.offset); | ||||
|                 } | ||||
|  | ||||
|                 if (command.colors.arity != 0) { | ||||
|                     SDL_assert(command.colors.buffer == command.vertices.buffer); | ||||
|  | ||||
|                     glEnableClientState(GL_COLOR_ARRAY); | ||||
|                     glColorPointer(command.colors.arity, | ||||
|                                    command.colors.type, | ||||
|                                    command.colors.stride, | ||||
|                                    (void *)command.colors.offset); | ||||
|                 } else if (command.constant_colored) | ||||
|                     glColor4ub(command.color.r, | ||||
|                                command.color.g, | ||||
|                                command.color.b, | ||||
|                                command.color.a); | ||||
|  | ||||
|                 if (command.textured) { | ||||
|                     if (command.uses_gpu_key) | ||||
|                         glBindTexture(GL_TEXTURE_2D, command.gpu_texture); | ||||
|                     else if (command.texture_repeat) | ||||
|                         textures_bind_repeating(&ctx.texture_cache, command.texture_key); | ||||
|                     else | ||||
|                         textures_bind(&ctx.texture_cache, command.texture_key); | ||||
|                 } | ||||
|  | ||||
|                 if (command.element_buffer) { | ||||
|                     SDL_assert(command.element_count != 0); | ||||
|                     if (command.range_start == command.range_end) | ||||
|                         glDrawElements(GL_TRIANGLES, command.element_count, GL_UNSIGNED_SHORT, NULL); | ||||
|                     else | ||||
|                         glDrawRangeElements(GL_TRIANGLES, | ||||
|                                             command.range_start, | ||||
|                                             command.range_end, | ||||
|                                             command.element_count, | ||||
|                                             GL_UNSIGNED_SHORT, | ||||
|                                             NULL); | ||||
|                 } else { | ||||
|                     SDL_assert(command.primitive_count != 0); | ||||
|                     glDrawArrays(GL_TRIANGLES, 0, command.primitive_count); | ||||
|                 } | ||||
|  | ||||
|                 /* state clearing */ | ||||
|  | ||||
|                 if (command.textured) | ||||
|                     glBindTexture(GL_TEXTURE_2D, 0); | ||||
|  | ||||
|                 if (command.colors.arity != 0) | ||||
|                     glDisableClientState(GL_COLOR_ARRAY); | ||||
|  | ||||
|                 if (command.texcoords.arity != 0) | ||||
|                     glDisableClientState(GL_TEXTURE_COORD_ARRAY); | ||||
|  | ||||
|                 glDisableClientState(GL_VERTEX_ARRAY); | ||||
|  | ||||
|                 glBindBuffer(GL_ARRAY_BUFFER, 0); | ||||
|                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | ||||
|  | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_DRAW_SKYBOX: { | ||||
|                 deferred_render_skybox(deferred_commands[i].draw_skybox.paths); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_USE_PIPIELINE: { | ||||
|                 switch (deferred_commands[i].use_pipeline.pipeline) { | ||||
|                     case PIPELINE_2D: | ||||
|                         finally_use_2d_pipeline(); | ||||
|                         break; | ||||
|                     case PIPELINE_SPACE: | ||||
|                         finally_use_space_pipeline(); | ||||
|                         break; | ||||
|                     case PIPELINE_NO: | ||||
|                     default: | ||||
|                         SDL_assert(false); | ||||
|                 } | ||||
|  | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_USE_TEXTURE_MODE: { | ||||
|                 finally_use_texture_mode(deferred_commands[i].use_texture_mode.mode); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_APPLY_FOG: { | ||||
|                 deferred_apply_fog(deferred_commands[i].apply_fog.start, | ||||
|                                    deferred_commands[i].apply_fog.end, | ||||
|                                    deferred_commands[i].apply_fog.density, | ||||
|                                    deferred_commands[i].apply_fog.color); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             case DEFERRED_COMMAND_TYPE_POP_FOG: { | ||||
|                 deferred_pop_fog(); | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             default: | ||||
|                 SDL_assert(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| void clear_draw_buffer(void) { | ||||
|     /* TODO: we can optimize a rectangle drawn over whole window to a clear color call*/ | ||||
|  | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_CLEAR, | ||||
|         .clear = (DeferredCommandClear) { | ||||
|             .clear_color = true, | ||||
|             .clear_depth = true, | ||||
|             .clear_stencil = true, | ||||
|             .color = (Color) { 230, 230, 230, 1 } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| void start_render_frame(void) { | ||||
|     clear_draw_buffer(); | ||||
| } | ||||
| @@ -318,17 +106,7 @@ void end_render_frame(void) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void use_space_pipeline(void) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_USE_PIPIELINE, | ||||
|         .use_pipeline = { PIPELINE_SPACE } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void finally_use_space_pipeline(void) { | ||||
| void finally_use_space_pipeline(void) { | ||||
|     if (pipeline_last_used == PIPELINE_SPACE) | ||||
|         return; | ||||
|  | ||||
| @@ -366,17 +144,7 @@ static void finally_use_space_pipeline(void) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void use_2d_pipeline(void) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_USE_PIPIELINE, | ||||
|         .use_pipeline = { PIPELINE_2D } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void finally_use_2d_pipeline(void) { | ||||
| void finally_use_2d_pipeline(void) { | ||||
|     if (pipeline_last_used == PIPELINE_SPACE) { | ||||
|         glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); | ||||
|         glFlush(); | ||||
| @@ -418,17 +186,7 @@ static void finally_use_2d_pipeline(void) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void use_texture_mode(TextureMode mode) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_USE_TEXTURE_MODE, | ||||
|         .use_texture_mode = { mode } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void finally_use_texture_mode(TextureMode mode) { | ||||
| void finally_use_texture_mode(TextureMode mode) { | ||||
|     if (texture_mode_last_used == mode) | ||||
|         return; | ||||
|  | ||||
| @@ -815,6 +573,7 @@ size_t get_text_payload_size(void) { | ||||
|     return sizeof (ElementIndexedQuadWithoutColor); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void load_cubemap_side(const char *path, GLenum target) { | ||||
|     SDL_Surface *surface = textures_load_surface(path); | ||||
|     /* TODO: sanity check whether all of them have same dimensions? */ | ||||
| @@ -891,35 +650,23 @@ void render_circle(const CirclePrimitive *circle) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_render_skybox(char *paths) { | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_DRAW_SKYBOX, | ||||
|         .draw_skybox = (DeferredCommandDrawSkybox){ | ||||
|             .paths = paths | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void deferred_render_skybox(char *paths) { | ||||
| void finally_render_skybox(DeferredCommandDrawSkybox command) { | ||||
|     static GLuint cubemap = 0; | ||||
|     static char *paths_cache = NULL; | ||||
|  | ||||
|     bool loading_needed = false; | ||||
|  | ||||
|     /* drop it */ | ||||
|     if (!paths_cache || (SDL_strcmp(paths_cache, paths) != 0)) { | ||||
|     if (!paths_cache || (SDL_strcmp(paths_cache, command.paths) != 0)) { | ||||
|         if (cubemap) | ||||
|             glDeleteTextures(1, &cubemap); | ||||
|         glGenTextures(1, &cubemap); | ||||
|         if (paths_cache) | ||||
|             SDL_free(paths_cache); | ||||
|         paths_cache = paths; | ||||
|         paths_cache = command.paths; | ||||
|         loading_needed = true; | ||||
|     } else | ||||
|         SDL_free(paths); | ||||
|         SDL_free(command.paths); | ||||
|  | ||||
|     Matrix4 camera_look_at_matrix_solipsist = camera_look_at_matrix; | ||||
|     camera_look_at_matrix_solipsist.row[3].x = 0; | ||||
| @@ -935,27 +682,27 @@ static void deferred_render_skybox(char *paths) { | ||||
|  | ||||
|     if (loading_needed) { | ||||
|         /* load all the sides */ | ||||
|         char *expanded = expand_asterisk(paths, "up"); | ||||
|         char *expanded = expand_asterisk(command.paths, "up"); | ||||
|         load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Y); | ||||
|         SDL_free(expanded); | ||||
|  | ||||
|         expanded = expand_asterisk(paths, "down"); | ||||
|         expanded = expand_asterisk(command.paths, "down"); | ||||
|         load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y); | ||||
|         SDL_free(expanded); | ||||
|  | ||||
|         expanded = expand_asterisk(paths, "east"); | ||||
|         expanded = expand_asterisk(command.paths, "east"); | ||||
|         load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_X); | ||||
|         SDL_free(expanded); | ||||
|  | ||||
|         expanded = expand_asterisk(paths, "north"); | ||||
|         expanded = expand_asterisk(command.paths, "north"); | ||||
|         load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Z); | ||||
|         SDL_free(expanded); | ||||
|  | ||||
|         expanded = expand_asterisk(paths, "west"); | ||||
|         expanded = expand_asterisk(command.paths, "west"); | ||||
|         load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_X); | ||||
|         SDL_free(expanded); | ||||
|  | ||||
|         expanded = expand_asterisk(paths, "south"); | ||||
|         expanded = expand_asterisk(command.paths, "south"); | ||||
|         load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z); | ||||
|         SDL_free(expanded); | ||||
|     } | ||||
| @@ -1051,65 +798,132 @@ static void deferred_render_skybox(char *paths) { | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_apply_fog(float start, float end, float density, Color color) { | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_APPLY_FOG, | ||||
|         .apply_fog = (DeferredCommandApplyFog){ | ||||
|             .start = start, | ||||
|             .end = end, | ||||
|             .density = density, | ||||
|             .color = color | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void deferred_apply_fog(float start, float end, float density, Color color) { | ||||
|     if (density < 0.0f || density > 1.0f) | ||||
| void finally_apply_fog(DeferredCommandApplyFog command) { | ||||
|     if (command.density < 0.0f || command.density > 1.0f) | ||||
|         log_warn("Invalid fog density given, should be in range [0..1]"); | ||||
|  | ||||
|     /* TODO: cache it for constant parameters, which is a common case */ | ||||
|  | ||||
|     glEnable(GL_FOG); | ||||
|  | ||||
|     glFogf(GL_FOG_DENSITY, density); | ||||
|     glFogf(GL_FOG_START, start); | ||||
|     glFogf(GL_FOG_END, end); | ||||
|     glFogf(GL_FOG_DENSITY, command.density); | ||||
|     glFogf(GL_FOG_START, command.start); | ||||
|     glFogf(GL_FOG_END, command.end); | ||||
|  | ||||
|     float color_conv[4]; | ||||
|     color_conv[0] = (float)color.r / UINT8_MAX; | ||||
|     color_conv[1] = (float)color.g / UINT8_MAX; | ||||
|     color_conv[2] = (float)color.b / UINT8_MAX; | ||||
|     color_conv[3] = (float)color.a / UINT8_MAX; | ||||
|     color_conv[0] = (float)command.color.r / UINT8_MAX; | ||||
|     color_conv[1] = (float)command.color.g / UINT8_MAX; | ||||
|     color_conv[2] = (float)command.color.b / UINT8_MAX; | ||||
|     color_conv[3] = (float)command.color.a / UINT8_MAX; | ||||
|  | ||||
|     glFogfv(GL_FOG_COLOR, color_conv); | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_pop_fog(void) { | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_POP_FOG, | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| } | ||||
|  | ||||
|  | ||||
| static void deferred_pop_fog(void) { | ||||
|     glDisable(GL_FOG); | ||||
| } | ||||
|  | ||||
|  | ||||
| void set_depth_range(double low, double high) { | ||||
|     DeferredCommand const command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_DEPTH_RANGE, | ||||
|         .depth_range = { | ||||
|             .low = low, | ||||
|             .high = high | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
| void finally_set_depth_range(DeferredCommandDepthRange command) { | ||||
|     glDepthRange(command.low, command.high); | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_clear_draw_buffer(DeferredCommandClear command) { | ||||
|     glClearColor((1.0f / 255) * command.color.r, | ||||
|                  (1.0f / 255) * command.color.g, | ||||
|                  (1.0f / 255) * command.color.b, | ||||
|                  (1.0f / 255) * command.color.a); | ||||
|  | ||||
|     /* needed as we might mess with it */ | ||||
|     glDepthRange(0.0, 1.0); | ||||
|     glDepthMask(GL_TRUE); | ||||
|  | ||||
|     glClear((command.clear_color   ? GL_COLOR_BUFFER_BIT   : 0) | | ||||
|             (command.clear_depth   ? GL_DEPTH_BUFFER_BIT   : 0) | | ||||
|             (command.clear_stencil ? GL_STENCIL_BUFFER_BIT : 0) ); | ||||
| } | ||||
|  | ||||
|  | ||||
| void finally_draw_command(DeferredCommandDraw command) { | ||||
|     /* TODO: don't assume a single vertex array ? */ | ||||
|     SDL_assert(command.vertices.arity != 0); | ||||
|     SDL_assert(command.vertices.buffer); | ||||
|     SDL_assert((command.element_buffer && command.element_count != 0) || command.primitive_count != 0); | ||||
|  | ||||
|     glBindBuffer(GL_ARRAY_BUFFER, command.vertices.buffer); | ||||
|     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, command.element_buffer); | ||||
|  | ||||
|     glEnableClientState(GL_VERTEX_ARRAY); | ||||
|     glVertexPointer(command.vertices.arity, | ||||
|                     command.vertices.type, | ||||
|                     command.vertices.stride, | ||||
|                     (void *)command.vertices.offset); | ||||
|  | ||||
|     if (command.texcoords.arity != 0) { | ||||
|         SDL_assert(command.texcoords.buffer == command.vertices.buffer); | ||||
|  | ||||
|         glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||||
|         glClientActiveTexture(GL_TEXTURE0); | ||||
|         glTexCoordPointer(command.texcoords.arity, | ||||
|                           command.texcoords.type, | ||||
|                           command.texcoords.stride, | ||||
|                           (void *)command.texcoords.offset); | ||||
|     } | ||||
|  | ||||
|     if (command.colors.arity != 0) { | ||||
|         SDL_assert(command.colors.buffer == command.vertices.buffer); | ||||
|  | ||||
|         glEnableClientState(GL_COLOR_ARRAY); | ||||
|         glColorPointer(command.colors.arity, | ||||
|                        command.colors.type, | ||||
|                        command.colors.stride, | ||||
|                        (void *)command.colors.offset); | ||||
|     } else if (command.constant_colored) | ||||
|         glColor4ub(command.color.r, | ||||
|                    command.color.g, | ||||
|                    command.color.b, | ||||
|                    command.color.a); | ||||
|  | ||||
|     if (command.textured) { | ||||
|         if (command.uses_gpu_key) | ||||
|             glBindTexture(GL_TEXTURE_2D, command.gpu_texture); | ||||
|         else if (command.texture_repeat) | ||||
|             textures_bind_repeating(&ctx.texture_cache, command.texture_key); | ||||
|         else | ||||
|             textures_bind(&ctx.texture_cache, command.texture_key); | ||||
|     } | ||||
|  | ||||
|     if (command.element_buffer) { | ||||
|         SDL_assert(command.element_count != 0); | ||||
|         if (command.range_start == command.range_end) | ||||
|             glDrawElements(GL_TRIANGLES, command.element_count, GL_UNSIGNED_SHORT, NULL); | ||||
|         else | ||||
|             glDrawRangeElements(GL_TRIANGLES, | ||||
|                                 command.range_start, | ||||
|                                 command.range_end, | ||||
|                                 command.element_count, | ||||
|                                 GL_UNSIGNED_SHORT, | ||||
|                                 NULL); | ||||
|     } else { | ||||
|         SDL_assert(command.primitive_count != 0); | ||||
|         glDrawArrays(GL_TRIANGLES, 0, command.primitive_count); | ||||
|     } | ||||
|  | ||||
|     /* state clearing */ | ||||
|  | ||||
|     if (command.textured) | ||||
|         glBindTexture(GL_TEXTURE_2D, 0); | ||||
|  | ||||
|     if (command.colors.arity != 0) | ||||
|         glDisableClientState(GL_COLOR_ARRAY); | ||||
|  | ||||
|     if (command.texcoords.arity != 0) | ||||
|         glDisableClientState(GL_TEXTURE_COORD_ARRAY); | ||||
|  | ||||
|     glDisableClientState(GL_VERTEX_ARRAY); | ||||
|  | ||||
|     glBindBuffer(GL_ARRAY_BUFFER, 0); | ||||
|     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,8 @@ | ||||
| #include "twn_gl_any_rendering_c.h" | ||||
| #include "twn_draw_c.h" | ||||
| #include "twn_engine_context_c.h" | ||||
|  | ||||
| #include <stb_ds.h> | ||||
|  | ||||
| #ifdef EMSCRIPTEN | ||||
| #include <GLES2/gl2.h> | ||||
| @@ -84,3 +88,35 @@ VertexBuffer get_circle_element_buffer(void) { | ||||
|  | ||||
|     return buffer; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* potentially double buffered array of vertex array handles */ | ||||
| /* we assume they will be refilled fully each frame */ | ||||
| static size_t scratch_va_front_used, scratch_va_back_used; | ||||
| static GLuint *front_scratch_vertex_arrays, *back_scratch_vertex_arrays; | ||||
| static GLuint **current_scratch_vertex_array = &front_scratch_vertex_arrays; | ||||
|  | ||||
| void restart_scratch_vertex_arrays(void) { | ||||
|     scratch_va_front_used = 0; | ||||
|     scratch_va_back_used = 0; | ||||
|  | ||||
|     if (ctx.render_double_buffered) { | ||||
|         current_scratch_vertex_array = current_scratch_vertex_array == &front_scratch_vertex_arrays ? | ||||
|                 &back_scratch_vertex_arrays : &front_scratch_vertex_arrays; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| GLuint get_scratch_vertex_array(void) { | ||||
|     size_t *used = current_scratch_vertex_array == &front_scratch_vertex_arrays ? | ||||
|             &scratch_va_front_used : &scratch_va_back_used; | ||||
|  | ||||
|     if (arrlenu(*current_scratch_vertex_array) <= *used) { | ||||
|         GLuint handle; | ||||
|         glGenBuffers(1, &handle); | ||||
|         arrpush(*current_scratch_vertex_array, handle); | ||||
|     } | ||||
|  | ||||
|     (*used)++; | ||||
|     return (*current_scratch_vertex_array)[*used - 1]; | ||||
| } | ||||
|   | ||||
							
								
								
									
										16
									
								
								src/rendering/twn_gl_any_rendering_c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/rendering/twn_gl_any_rendering_c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| #ifndef TWN_GL_ANY_RENDERING_C_H | ||||
| #define TWN_GL_ANY_RENDERING_C_H | ||||
|  | ||||
| #ifdef EMSCRIPTEN | ||||
| #include <GLES2/gl2.h> | ||||
| #else | ||||
| #include <glad/glad.h> | ||||
| #endif | ||||
|  | ||||
| #include <stddef.h> | ||||
|  | ||||
| void restart_scratch_vertex_arrays(void); | ||||
|  | ||||
| GLuint get_scratch_vertex_array(void); | ||||
|  | ||||
| #endif | ||||
| @@ -2,6 +2,7 @@ | ||||
| #include "twn_draw_c.h" | ||||
|  | ||||
| #include <SDL2/SDL.h> | ||||
| #include <stb_ds.h> | ||||
|  | ||||
| static char *paths_in_use; | ||||
|  | ||||
| @@ -21,6 +22,14 @@ void render_skybox(void) { | ||||
|         return; | ||||
|  | ||||
|     /* note: ownership of 'paths_in_use' goes there */ | ||||
|     finally_render_skybox(paths_in_use); | ||||
|     DeferredCommand command = { | ||||
|         .type = DEFERRED_COMMAND_TYPE_DRAW_SKYBOX, | ||||
|         .draw_skybox = (DeferredCommandDrawSkybox){ | ||||
|             .paths = paths_in_use | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     arrpush(deferred_commands, command); | ||||
|  | ||||
|     paths_in_use = NULL; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user