townengine/src/rendering/twn_gl_15_rendering.c

503 lines
16 KiB
C
Raw Normal View History

2024-10-07 14:53:09 +00:00
#include "twn_draw_c.h"
#include "twn_util.h"
2024-09-26 18:02:56 +00:00
#include "twn_util_c.h"
#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>
static TextureMode texture_mode_last_used = TEXTURE_MODE_UNKNOWN;
static Pipeline pipeline_last_used = PIPELINE_NO;
void start_render_frame(void) {
clear_draw_buffer();
}
void end_render_frame(void) {
if (!ctx.render_double_buffered || ctx.game.frame_number == 1) {
issue_deferred_draw_commands();
SDL_GL_SwapWindow(ctx.window);
arrsetlen(deferred_commands, 0);
restart_scratch_vertex_arrays();
} else {
/* instead of waiting for frame to finish for the swap, we continue */
/* while issuing new state for the next call, but deferring any fragment emitting calls for later */
/* actual swap will happen when next frame is fully finished, introducing a delay */
SDL_GL_SwapWindow(ctx.window);
issue_deferred_draw_commands();
restart_scratch_vertex_arrays();
glFlush();
arrsetlen(deferred_commands, 0);
}
}
void finally_use_space_pipeline(void) {
if (pipeline_last_used == PIPELINE_SPACE)
return;
static GLuint list = 0;
if (!list) {
list = glGenLists(1);
glNewList(list, GL_COMPILE); {
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glShadeModel(GL_SMOOTH);
if (GLAD_GL_ARB_depth_clamp)
glDisable(GL_DEPTH_CLAMP);
glEnable(GL_CULL_FACE);
glDepthRange(0, 1);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
/* solid white, no modulation */
glColor4ub(255, 255, 255, 255);
} glEndList();
}
glCallList(list);
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(&camera_projection_matrix.row[0].x);
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&camera_look_at_matrix.row[0].x);
texture_mode_last_used = -1;
pipeline_last_used = PIPELINE_SPACE;
}
void finally_use_2d_pipeline(void) {
if (pipeline_last_used == PIPELINE_SPACE) {
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glFlush();
}
if (pipeline_last_used == PIPELINE_2D)
return;
static GLuint list = 0;
if (!list) {
list = glGenLists(1);
glNewList(list, GL_COMPILE); {
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
glShadeModel(GL_FLAT);
/* removes near/far plane comparison and discard */
if (GLAD_GL_ARB_depth_clamp)
glEnable(GL_DEPTH_CLAMP);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
} glEndList();
}
glCallList(list);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, (double)ctx.base_render_width, (double)ctx.base_render_height, 0, 0, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
texture_mode_last_used = -1;
pipeline_last_used = PIPELINE_2D;
}
void finally_use_texture_mode(TextureMode mode) {
if (texture_mode_last_used == mode)
return;
static GLuint lists = 0;
if (!lists) {
lists = glGenLists(3);
/* ghostly */
glNewList(lists + 0, GL_COMPILE); {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthFunc(GL_LESS);
glDepthMask(GL_FALSE);
glDisable(GL_ALPHA_TEST);
} glEndList();
/* seethrough */
glNewList(lists + 1, GL_COMPILE); {
glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_EQUAL, 1.0f);
} glEndList();
/* opaque */
glNewList(lists + 2, GL_COMPILE); {
glDisable(GL_BLEND);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);
glDisable(GL_ALPHA_TEST);
} glEndList();
}
if (mode == TEXTURE_MODE_GHOSTLY) {
glCallList(lists + 0);
} else if (mode == TEXTURE_MODE_SEETHROUGH) {
glCallList(lists + 1);
} else {
glCallList(lists + 2);
}
texture_mode_last_used = mode;
}
VertexBufferBuilder build_vertex_buffer(VertexBuffer buffer, size_t bytes) {
SDL_assert(bytes != 0);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, bytes, NULL, GL_STREAM_DRAW);
void *mapping = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
if (!mapping)
CRY("build_vertex_buffer", "Error mapping a vertex array buffer");
return (VertexBufferBuilder) {
.mapping = mapping,
.bytes_left = bytes,
};
}
bool push_to_vertex_buffer_builder(VertexBufferBuilder *builder,
void const *bytes, size_t size) {
if (builder->bytes_left == 0)
return false;
2024-10-15 16:32:42 +00:00
memcpy(builder->mapping, bytes, size);
builder->bytes_left -= size;
/* trigger data send */
if (builder->bytes_left == 0) {
glUnmapBuffer(GL_ARRAY_BUFFER);
return false;
}
builder->mapping = (void *)((uintptr_t)builder->mapping + size);
return true;
}
2024-09-26 18:02:56 +00:00
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? */
glTexImage2D(target,
0,
GL_RGBA8,
surface->w, surface->h,
0,
surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB,
GL_UNSIGNED_BYTE,
surface->pixels);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
SDL_free(surface->pixels);
SDL_FreeSurface(surface);
}
void finally_render_skybox(DeferredCommandDrawSkybox command) {
2024-09-26 18:02:56 +00:00
static GLuint cubemap = 0;
static char *paths_cache = NULL;
bool loading_needed = false;
/* drop it */
if (!paths_cache || (SDL_strcmp(paths_cache, command.paths) != 0)) {
2024-09-26 18:02:56 +00:00
if (cubemap)
glDeleteTextures(1, &cubemap);
glGenTextures(1, &cubemap);
if (paths_cache)
SDL_free(paths_cache);
paths_cache = command.paths;
2024-09-26 18:02:56 +00:00
loading_needed = true;
} else
SDL_free(command.paths);
2024-09-26 18:02:56 +00:00
Matrix4 camera_look_at_matrix_solipsist = camera_look_at_matrix;
camera_look_at_matrix_solipsist.row[3].x = 0;
camera_look_at_matrix_solipsist.row[3].y = 0;
camera_look_at_matrix_solipsist.row[3].z = 0;
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf(&camera_look_at_matrix_solipsist.row[0].x);
glDisable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_CUBE_MAP);
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemap);
if (loading_needed) {
/* load all the sides */
char *expanded = expand_asterisk(command.paths, "up");
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Y);
2024-09-26 18:02:56 +00:00
SDL_free(expanded);
expanded = expand_asterisk(command.paths, "down");
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y);
2024-09-26 18:02:56 +00:00
SDL_free(expanded);
expanded = expand_asterisk(command.paths, "east");
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_X);
2024-09-26 18:02:56 +00:00
SDL_free(expanded);
expanded = expand_asterisk(command.paths, "north");
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_POSITIVE_Z);
2024-09-26 18:02:56 +00:00
SDL_free(expanded);
expanded = expand_asterisk(command.paths, "west");
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_X);
2024-09-26 18:02:56 +00:00
SDL_free(expanded);
expanded = expand_asterisk(command.paths, "south");
load_cubemap_side(expanded, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
2024-09-26 18:02:56 +00:00
SDL_free(expanded);
}
/* TODO: figure out which coordinates to use to not have issues with far z */
/* TODO: recalculate the list if far z requirement changes */
static GLuint list = 0;
if (!list) {
list = glGenLists(1);
glNewList(list, GL_COMPILE); {
/* note: assumes that space pipeline is applied already */
glDisable(GL_ALPHA_TEST);
glDepthMask(GL_FALSE);
/* removes near/far plane comparison and discard */
if (GLAD_GL_ARB_depth_clamp)
glEnable(GL_DEPTH_CLAMP);
glBegin(GL_QUADS); {
/* up */
glTexCoord3f(50.f, 50.f, 50.f);
glVertex3f(50.f, 50.f, 50.f);
glTexCoord3f(-50.f, 50.f, 50.f);
glVertex3f(-50.f, 50.f, 50.f);
glTexCoord3f(-50.f, 50.f, -50.f);
glVertex3f(-50.f, 50.f, -50.f);
glTexCoord3f(50.f, 50.f, -50.f);
glVertex3f(50.f, 50.f, -50.f);
/* down */
glTexCoord3f(50.f, -50.f, 50.f);
glVertex3f(50.f, -50.f, 50.f);
glTexCoord3f(50.f, -50.f, -50.f);
glVertex3f(50.f, -50.f, -50.f);
glTexCoord3f(-50.f, -50.f, -50.f);
glVertex3f(-50.f, -50.f, -50.f);
glTexCoord3f(-50.f, -50.f, 50.f);
glVertex3f(-50.f, -50.f, 50.f);
/* east */
glTexCoord3f(50.f, -50.f, 50.f);
glVertex3f(50.f, -50.f, 50.f);
glTexCoord3f(50.f, 50.f, 50.f);
glVertex3f(50.f, 50.f, 50.f);
glTexCoord3f(50.f, 50.f, -50.f);
glVertex3f(50.f, 50.f, -50.f);
glTexCoord3f(50.f, -50.f, -50.f);
glVertex3f(50.f, -50.f, -50.f);
/* west */
glTexCoord3f(-50.f, -50.f, 50.f);
glVertex3f(-50.f, -50.f, 50.f);
glTexCoord3f(-50.f, -50.f, -50.f);
glVertex3f(-50.f, -50.f, -50.f);
glTexCoord3f(-50.f, 50.f, -50.f);
glVertex3f(-50.f, 50.f, -50.f);
glTexCoord3f(-50.f, 50.f, 50.f);
glVertex3f(-50.f, 50.f, 50.f);
/* north */
glTexCoord3f(-50.f, -50.f, 50.f);
glVertex3f(-50.f, -50.f, 50.f);
glTexCoord3f(-50.f, 50.f, 50.f);
glVertex3f(-50.f, 50.f, 50.f);
glTexCoord3f(50.f, 50.f, 50.f);
glVertex3f(50.f, 50.f, 50.f);
glTexCoord3f(50.f, -50.f, 50.f);
glVertex3f(50.f, -50.f, 50.f);
/* south */
glTexCoord3f(-50.f, -50.f, -50.f);
glVertex3f(-50.f, -50.f, -50.f);
glTexCoord3f(50.f, -50.f, -50.f);
glVertex3f(50.f, -50.f, -50.f);
glTexCoord3f(50.f, 50.f, -50.f);
glVertex3f(50.f, 50.f, -50.f);
glTexCoord3f(-50.f, 50.f, -50.f);
glVertex3f(-50.f, 50.f, -50.f);
} glEnd();
if (GLAD_GL_ARB_depth_clamp)
glDisable(GL_DEPTH_CLAMP);
glDepthMask(GL_TRUE);
glDisable(GL_TEXTURE_CUBE_MAP);
} glEndList();
}
2024-09-26 18:02:56 +00:00
glCallList(list);
2024-09-26 18:02:56 +00:00
}
2024-10-01 13:10:36 +00:00
void finally_apply_fog(DeferredCommandApplyFog command) {
if (command.density < 0.0f || command.density > 1.0f)
2024-10-01 13:10:36 +00:00
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);
2024-10-01 13:10:36 +00:00
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;
2024-10-01 13:10:36 +00:00
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,
(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) );
2024-10-01 13:10:36 +00:00
}
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);
2025-01-05 16:46:05 +00:00
} 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);
}