From 251b611fef77533082165bab0b88eefa05b49fbe Mon Sep 17 00:00:00 2001 From: Connor Thomson Date: Sat, 12 Sep 2026 14:16:55 -0700 Subject: Add simple shadows --- src/shadow.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 src/shadow.c (limited to 'src/shadow.c') diff --git a/src/shadow.c b/src/shadow.c new file mode 100644 index 0000000..2967674 --- /dev/null +++ b/src/shadow.c @@ -0,0 +1,152 @@ +/* + * Tuxánci 2 - A first person shooter + * Copyright (C) 2025-2026 Connor Thomson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include "shadow.h" +#include "gpu.h" +#include "ta_math.h" + +#define TA_SHADOW_EXTENT 16.0f +#define TA_SHADOW_DISTANCE 30.0f +#define TA_SHADOW_NEAR 1.0f +#define TA_SHADOW_FAR 60.0f + +SDL_GPUTexture *ta_shadow_texture; +SDL_GPUSampler *ta_shadow_sampler; + +float ta_shadow_light_direction[TA_SHADOW_DIMENSION] = { -0.45f, 0.75f, 0.55f }; + +float ta_shadow_normal_offset = 0.03f; +float ta_shadow_minimum_bias = 0.0005f; +float ta_shadow_maximum_bias = 0.003f; +float ta_shadow_strength = 1.0f; + +static float ta_shadow_dot(const float *left, const float *right) { + return left[0] * right[0] + left[1] * right[1] + left[2] * right[2]; +} + +static void ta_shadow_cross(float *result, const float *left, const float *right) { + result[0] = left[1] * right[2] - left[2] * right[1]; + result[1] = left[2] * right[0] - left[0] * right[2]; + result[2] = left[0] * right[1] - left[1] * right[0]; +} + +static void ta_shadow_normalize(float *vector) { + float length = sqrtf(ta_shadow_dot(vector, vector)); + if (length > 0.0f) { + vector[0] /= length; + vector[1] /= length; + vector[2] /= length; + } +} + +void ta_shadow_init(void) { + SDL_GPUTextureCreateInfo texture_info = { + .type = SDL_GPU_TEXTURETYPE_2D, + .format = TA_SHADOW_FORMAT, + .usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER, + .width = TA_SHADOW_MAP_SIZE, + .height = TA_SHADOW_MAP_SIZE, + .layer_count_or_depth = 1, + .num_levels = 1, + .sample_count = SDL_GPU_SAMPLECOUNT_1, + }; + ta_shadow_texture = ta_sdl_create_gpu_texture(ta_gpu_device, &texture_info); + + SDL_GPUSamplerCreateInfo sampler_info = { + .min_filter = SDL_GPU_FILTER_LINEAR, + .mag_filter = SDL_GPU_FILTER_LINEAR, + .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, + .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .compare_op = SDL_GPU_COMPAREOP_LESS, + .enable_compare = true, + }; + ta_shadow_sampler = ta_sdl_create_gpu_sampler(ta_gpu_device, &sampler_info); +} + +void ta_shadow_get_light_view_projection(float *matrix) { + static const float world_up[TA_SHADOW_DIMENSION] = { 0.0f, 1.0f, 0.0f }; + static const float world_forward[TA_SHADOW_DIMENSION] = { 0.0f, 0.0f, 1.0f }; + + float direction[TA_SHADOW_DIMENSION]; + float forward[TA_SHADOW_DIMENSION]; + float right[TA_SHADOW_DIMENSION]; + float up[TA_SHADOW_DIMENSION]; + float eye[TA_SHADOW_DIMENSION]; + float view[TA_MATRIX_ELEMENTS]; + float projection[TA_MATRIX_ELEMENTS] = { 0 }; + + memcpy(direction, ta_shadow_light_direction, sizeof(direction)); + ta_shadow_normalize(direction); + + for (int index = 0; index < TA_SHADOW_DIMENSION; index++) { + eye[index] = direction[index] * TA_SHADOW_DISTANCE; + forward[index] = -direction[index]; + } + + ta_shadow_cross(right, forward, fabsf(direction[1]) > 0.999f ? world_forward : world_up); + ta_shadow_normalize(right); + ta_shadow_cross(up, right, forward); + + ta_matrix_identity(view); + view[0] = right[0]; + view[4] = right[1]; + view[8] = right[2]; + view[12] = -ta_shadow_dot(right, eye); + view[1] = up[0]; + view[5] = up[1]; + view[9] = up[2]; + view[13] = -ta_shadow_dot(up, eye); + view[2] = -forward[0]; + view[6] = -forward[1]; + view[10] = -forward[2]; + view[14] = ta_shadow_dot(forward, eye); + + projection[0] = 1.0f / TA_SHADOW_EXTENT; + projection[5] = 1.0f / TA_SHADOW_EXTENT; + projection[10] = -1.0f / (TA_SHADOW_FAR - TA_SHADOW_NEAR); + projection[14] = -TA_SHADOW_NEAR / (TA_SHADOW_FAR - TA_SHADOW_NEAR); + projection[15] = 1.0f; + + ta_matrix_multiply(matrix, projection, view); +} + +SDL_GPURenderPass *ta_shadow_begin_render_pass(SDL_GPUCommandBuffer *command_buffer) { + if (!ta_shadow_texture) { + return NULL; + } + + SDL_GPUDepthStencilTargetInfo depth_target = { + .texture = ta_shadow_texture, + .clear_depth = 1.0f, + .load_op = SDL_GPU_LOADOP_CLEAR, + .store_op = SDL_GPU_STOREOP_STORE, + .stencil_load_op = SDL_GPU_LOADOP_DONT_CARE, + .stencil_store_op = SDL_GPU_STOREOP_DONT_CARE, + }; + return ta_sdl_begin_gpu_render_pass(command_buffer, NULL, 0, &depth_target); +} + +void ta_shadow_shutdown(void) { + SDL_ReleaseGPUSampler(ta_gpu_device, ta_shadow_sampler); + SDL_ReleaseGPUTexture(ta_gpu_device, ta_shadow_texture); + ta_shadow_sampler = NULL; + ta_shadow_texture = NULL; +} \ No newline at end of file -- cgit v1.2.3