diff --git a/CMakeLists.txt b/CMakeLists.txt index 222dd2c..df202f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,8 +114,7 @@ set(TWN_NONOPT_SOURCE_FILES src/rendering/twn_triangles.c src/rendering/twn_billboards.c src/rendering/twn_circles.c - src/rendering/twn_skybox.c - src/rendering/twn_fog.c) + src/rendering/twn_skybox.c) set(TWN_SOURCE_FILES $,src/twn_amalgam.c src/twn_stb.c,${TWN_NONOPT_SOURCE_FILES}> diff --git a/apps/demos/scenery/scenes/ingame.c b/apps/demos/scenery/scenes/ingame.c index a8ba304..acb5d2b 100644 --- a/apps/demos/scenery/scenes/ingame.c +++ b/apps/demos/scenery/scenes/ingame.c @@ -222,7 +222,10 @@ static void ingame_tick(State *state) { draw_terrain(scn); draw_skybox("/assets/miramar/miramar_*.tga"); - draw_fog(0.9f, 1.0f, 0.05f, (Color){ 140, 147, 160, 255 }); + + ctx.fog_color = (Color){ 140, 147, 160, 255 }; + ctx.fog_start = 0.9f; + ctx.fog_density = 0.05f; } diff --git a/include/twn_context.h b/include/twn_context.h index 55b2412..a057c45 100644 --- a/include/twn_context.h +++ b/include/twn_context.h @@ -23,6 +23,10 @@ typedef struct Context { /* TODO: actually set it */ float frame_duration; + /* it is disabled by having fog_density approximately equal to zero */ + float fog_start, fog_end, fog_density; + Color fog_color; + /* resolution is set from config and dictates both logical and drawing space, as they're related */ /* even if scaling is done, game logic should never change over that */ Vec2 resolution; diff --git a/include/twn_draw.h b/include/twn_draw.h index e81a045..09c157b 100644 --- a/include/twn_draw.h +++ b/include/twn_draw.h @@ -88,12 +88,6 @@ TWN_API DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 p /* expects '*' masks that will be expanded to 6 names: 'up', 'down', 'east', 'west', 'north' and 'south' */ TWN_API void draw_skybox(const char *textures); -/* only one for setting is supported for a frame, any call overwrites previous */ -TWN_API void draw_fog(float start, /* optional, default: 0.0 */ - float end, /* optional, default: 1.0 */ - float density, /* optional, default: 0.0 */ - Color color); /* optional, default: all 255 */ - #ifndef TWN_NOT_C diff --git a/include/twn_texture_modes.h b/include/twn_texture_modes.h index cc07fd2..505fbad 100644 --- a/include/twn_texture_modes.h +++ b/include/twn_texture_modes.h @@ -9,6 +9,7 @@ typedef enum TextureMode { TEXTURE_MODE_OPAQUE, /* all pixels are solid */ TEXTURE_MODE_SEETHROUGH, /* some pixels are alpha zero */ TEXTURE_MODE_GHOSTLY, /* arbitrary alpha values */ + TEXTURE_MODE_COUNT, TEXTURE_MODE_UNKNOWN = -1, /* a sentinel */ } TextureMode; diff --git a/share/twn_api.json b/share/twn_api.json index 964deae..22f3b0e 100644 --- a/share/twn_api.json +++ b/share/twn_api.json @@ -213,18 +213,6 @@ { "name": "textures", "type": "char *", "default": {} } ] }, - - "draw_fog": { - "module": "draw", - "symbol": "fog", - "header": "twn_draw.h", - "params": [ - { "name": "start", "type": "float", "default": 0 }, - { "name": "end", "type": "float", "default": 1 }, - { "name": "density", "type": "float", "default": 0 }, - { "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } } - ] - } }, "types": { @@ -269,6 +257,10 @@ "fields": [ { "name": "frame_number", "type": "float" }, { "name": "frame_duration", "type": "float" }, + { "name": "fog_start", "type": "float" }, + { "name": "fog_end", "type": "float" }, + { "name": "fog_density", "type": "float" }, + { "name": "fog_color", "type": "Color" }, { "name": "resolution", "type": "Vec2" }, { "name": "mouse_position", "type": "Vec2" }, { "name": "mouse_movement", "type": "Vec2" }, diff --git a/src/rendering/twn_billboards.c b/src/rendering/twn_billboards.c index 95b01c3..eba0364 100644 --- a/src/rendering/twn_billboards.c +++ b/src/rendering/twn_billboards.c @@ -133,7 +133,7 @@ void finally_draw_billboard_batch(struct MeshBatch const *batch, command.vertices = (AttributeArrayPointer) { .arity = 3, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = offsetof(ElementIndexedBillboard, v1), .offset = offsetof(ElementIndexedBillboard, v0), .buffer = buffer @@ -141,7 +141,7 @@ void finally_draw_billboard_batch(struct MeshBatch const *batch, command.texcoords = (AttributeArrayPointer) { .arity = 2, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = offsetof(ElementIndexedBillboard, v1), .offset = offsetof(ElementIndexedBillboard, uv0), .buffer = buffer @@ -149,7 +149,7 @@ void finally_draw_billboard_batch(struct MeshBatch const *batch, command.colors = (AttributeArrayPointer) { .arity = 4, - .type = GL_UNSIGNED_BYTE, + .type = TWN_UNSIGNED_BYTE, .stride = offsetof(ElementIndexedBillboard, v1), .offset = offsetof(ElementIndexedBillboard, c0), .buffer = buffer @@ -159,14 +159,19 @@ void finally_draw_billboard_batch(struct MeshBatch const *batch, command.texture_key = texture_key; command.element_buffer = get_quad_element_buffer(); - command.element_count = 6 * (GLsizei)primitives_len; - command.range_end = 6 * (GLsizei)primitives_len; + command.element_count = 6 * (uint32_t)primitives_len; + command.range_end = 6 * (uint32_t)primitives_len; /* TODO: support alpha blended case, with distance sort */ TextureMode mode = textures_get_mode(&ctx.texture_cache, texture_key); if (mode == TEXTURE_MODE_GHOSTLY) mode = TEXTURE_MODE_SEETHROUGH; - use_texture_mode(mode); + + command.texture_mode = mode; + command.pipeline = PIPELINE_SPACE; + + command.depth_range_high = depth_range_high; + command.depth_range_low = depth_range_low; DeferredCommand final_command = { .type = DEFERRED_COMMAND_TYPE_DRAW, diff --git a/src/rendering/twn_circles.c b/src/rendering/twn_circles.c index 9d9d13f..fe70ca8 100644 --- a/src/rendering/twn_circles.c +++ b/src/rendering/twn_circles.c @@ -99,7 +99,7 @@ void render_circle(const CirclePrimitive *circle) { command.vertices = (AttributeArrayPointer) { .arity = 2, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = sizeof (Vec2), .offset = 0, .buffer = buffer @@ -112,7 +112,11 @@ void render_circle(const CirclePrimitive *circle) { command.element_count = (num_vertices - 2) * 3; command.range_end = (num_vertices - 2) * 3; - use_texture_mode(circle->color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY); + command.texture_mode = circle->color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY; + command.pipeline = PIPELINE_2D; + + command.depth_range_high = depth_range_high; + command.depth_range_low = depth_range_low; DeferredCommand final_command = { .type = DEFERRED_COMMAND_TYPE_DRAW, diff --git a/src/rendering/twn_deferred_commands.h b/src/rendering/twn_deferred_commands.h index 0616f2d..1c553b2 100644 --- a/src/rendering/twn_deferred_commands.h +++ b/src/rendering/twn_deferred_commands.h @@ -12,7 +12,7 @@ typedef enum { PIPELINE_NO, PIPELINE_SPACE, - PIPELINE_2D, + PIPELINE_2D, /* TODO: rename to PIPELINE_PLANE? */ } Pipeline; @@ -30,25 +30,27 @@ typedef struct { typedef struct { AttributeArrayPointer vertices; AttributeArrayPointer texcoords; - - bool constant_colored; union { - AttributeArrayPointer colors; + AttributeArrayPointer colors; Color color; }; - bool textured, texture_repeat, uses_gpu_key; + double depth_range_low, depth_range_high; + + Pipeline pipeline; + TextureMode texture_mode; + TextureKey texture_key; GPUTexture gpu_texture; + /* could be either `element_count` with supplied `element_buffer`, or this, but not both */ + uint32_t primitive_count; uint32_t element_buffer; uint32_t element_count; uint32_t range_start, range_end; - /* could be either `element_count` with supplied `element_buffer`, or this, but not both */ - uint32_t primitive_count; - - double depth_range_low, depth_range_high; + bool constant_colored; + bool textured, texture_repeat, uses_gpu_key; } DeferredCommandDraw; @@ -65,47 +67,17 @@ typedef struct { } DeferredCommandClear; -typedef struct { - Pipeline pipeline; -} DeferredCommandUsePipeline; - - -typedef struct { - TextureMode mode; -} DeferredCommandUseTextureMode; - - -typedef struct { - double low, high; -} DeferredCommandDepthRange; - - -typedef struct { - float start, end, density; - Color color; -} DeferredCommandApplyFog; - - typedef struct { enum DeferredCommandType { DEFERRED_COMMAND_TYPE_DRAW, DEFERRED_COMMAND_TYPE_DRAW_SKYBOX, DEFERRED_COMMAND_TYPE_CLEAR, - DEFERRED_COMMAND_TYPE_USE_PIPIELINE, - DEFERRED_COMMAND_TYPE_USE_TEXTURE_MODE, - DEFERRED_COMMAND_TYPE_DEPTH_RANGE, - DEFERRED_COMMAND_TYPE_APPLY_FOG, - DEFERRED_COMMAND_TYPE_POP_FOG, } type; union { DeferredCommandDraw draw; DeferredCommandDrawSkybox draw_skybox; DeferredCommandClear clear; - DeferredCommandUsePipeline use_pipeline; - DeferredCommandUseTextureMode use_texture_mode; - DeferredCommandDepthRange depth_range; - DeferredCommandApplyFog apply_fog; }; } DeferredCommand; diff --git a/src/rendering/twn_draw.c b/src/rendering/twn_draw.c index 35ff13d..7f0b335 100644 --- a/src/rendering/twn_draw.c +++ b/src/rendering/twn_draw.c @@ -3,7 +3,6 @@ #include "twn_engine_context_c.h" #include "twn_camera_c.h" #include "twn_types.h" -#include "twn_util_c.h" #include "twn_vec.h" #include "twn_deferred_commands.h" @@ -11,15 +10,19 @@ #include #include +#include #include DeferredCommand *deferred_commands; /* TODO: have a default initialized one */ +/* TODO: with buffered render, don't we use camera of wrong frame right now ? */ Matrix4 camera_projection_matrix; Matrix4 camera_look_at_matrix; +double depth_range_low, depth_range_high; + void render_queue_clear(void) { text_cache_reset_arena(&ctx.text_cache); @@ -205,8 +208,6 @@ TWN_API void draw_quad(char const *texture, static void render_2d(void) { - use_2d_pipeline(); - const size_t render_queue_len = arrlenu(ctx.render_queue_2d); struct Render2DInvocation { @@ -362,9 +363,6 @@ static void render_space(void) { /* nothing to do, abort */ /* as space pipeline isn't used we can have fewer changes and initialization costs */ if (hmlenu(ctx.uncolored_mesh_batches) != 0 || hmlenu(ctx.billboard_batches) != 0) { - use_space_pipeline(); - apply_fog(); - for (size_t i = 0; i < hmlenu(ctx.uncolored_mesh_batches); ++i) { finally_draw_uncolored_space_traingle_batch(&ctx.uncolored_mesh_batches[i].value, ctx.uncolored_mesh_batches[i].key); @@ -373,8 +371,6 @@ static void render_space(void) { for (size_t i = 0; i < hmlenu(ctx.billboard_batches); ++i) { finally_draw_billboard_batch(&ctx.billboard_batches[i].value, ctx.billboard_batches[i].key); } - - pop_fog(); } render_skybox(); /* after everything else, as to use depth buffer for early z rejection */ @@ -439,15 +435,8 @@ DrawCameraFromPrincipalAxesResult draw_camera_from_principal_axes(Vec3 position, 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); + depth_range_low = low; + depth_range_high = high; } @@ -468,44 +457,9 @@ void clear_draw_buffer(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); -} - - -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; @@ -521,37 +475,6 @@ void issue_deferred_draw_commands(void) { 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); } diff --git a/src/rendering/twn_draw_c.h b/src/rendering/twn_draw_c.h index 2a3c74b..1ee4d1f 100644 --- a/src/rendering/twn_draw_c.h +++ b/src/rendering/twn_draw_c.h @@ -12,27 +12,35 @@ #include #include -#ifdef EMSCRIPTEN -#include -#else -#include -#endif - #include extern Matrix4 camera_projection_matrix; extern Matrix4 camera_look_at_matrix; +extern double depth_range_low, depth_range_high; #define QUAD_ELEMENT_BUFFER_LENGTH (65536 / 6) #define CIRCLE_VERTICES_MAX 2048 +/* TODO: limit to only most necessary */ +enum { + TWN_FLOAT, + TWN_INT, + TWN_SHORT, + TWN_UNSIGNED_SHORT, + TWN_UNSIGNED_INT, + TWN_BYTE, + TWN_UNSIGNED_BYTE, +}; -typedef GLuint VertexBuffer; + +typedef uint32_t VertexBuffer; typedef struct VertexBufferBuilder { size_t bytes_left; + size_t size; void *mapping; + void *base; } VertexBufferBuilder; @@ -209,6 +217,8 @@ typedef struct ElementIndexedBillboard { } ElementIndexedBillboard; +bool render_init(void); + /* renders the background, then the primitives in all render queues */ void render(void); @@ -250,6 +260,8 @@ void text_cache_reset_arena(TextCache *cache); VertexBuffer create_vertex_buffer(void); +void restart_scratch_vertex_arrays(void); + VertexBuffer get_scratch_vertex_array(void); void delete_vertex_buffer(VertexBuffer buffer); @@ -274,7 +286,6 @@ 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); @@ -284,15 +295,6 @@ 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, VertexBuffer buffer); @@ -325,11 +327,6 @@ void finally_draw_text(FontData const *font_data, void render_skybox(void); void finally_render_skybox(DeferredCommandDrawSkybox); -void apply_fog(void); -void finally_apply_fog(DeferredCommandApplyFog); -void pop_fog(void); -void finally_pop_fog(void); - void start_render_frame(void); void end_render_frame(void); diff --git a/src/rendering/twn_fog.c b/src/rendering/twn_fog.c deleted file mode 100644 index 1d4c4a5..0000000 --- a/src/rendering/twn_fog.c +++ /dev/null @@ -1,49 +0,0 @@ -#include "twn_draw.h" -#include "twn_draw_c.h" - -#include - -#include - -static float start_cache, end_cache, density_cache; -static Color color_cache; -static bool fog_used = false; - - -void draw_fog(float start, float end, float density, Color color) { - start_cache = start; - end_cache = end; - density_cache = density; - color_cache = color; - fog_used = true; -} - - -void apply_fog(void) { - if (!fog_used) - return; - - 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); -} - - -void pop_fog(void) { - if (!fog_used) - return; - - DeferredCommand command = { - .type = DEFERRED_COMMAND_TYPE_POP_FOG, - }; - - arrpush(deferred_commands, command); -} diff --git a/src/rendering/twn_gl_15_gpu_texture.c b/src/rendering/twn_gl_15_gpu_texture.c deleted file mode 100644 index 99b4dfc..0000000 --- a/src/rendering/twn_gl_15_gpu_texture.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "twn_gpu_texture_c.h" -#include "twn_util_c.h" - - -GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps) { - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); - - if (filter == TEXTURE_FILTER_NEAREAST) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } else if (filter == TEXTURE_FILTER_LINEAR) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - -#if !defined(EMSCRIPTEN) - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); -#endif - - glBindTexture(GL_TEXTURE_2D, 0); - return texture; -} - - -void delete_gpu_texture(GPUTexture texture) { - glDeleteTextures(1, &texture); -} - - -void upload_gpu_texture(GPUTexture texture, void *pixels, int channels, int width, int height) { - glBindTexture(GL_TEXTURE_2D, texture); - - int format_internal, format; - if (channels == 4) { - format_internal = GL_RGBA8; - format = GL_RGBA; - } else if (channels == 3) { - format_internal = GL_RGBA8; - format = GL_RGB; - } else if (channels == 1) { - format_internal = GL_ALPHA; - format = GL_ALPHA; - } else { - CRY("upload_gpu_texture", "Unsupported channel count"); - return; - } - - glTexImage2D(GL_TEXTURE_2D, - 0, - format_internal, - width, - height, - 0, - format, - GL_UNSIGNED_BYTE, - pixels); - - glBindTexture(GL_TEXTURE_2D, 0); -} - - -void bind_gpu_texture(GPUTexture texture) { - glBindTexture(GL_TEXTURE_2D, texture); -} diff --git a/src/rendering/twn_gl_15_rendering.c b/src/rendering/twn_gl_15_rendering.c index 2e9207f..da3db66 100644 --- a/src/rendering/twn_gl_15_rendering.c +++ b/src/rendering/twn_gl_15_rendering.c @@ -4,7 +4,6 @@ #include "twn_engine_context_c.h" #include "twn_types.h" #include "twn_deferred_commands.h" -#include "twn_gl_any_rendering_c.h" #include #include @@ -14,8 +13,49 @@ static TextureMode texture_mode_last_used = TEXTURE_MODE_UNKNOWN; static Pipeline pipeline_last_used = PIPELINE_NO; +static void APIENTRY opengl_log(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) +{ + (void)source; + (void)type; + (void)id; + (void)severity; + (void)userParam; + + log_info("OpenGL: %.*s\n", length, message); +} + + +bool render_init(void) { + if (gladLoadGL() == 0) { + CRY("Init", "GLAD failed"); + return false; + } + + log_info("OpenGL context: %s\n", glGetString(GL_VERSION)); + + glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST); + glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); + glHint(GL_FOG_HINT, GL_FASTEST); + + /* hook up opengl debugging callback */ + if (ctx.game.debug) { + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(opengl_log, NULL); + } + + return true; +} + + void start_render_frame(void) { clear_draw_buffer(); + pipeline_last_used = PIPELINE_NO; } @@ -38,10 +78,13 @@ void end_render_frame(void) { } -void finally_use_space_pipeline(void) { +static void finally_use_space_pipeline(void) { if (pipeline_last_used == PIPELINE_SPACE) return; + depth_range_high = 1.0; + depth_range_low = 0.0; + static GLuint list = 0; if (!list) { list = glGenLists(1); @@ -54,9 +97,9 @@ void finally_use_space_pipeline(void) { glDisable(GL_DEPTH_CLAMP); glEnable(GL_CULL_FACE); - glDepthRange(0, 1); glEnable(GL_TEXTURE_2D); glActiveTexture(GL_TEXTURE0); + glEnable(GL_DEPTH_TEST); /* solid white, no modulation */ glColor4ub(255, 255, 255, 255); @@ -76,7 +119,7 @@ void finally_use_space_pipeline(void) { } -void finally_use_2d_pipeline(void) { +static void finally_use_2d_pipeline(void) { if (pipeline_last_used == PIPELINE_SPACE) { glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glFlush(); @@ -111,6 +154,7 @@ void finally_use_2d_pipeline(void) { glOrtho(0, (double)ctx.base_render_width, (double)ctx.base_render_height, 0, 0, 1); glMatrixMode(GL_MODELVIEW); + /* TODO: 2d camera */ glLoadIdentity(); texture_mode_last_used = -1; @@ -118,7 +162,7 @@ void finally_use_2d_pipeline(void) { } -void finally_use_texture_mode(TextureMode mode) { +static void finally_use_texture_mode(TextureMode mode) { if (texture_mode_last_used == mode) return; @@ -248,6 +292,9 @@ void finally_render_skybox(DeferredCommandDrawSkybox command) { glMatrixMode(GL_MODELVIEW); glLoadMatrixf(&camera_look_at_matrix_solipsist.row[0].x); + glDepthRange(0.0f, 1.0f); + glDisable(GL_FOG); + glDisable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_CUBE_MAP); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap); @@ -370,38 +417,6 @@ void finally_render_skybox(DeferredCommandDrawSkybox command) { } -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, command.density); - glFogf(GL_FOG_START, command.start); - glFogf(GL_FOG_END, command.end); - - float color_conv[4]; - 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) { - glDisable(GL_FOG); -} - - -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, @@ -418,18 +433,64 @@ void finally_clear_draw_buffer(DeferredCommandClear command) { } +static GLsizei to_gl_type_enum(int value) { + switch (value) { + case TWN_FLOAT: return GL_FLOAT; + case TWN_INT: return GL_INT; + case TWN_SHORT: return GL_SHORT; + case TWN_UNSIGNED_SHORT: return GL_UNSIGNED_SHORT; + case TWN_UNSIGNED_BYTE: return GL_UNSIGNED_BYTE; + case TWN_BYTE: return GL_BYTE; + default: + CRY("to_gl_type_enum", "Unknown primitive type"); + return -1; + } +} + + 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); + /* TODO: cache previous setting, don't recommit */ + glDepthRange(command.depth_range_low, command.depth_range_high); + + /* TODO: cache it for constant parameters, which is a common case */ + if (fabsf(0.0f - ctx.game_copy.fog_density) >= 0.00001f) { + glEnable(GL_FOG); + + /* clamp to valid range */ + ctx.game_copy.fog_density = clampf(ctx.game_copy.fog_density, 0.0, 1.0); + + glFogf(GL_FOG_DENSITY, ctx.game_copy.fog_density); + glFogf(GL_FOG_START, ctx.game_copy.fog_start); + glFogf(GL_FOG_END, ctx.game_copy.fog_end); + + float color_conv[4]; + color_conv[0] = (float)ctx.game_copy.fog_color.r / UINT8_MAX; + color_conv[1] = (float)ctx.game_copy.fog_color.g / UINT8_MAX; + color_conv[2] = (float)ctx.game_copy.fog_color.b / UINT8_MAX; + color_conv[3] = (float)ctx.game_copy.fog_color.a / UINT8_MAX; + + glFogfv(GL_FOG_COLOR, color_conv); + } else + glDisable(GL_FOG); + + if (command.pipeline == PIPELINE_SPACE) + finally_use_space_pipeline(); + else + finally_use_2d_pipeline(); + + finally_use_texture_mode(command.texture_mode); + 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, + to_gl_type_enum(command.vertices.type), command.vertices.stride, (void *)command.vertices.offset); @@ -439,7 +500,7 @@ void finally_draw_command(DeferredCommandDraw command) { glEnableClientState(GL_TEXTURE_COORD_ARRAY); glClientActiveTexture(GL_TEXTURE0); glTexCoordPointer(command.texcoords.arity, - command.texcoords.type, + to_gl_type_enum(command.texcoords.type), command.texcoords.stride, (void *)command.texcoords.offset); } @@ -449,7 +510,7 @@ void finally_draw_command(DeferredCommandDraw command) { glEnableClientState(GL_COLOR_ARRAY); glColorPointer(command.colors.arity, - command.colors.type, + to_gl_type_enum(command.colors.type), command.colors.stride, (void *)command.colors.offset); diff --git a/src/rendering/twn_gl_any_rendering.c b/src/rendering/twn_gl_any_rendering.c index f228995..8010d6f 100644 --- a/src/rendering/twn_gl_any_rendering.c +++ b/src/rendering/twn_gl_any_rendering.c @@ -1,6 +1,6 @@ -#include "twn_gl_any_rendering_c.h" -#include "twn_draw_c.h" #include "twn_engine_context_c.h" +#include "twn_util_c.h" +#include "twn_draw_c.h" #include @@ -120,3 +120,85 @@ GLuint get_scratch_vertex_array(void) { (*used)++; return (*current_scratch_vertex_array)[*used - 1]; } + + +GPUTexture create_gpu_texture(TextureFilter filter, bool generate_mipmaps) { + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + +#if !defined(EMSCRIPTEN) + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, (GLboolean)generate_mipmaps); +#else + if (generate_mipmaps) + glGenerateMipmap(GL_TEXTURE_2D); +#endif + + if (filter == TEXTURE_FILTER_NEAREAST) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } else if (filter == TEXTURE_FILTER_LINEAR) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + +#if !defined(EMSCRIPTEN) + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); +#endif + + glBindTexture(GL_TEXTURE_2D, 0); + return texture; +} + + +void delete_gpu_texture(GPUTexture texture) { + glDeleteTextures(1, &texture); +} + + +void upload_gpu_texture(GPUTexture texture, void *pixels, int channels, int width, int height) { + glBindTexture(GL_TEXTURE_2D, texture); + + int format_internal, format; + if (channels == 4) { + #ifdef EMSCRIPTEN + format_internal = GL_RGBA; + #else + format_internal = GL_RGBA8; + #endif + format = GL_RGBA; + } else if (channels == 3) { + #ifdef EMSCRIPTEN + format_internal = GL_RGBA; + #else + format_internal = GL_RGBA8; + #endif + format = GL_RGB; + } else if (channels == 1) { + format_internal = GL_ALPHA; + format = GL_ALPHA; + } else { + CRY("upload_gpu_texture", "Unsupported channel count"); + return; + } + + glTexImage2D(GL_TEXTURE_2D, + 0, + format_internal, + width, + height, + 0, + format, + GL_UNSIGNED_BYTE, + pixels); + + glBindTexture(GL_TEXTURE_2D, 0); +} + + +void bind_gpu_texture(GPUTexture texture) { + glBindTexture(GL_TEXTURE_2D, texture); +} diff --git a/src/rendering/twn_gl_any_rendering_c.h b/src/rendering/twn_gl_any_rendering_c.h deleted file mode 100644 index 74ec20e..0000000 --- a/src/rendering/twn_gl_any_rendering_c.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef TWN_GL_ANY_RENDERING_C_H -#define TWN_GL_ANY_RENDERING_C_H - -#ifdef EMSCRIPTEN -#include -#else -#include -#endif - -#include - -void restart_scratch_vertex_arrays(void); - -GLuint get_scratch_vertex_array(void); - -#endif diff --git a/src/rendering/twn_gpu_texture_c.h b/src/rendering/twn_gpu_texture_c.h index 5ccacbf..962f41a 100644 --- a/src/rendering/twn_gpu_texture_c.h +++ b/src/rendering/twn_gpu_texture_c.h @@ -2,8 +2,9 @@ #define TWN_GPU_TEXTURE_C_H #include +#include -typedef GLuint GPUTexture; +typedef uint32_t GPUTexture; typedef enum TextureFilter { TEXTURE_FILTER_NEAREAST, diff --git a/src/rendering/twn_quads.c b/src/rendering/twn_quads.c index 8869ee1..79306b7 100644 --- a/src/rendering/twn_quads.c +++ b/src/rendering/twn_quads.c @@ -11,7 +11,7 @@ void finally_render_quads(const Primitive2D primitives[], { DeferredCommandDraw command = {0}; - GLsizei off = 0, voff = 0, uvoff = 0, coff = 0; + uint32_t off = 0, voff = 0, uvoff = 0, coff = 0; if (!batch.constant_colored && batch.textured) { off = offsetof(ElementIndexedQuad, v1); @@ -33,7 +33,7 @@ void finally_render_quads(const Primitive2D primitives[], command.vertices = (AttributeArrayPointer) { .arity = 2, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = off, .offset = voff, .buffer = buffer @@ -42,7 +42,7 @@ void finally_render_quads(const Primitive2D primitives[], if (batch.textured) command.texcoords = (AttributeArrayPointer) { .arity = 2, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = off, .offset = uvoff, .buffer = buffer @@ -51,7 +51,7 @@ void finally_render_quads(const Primitive2D primitives[], if (!batch.constant_colored) { command.colors = (AttributeArrayPointer) { .arity = 4, - .type = GL_UNSIGNED_BYTE, + .type = TWN_UNSIGNED_BYTE, .stride = off, .offset = coff, .buffer = buffer @@ -68,10 +68,14 @@ void finally_render_quads(const Primitive2D primitives[], } command.element_buffer = get_quad_element_buffer(); - command.element_count = 6 * (GLsizei)batch.size; - command.range_end = 6 * (GLsizei)batch.size; + command.element_count = 6 * (uint32_t)batch.size; + command.range_end = 6 * (uint32_t)batch.size; - use_texture_mode(batch.mode); + command.texture_mode = batch.mode; + command.pipeline = PIPELINE_2D; + + command.depth_range_high = depth_range_high; + command.depth_range_low = depth_range_low; DeferredCommand final_command = { .type = DEFERRED_COMMAND_TYPE_DRAW, diff --git a/src/rendering/twn_rects.c b/src/rendering/twn_rects.c index 829c170..cf0acad 100644 --- a/src/rendering/twn_rects.c +++ b/src/rendering/twn_rects.c @@ -70,8 +70,6 @@ void render_rect_batch(const Primitive2D primitives[], /* single vertex array is used for every batch with NULL glBufferData() trick at the end */ VertexBuffer const vertex_array = get_scratch_vertex_array(); - use_texture_mode(batch.mode); - /* vertex population over a vertex buffer builder interface */ { VertexBufferBuilder payload = build_vertex_buffer(vertex_array, get_quad_payload_size(batch) * batch.size); diff --git a/src/rendering/twn_skybox.c b/src/rendering/twn_skybox.c index 40a26e3..590edc2 100644 --- a/src/rendering/twn_skybox.c +++ b/src/rendering/twn_skybox.c @@ -21,8 +21,6 @@ void render_skybox(void) { if (!paths_in_use) return; - use_space_pipeline(); - /* note: ownership of 'paths_in_use' goes there */ DeferredCommand command = { .type = DEFERRED_COMMAND_TYPE_DRAW_SKYBOX, diff --git a/src/rendering/twn_text.c b/src/rendering/twn_text.c index f734c24..2239285 100644 --- a/src/rendering/twn_text.c +++ b/src/rendering/twn_text.c @@ -351,7 +351,7 @@ void finally_draw_text(FontData const *font_data, command.vertices = (AttributeArrayPointer) { .arity = 2, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = offsetof(ElementIndexedQuadWithoutColor, v1), .offset = offsetof(ElementIndexedQuadWithoutColor, v0), .buffer = buffer @@ -359,7 +359,7 @@ void finally_draw_text(FontData const *font_data, command.texcoords = (AttributeArrayPointer) { .arity = 2, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = offsetof(ElementIndexedQuadWithoutColor, v1), .offset = offsetof(ElementIndexedQuadWithoutColor, uv0), .buffer = buffer @@ -373,10 +373,14 @@ void finally_draw_text(FontData const *font_data, command.textured = true; command.element_buffer = get_quad_element_buffer(); - command.element_count = 6 * (GLsizei)len; - command.range_end = 6 * (GLsizei)len; + command.element_count = 6 * (uint32_t)len; + command.range_end = 6 * (uint32_t)len; - use_texture_mode(TEXTURE_MODE_GHOSTLY); + command.texture_mode = TEXTURE_MODE_GHOSTLY; + command.pipeline = PIPELINE_2D; + + command.depth_range_high = depth_range_high; + command.depth_range_low = depth_range_low; DeferredCommand final_command = { .type = DEFERRED_COMMAND_TYPE_DRAW, diff --git a/src/rendering/twn_triangles.c b/src/rendering/twn_triangles.c index 7687c0a..873b069 100644 --- a/src/rendering/twn_triangles.c +++ b/src/rendering/twn_triangles.c @@ -87,7 +87,7 @@ void finally_draw_uncolored_space_traingle_batch(const MeshBatch *batch, command.vertices = (AttributeArrayPointer) { .arity = 3, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = offsetof(UncoloredSpaceTriangle, v1), .offset = offsetof(UncoloredSpaceTriangle, v0), .buffer = buffer @@ -95,7 +95,7 @@ void finally_draw_uncolored_space_traingle_batch(const MeshBatch *batch, command.texcoords = (AttributeArrayPointer) { .arity = 2, - .type = GL_FLOAT, + .type = TWN_FLOAT, .stride = offsetof(UncoloredSpaceTriangle, v1), .offset = offsetof(UncoloredSpaceTriangle, uv0), .buffer = buffer @@ -104,13 +104,18 @@ void finally_draw_uncolored_space_traingle_batch(const MeshBatch *batch, command.textured = true; command.texture_key = texture_key; - command.primitive_count = (GLsizei)(3 * primitives_len); + command.primitive_count = (uint32_t)(3 * primitives_len); /* TODO: support alpha blended case, with distance sort */ TextureMode mode = textures_get_mode(&ctx.texture_cache, texture_key); if (mode == TEXTURE_MODE_GHOSTLY) mode = TEXTURE_MODE_SEETHROUGH; - use_texture_mode(mode); + + command.texture_mode = mode; + command.pipeline = PIPELINE_SPACE; + + command.depth_range_high = depth_range_high; + command.depth_range_low = depth_range_low; DeferredCommand final_command = { .type = DEFERRED_COMMAND_TYPE_DRAW, diff --git a/src/twn_amalgam.c b/src/twn_amalgam.c index 1a2fb99..f8e0613 100644 --- a/src/twn_amalgam.c +++ b/src/twn_amalgam.c @@ -12,7 +12,6 @@ #include "rendering/twn_circles.c" #include "rendering/twn_draw.c" -#include "rendering/twn_fog.c" #include "rendering/twn_skybox.c" #include "rendering/twn_sprites.c" #include "rendering/twn_rects.c" diff --git a/src/twn_loop.c b/src/twn_loop.c index fb7d224..a6bc47f 100644 --- a/src/twn_loop.c +++ b/src/twn_loop.c @@ -12,13 +12,6 @@ #include #include -/* TODO: should not be used here directly */ -#ifdef EMSCRIPTEN -#include -#else -#include -#endif - #include #include @@ -100,28 +93,6 @@ static void poll_events(void) { } -#ifndef EMSCRIPTEN - -static void APIENTRY opengl_log(GLenum source, - GLenum type, - GLuint id, - GLenum severity, - GLsizei length, - const GLchar* message, - const void* userParam) -{ - (void)source; - (void)type; - (void)id; - (void)severity; - (void)userParam; - - log_info("OpenGL: %.*s\n", length, message); -} - -#endif - - static void preserve_persistent_ctx_fields(void) { ctx.game.udata = ctx.game_copy.udata; } @@ -539,25 +510,13 @@ static bool initialize(void) { if (SDL_GL_SetSwapInterval(-1)) SDL_GL_SetSwapInterval(1); -#ifndef EMSCRIPTEN - if (gladLoadGL() == 0) { - CRY("Init", "GLAD failed"); + if (!render_init()) goto fail; - } -#endif - - log_info("OpenGL context: %s\n", glGetString(GL_VERSION)); - -#ifndef EMSCRIPTEN - glHint(GL_TEXTURE_COMPRESSION_HINT, GL_NICEST); - glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); - glHint(GL_FOG_HINT, GL_FASTEST); -#endif /* might need this to have multiple windows */ ctx.window_id = SDL_GetWindowID(ctx.window); - glViewport(0, 0, (GLsizei)ctx.base_render_width, (GLsizei)ctx.base_render_height); + setup_viewport(0, 0, (int)ctx.base_render_width, (int)ctx.base_render_height); /* TODO: */ // SDL_GetRendererOutputSize(ctx.renderer, &ctx.window_w, &ctx.window_h); @@ -596,14 +555,6 @@ static bool initialize(void) { /* you could change this at runtime if you wanted */ ctx.update_multiplicity = 1; -#ifndef EMSCRIPTEN - /* hook up opengl debugging callback */ - if (ctx.game.debug) { - glEnable(GL_DEBUG_OUTPUT); - glDebugMessageCallback(opengl_log, NULL); - } -#endif - /* random seeding */ /* SDL_GetPerformanceCounter returns some platform-dependent number. */ /* it should vary between game instances. i checked! random enough for me. */ @@ -718,6 +669,9 @@ static bool initialize(void) { ctx.render_double_buffered = true; ctx.window_mouse_resident = true; + ctx.game.fog_color = (Color){ 255, 255, 255, 255 }; /* TODO: pick some grey? */ + ctx.game.fog_end = 1.0f; + return true; fail: diff --git a/src/twn_main.c b/src/twn_main.c index d048247..c892cd5 100644 --- a/src/twn_main.c +++ b/src/twn_main.c @@ -1,6 +1,8 @@ #include "twn_loop.h" +#ifndef EMSCRIPTEN #define SDL_MAIN_HANDLED +#endif #include