summaryrefslogtreecommitdiff
path: root/src/shadow.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shadow.c')
-rw-r--r--src/shadow.c152
1 files changed, 152 insertions, 0 deletions
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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#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