big rendering overhaul (cleaning and api abstraction)

This commit is contained in:
veclavtalica 2025-01-14 23:20:54 +03:00
parent b7cb37c06a
commit 5059802d09
25 changed files with 290 additions and 424 deletions

View File

@ -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
$<IF:$<BOOL:${TWN_USE_AMALGAM}>,src/twn_amalgam.c src/twn_stb.c,${TWN_NONOPT_SOURCE_FILES}>

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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" },

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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 <stb_ds.h>
#include <stddef.h>
#include <math.h>
#include <tgmath.h>
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);
}

View File

@ -12,27 +12,35 @@
#include <SDL2/SDL.h>
#include <stb_truetype.h>
#ifdef EMSCRIPTEN
#include <GLES2/gl2.h>
#else
#include <glad/glad.h>
#endif
#include <stdbool.h>
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);

View File

@ -1,49 +0,0 @@
#include "twn_draw.h"
#include "twn_draw_c.h"
#include <stb_ds.h>
#include <stdbool.h>
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);
}

View File

@ -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);
}

View File

@ -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 <glad/glad.h>
#include <stb_ds.h>
@ -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);

View File

@ -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 <stb_ds.h>
@ -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);
}

View File

@ -1,16 +0,0 @@
#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

View File

@ -2,8 +2,9 @@
#define TWN_GPU_TEXTURE_C_H
#include <stdbool.h>
#include <stdint.h>
typedef GLuint GPUTexture;
typedef uint32_t GPUTexture;
typedef enum TextureFilter {
TEXTURE_FILTER_NEAREAST,

View File

@ -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,

View File

@ -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);

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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"

View File

@ -12,13 +12,6 @@
#include <stb_ds.h>
#include <toml.h>
/* TODO: should not be used here directly */
#ifdef EMSCRIPTEN
#include <GLES2/gl2.h>
#else
#include <glad/glad.h>
#endif
#include <stdbool.h>
#include <limits.h>
@ -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:

View File

@ -1,6 +1,8 @@
#include "twn_loop.h"
#ifndef EMSCRIPTEN
#define SDL_MAIN_HANDLED
#endif
#include <SDL2/SDL.h>