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/gpu.c | 7 ++ src/main.c | 3 + src/model.c | 179 ++++++++++++++++++++++++++++++++++-------------- src/screen.c | 10 +++ src/screens/main_menu.c | 4 ++ src/shadow.c | 152 ++++++++++++++++++++++++++++++++++++++++ src/ta_math.c | 18 +++++ src/teapot.c | 5 ++ 8 files changed, 328 insertions(+), 50 deletions(-) create mode 100644 src/shadow.c (limited to 'src') diff --git a/src/gpu.c b/src/gpu.c index b8c0c7d..e6f83ce 100644 --- a/src/gpu.c +++ b/src/gpu.c @@ -25,6 +25,7 @@ #include "text.h" #include "logging.h" #include "screen.h" +#include "shadow.h" #define SPIRV_ENTRY_POINT "main" @@ -361,6 +362,12 @@ void ta_gpu_end_frame(void) { ta_gpu_upload_text(ta_gpu_command_buffer); ta_screen_upload(ta_gpu_command_buffer); + SDL_GPURenderPass *shadow_pass = ta_shadow_begin_render_pass(ta_gpu_command_buffer); + if (shadow_pass) { + ta_screen_render_shadow(ta_gpu_command_buffer, shadow_pass); + SDL_EndGPURenderPass(shadow_pass); + } + SDL_GPUColorTargetInfo color_target = { .texture = ta_gpu_msaa_texture, .load_op = SDL_GPU_LOADOP_CLEAR, diff --git a/src/main.c b/src/main.c index 4edac13..adbc5cc 100644 --- a/src/main.c +++ b/src/main.c @@ -35,6 +35,7 @@ #include "teapot.h" #include "mouse.h" #include "camera.h" +#include "shadow.h" static void ta_main_init(void) { ta_color_init(); @@ -44,6 +45,7 @@ static void ta_main_init(void) { ta_sdl_ttf_init(); ta_text_init(); ta_gpu_init(); + ta_shadow_init(); ta_teapot_init(); #ifdef ENABLE_AUDIO ta_audio_init(); @@ -71,6 +73,7 @@ void ta_main_exit(int code) { #endif // ENABLE_AUDIO ta_text_shutdown(); ta_teapot_shutdown(); + ta_shadow_shutdown(); ta_window_shutdown(); ta_sdl_ttf_shutdown(); ta_sdl_shutdown(); diff --git a/src/model.c b/src/model.c index 4171c21..2c78850 100644 --- a/src/model.c +++ b/src/model.c @@ -23,6 +23,7 @@ #include "camera.h" #include "files.h" #include "gpu.h" +#include "shadow.h" #include "ta_math.h" static bool ta_model_push_vertex(ta_model_mesh *mesh, size_t *capacity, const ta_model_vertex *vertex) { @@ -131,41 +132,39 @@ void ta_model_free(ta_model_mesh *mesh) { } typedef struct ta_model_uniforms { - float mvp[16]; // TODO: Use dynamic buffer - float model[16]; // TODO: Use dynamic buffer + float mvp[TA_MATRIX_ELEMENTS]; + float model[TA_MATRIX_ELEMENTS]; + float light_view_projection[TA_MATRIX_ELEMENTS]; float camera_position[TA_MODEL_DIMENSION]; - float padding; + float shadow_normal_offset; } ta_model_uniforms; -static SDL_GPUShader *ta_model_shader(SDL_GPUDevice *device, const void *code, size_t size, SDL_GPUShaderStage stage) { +typedef struct ta_model_light_uniforms { + float direction[TA_MODEL_DIMENSION]; + float shadow_texel_size; + float shadow_minimum_bias; + float shadow_maximum_bias; + float shadow_strength; + float padding; +} ta_model_light_uniforms; + +typedef struct ta_model_shadow_uniforms { + float light_model_view_projection[TA_MATRIX_ELEMENTS]; +} ta_model_shadow_uniforms; + +static SDL_GPUShader *ta_model_shader(SDL_GPUDevice *device, const void *code, size_t size, SDL_GPUShaderStage stage, Uint32 num_samplers, Uint32 num_uniform_buffers) { SDL_GPUShaderCreateInfo info = { .code = code, .code_size = size, .entrypoint = "main", .format = SDL_GPU_SHADERFORMAT_SPIRV, .stage = stage, - .num_uniform_buffers = stage == SDL_GPU_SHADERSTAGE_VERTEX ? 1 : 0, + .num_samplers = num_samplers, + .num_uniform_buffers = num_uniform_buffers, }; return ta_sdl_create_gpu_shader(device, &info); } -static void ta_model_identity(float *matrix) { - for (int index = 0; index < 16; index++) { - matrix[index] = index % 5 == 0 ? 1.0f : 0.0f; - } -} - -static void ta_model_multiply(float *result, const float *left, const float *right) { - float product[16]; - for (int column = 0; column < 4; column++) for (int row = 0; row < 4; row++) { - product[column * 4 + row] = 0.0f; - for (int inner = 0; inner < 4; inner++) { - product[column * 4 + row] += left[inner * 4 + row] * right[column * 4 + inner]; - } - } - memcpy(result, product, sizeof(product)); -} - bool ta_model_init(ta_model *model, SDL_GPUDevice *device, const float positions[][TA_MODEL_DIMENSION], size_t position_count, const unsigned int faces[][TA_MODEL_DIMENSION], size_t face_count, SDL_GPUTextureFormat color_format, SDL_GPUTextureFormat depth_format, SDL_GPUSampleCount sample_count) { memset(model, 0, sizeof(*model)); if (!ta_model_build(positions, position_count, faces, face_count, &model->mesh) || model->mesh.vertex_count == 0) { @@ -182,8 +181,8 @@ bool ta_model_init(ta_model *model, SDL_GPUDevice *device, const float positions return false; } model->vertex_count = (Uint32)model->mesh.vertex_count; - SDL_GPUShader *vertex_shader = ta_model_shader(device, file_model_vert_slang_spv_start, file_model_vert_slang_spv_size, SDL_GPU_SHADERSTAGE_VERTEX); - SDL_GPUShader *fragment_shader = ta_model_shader(device, file_model_frag_slang_spv_start, file_model_frag_slang_spv_size, SDL_GPU_SHADERSTAGE_FRAGMENT); + SDL_GPUShader *vertex_shader = ta_model_shader(device, file_model_vert_slang_spv_start, file_model_vert_slang_spv_size, SDL_GPU_SHADERSTAGE_VERTEX, 0, 1); + SDL_GPUShader *fragment_shader = ta_model_shader(device, file_model_frag_slang_spv_start, file_model_frag_slang_spv_size, SDL_GPU_SHADERSTAGE_FRAGMENT, 1, 1); SDL_GPUVertexBufferDescription vertex_buffer = {.slot = 0, .pitch = sizeof(ta_model_vertex), .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX}; SDL_GPUVertexAttribute attributes[2] = { { @@ -232,15 +231,48 @@ bool ta_model_init(ta_model *model, SDL_GPUDevice *device, const float positions }; model->pipeline = ta_sdl_create_gpu_graphics_pipeline(device, &pipeline_info); - SDL_GPUShader *wireframe_fragment_shader = ta_model_shader(device, file_model_wireframe_frag_slang_spv_start, file_model_wireframe_frag_slang_spv_size, SDL_GPU_SHADERSTAGE_FRAGMENT); + SDL_GPUShader *wireframe_fragment_shader = ta_model_shader(device, file_model_wireframe_frag_slang_spv_start, file_model_wireframe_frag_slang_spv_size, SDL_GPU_SHADERSTAGE_FRAGMENT, 0, 0); pipeline_info.fragment_shader = wireframe_fragment_shader; pipeline_info.rasterizer_state.fill_mode = SDL_GPU_FILLMODE_LINE; model->wireframe_pipeline = ta_sdl_create_gpu_graphics_pipeline(device, &pipeline_info); + SDL_GPUShader *shadow_vertex_shader = ta_model_shader(device, file_shadow_vert_slang_spv_start, file_shadow_vert_slang_spv_size, SDL_GPU_SHADERSTAGE_VERTEX, 0, 1); + SDL_GPUShader *shadow_fragment_shader = ta_model_shader(device, file_shadow_frag_slang_spv_start, file_shadow_frag_slang_spv_size, SDL_GPU_SHADERSTAGE_FRAGMENT, 0, 0); + SDL_GPUGraphicsPipelineCreateInfo shadow_pipeline_info = { + .vertex_shader = shadow_vertex_shader, + .fragment_shader = shadow_fragment_shader, + .vertex_input_state = { + .vertex_buffer_descriptions = &vertex_buffer, + .num_vertex_buffers = 1, + .vertex_attributes = attributes, + .num_vertex_attributes = 1 + }, + .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, + .rasterizer_state = { + .cull_mode = SDL_GPU_CULLMODE_NONE + }, + .depth_stencil_state = { + .compare_op = SDL_GPU_COMPAREOP_LESS, + .enable_depth_test = true, + .enable_depth_write = true + }, + .multisample_state = { + .sample_count = SDL_GPU_SAMPLECOUNT_1 + }, + .target_info = { + .num_color_targets = 0, + .depth_stencil_format = TA_SHADOW_FORMAT, + .has_depth_stencil_target = true + }, + }; + model->shadow_pipeline = ta_sdl_create_gpu_graphics_pipeline(device, &shadow_pipeline_info); + SDL_ReleaseGPUShader(device, vertex_shader); SDL_ReleaseGPUShader(device, fragment_shader); SDL_ReleaseGPUShader(device, wireframe_fragment_shader); - return model->pipeline != NULL && model->wireframe_pipeline != NULL; + SDL_ReleaseGPUShader(device, shadow_vertex_shader); + SDL_ReleaseGPUShader(device, shadow_fragment_shader); + return model->pipeline != NULL && model->wireframe_pipeline != NULL && model->shadow_pipeline != NULL; } void ta_model_upload(ta_model *model, SDL_GPUCommandBuffer *command_buffer, SDL_GPUDevice *device) { @@ -271,44 +303,48 @@ void ta_model_upload(ta_model *model, SDL_GPUCommandBuffer *command_buffer, SDL_ model->uploaded = true; } -void ta_model_render(const ta_model *model, SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass, int window_width, int window_height) { - if (!model->pipeline || !model->vertex_buffer || !model->uploaded) { - return; - } - ta_model_uniforms uniforms; - float view[16]; - float projection[16] = {0}; - float view_model[16]; - float rotation_x[16]; - float rotation_y[16]; - float rotation_z[16]; - float rotation_xy[16]; +static void ta_model_get_model_matrix(const ta_model *model, float *matrix) { + float rotation_x[TA_MATRIX_ELEMENTS]; + float rotation_y[TA_MATRIX_ELEMENTS]; + float rotation_z[TA_MATRIX_ELEMENTS]; + float rotation_xy[TA_MATRIX_ELEMENTS]; float cosine = cosf(model->rotation[0]); float sine = sinf(model->rotation[0]); - ta_model_identity(rotation_x); + ta_matrix_identity(rotation_x); rotation_x[5] = cosine; rotation_x[6] = sine; rotation_x[9] = -sine; rotation_x[10] = cosine; cosine = cosf(model->rotation[1]); sine = sinf(model->rotation[1]); - ta_model_identity(rotation_y); + ta_matrix_identity(rotation_y); rotation_y[0] = cosine; rotation_y[2] = -sine; rotation_y[8] = sine; rotation_y[10] = cosine; cosine = cosf(model->rotation[2]); sine = sinf(model->rotation[2]); - ta_model_identity(rotation_z); + ta_matrix_identity(rotation_z); rotation_z[0] = cosine; rotation_z[1] = sine; rotation_z[4] = -sine; rotation_z[5] = cosine; - ta_model_multiply(rotation_xy, rotation_y, rotation_x); - ta_model_multiply(uniforms.model, rotation_z, rotation_xy); - uniforms.model[12] = model->position[0]; - uniforms.model[13] = model->position[1]; - uniforms.model[14] = model->position[2]; + ta_matrix_multiply(rotation_xy, rotation_y, rotation_x); + ta_matrix_multiply(matrix, rotation_z, rotation_xy); + matrix[12] = model->position[0]; + matrix[13] = model->position[1]; + matrix[14] = model->position[2]; +} + +void ta_model_render(const ta_model *model, SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass, int window_width, int window_height) { + if (!model->pipeline || !model->vertex_buffer || !model->uploaded) { + return; + } + ta_model_uniforms uniforms; + float view[TA_MATRIX_ELEMENTS]; + float projection[TA_MATRIX_ELEMENTS] = {0}; + float view_model[TA_MATRIX_ELEMENTS]; + ta_model_get_model_matrix(model, uniforms.model); float aspect = (float)window_width / (float)window_height; float focal_length = 1.0f / tanf(ta_rad(ta_camera_current.fov * 0.5f)); float near_plane = 0.1f; @@ -319,15 +355,57 @@ void ta_model_render(const ta_model *model, SDL_GPUCommandBuffer *command_buffer projection[10] = far_plane / (near_plane - far_plane); projection[11] = -1.0f; projection[14] = near_plane * far_plane / (near_plane - far_plane); - ta_model_multiply(view_model, view, uniforms.model); - ta_model_multiply(uniforms.mvp, projection, view_model); + ta_matrix_multiply(view_model, view, uniforms.model); + ta_matrix_multiply(uniforms.mvp, projection, view_model); + ta_shadow_get_light_view_projection(uniforms.light_view_projection); uniforms.camera_position[0] = ta_camera_current.x; uniforms.camera_position[1] = ta_camera_current.y; uniforms.camera_position[2] = ta_camera_current.z; - uniforms.padding = 0.0f; - SDL_GPUGraphicsPipeline *pipeline = ta_gpu_enable_wireframe && model->wireframe_pipeline ? model->wireframe_pipeline : model->pipeline; + uniforms.shadow_normal_offset = ta_shadow_normal_offset; + bool wireframe = ta_gpu_enable_wireframe && model->wireframe_pipeline; + SDL_GPUGraphicsPipeline *pipeline = wireframe ? model->wireframe_pipeline : model->pipeline; SDL_PushGPUVertexUniformData(command_buffer, 0, &uniforms, sizeof(uniforms)); SDL_BindGPUGraphicsPipeline(render_pass, pipeline); + + if (!wireframe) { + ta_model_light_uniforms light = { + .direction = { + ta_shadow_light_direction[0], + ta_shadow_light_direction[1], + ta_shadow_light_direction[2] + }, + .shadow_texel_size = 1.0f / (float)TA_SHADOW_MAP_SIZE, + .shadow_minimum_bias = ta_shadow_minimum_bias, + .shadow_maximum_bias = ta_shadow_maximum_bias, + .shadow_strength = ta_shadow_strength, + }; + SDL_GPUTextureSamplerBinding shadow_binding = { + .texture = ta_shadow_texture, + .sampler = ta_shadow_sampler + }; + SDL_PushGPUFragmentUniformData(command_buffer, 0, &light, sizeof(light)); + SDL_BindGPUFragmentSamplers(render_pass, 0, &shadow_binding, 1); + } + + SDL_GPUBufferBinding binding = { + .buffer = model->vertex_buffer + }; + SDL_BindGPUVertexBuffers(render_pass, 0, &binding, 1); + SDL_DrawGPUPrimitives(render_pass, model->vertex_count, 1, 0, 0); +} + +void ta_model_render_shadow(const ta_model *model, SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass) { + if (!model->shadow_pipeline || !model->vertex_buffer || !model->uploaded) { + return; + } + ta_model_shadow_uniforms uniforms; + float model_matrix[TA_MATRIX_ELEMENTS]; + float light_view_projection[TA_MATRIX_ELEMENTS]; + ta_model_get_model_matrix(model, model_matrix); + ta_shadow_get_light_view_projection(light_view_projection); + ta_matrix_multiply(uniforms.light_model_view_projection, light_view_projection, model_matrix); + SDL_PushGPUVertexUniformData(command_buffer, 0, &uniforms, sizeof(uniforms)); + SDL_BindGPUGraphicsPipeline(render_pass, model->shadow_pipeline); SDL_GPUBufferBinding binding = { .buffer = model->vertex_buffer }; @@ -338,6 +416,7 @@ void ta_model_render(const ta_model *model, SDL_GPUCommandBuffer *command_buffer void ta_model_destroy(ta_model *model, SDL_GPUDevice *device) { SDL_ReleaseGPUGraphicsPipeline(device, model->pipeline); SDL_ReleaseGPUGraphicsPipeline(device, model->wireframe_pipeline); + SDL_ReleaseGPUGraphicsPipeline(device, model->shadow_pipeline); SDL_ReleaseGPUBuffer(device, model->vertex_buffer); ta_model_free(&model->mesh); memset(model, 0, sizeof(*model)); diff --git a/src/screen.c b/src/screen.c index 599e79e..ed76094 100644 --- a/src/screen.c +++ b/src/screen.c @@ -51,4 +51,14 @@ void ta_screen_render(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *r case NONE: break; } +} + +void ta_screen_render_shadow(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass) { + switch (ta_screen_current) { + case MAIN_MENU: + ta_screens_main_menu_render_shadow(command_buffer, render_pass); + break; + case NONE: + break; + } } \ No newline at end of file diff --git a/src/screens/main_menu.c b/src/screens/main_menu.c index 8ac0f43..18b9bff 100644 --- a/src/screens/main_menu.c +++ b/src/screens/main_menu.c @@ -29,4 +29,8 @@ void ta_screens_main_menu_upload(SDL_GPUCommandBuffer *command_buffer) { void ta_screens_main_menu_render(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass) { ta_teapot_render(command_buffer, render_pass); +} + +void ta_screens_main_menu_render_shadow(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass) { + ta_teapot_render_shadow(command_buffer, render_pass); } \ No newline at end of file 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 diff --git a/src/ta_math.c b/src/ta_math.c index 53375a5..5b976fc 100644 --- a/src/ta_math.c +++ b/src/ta_math.c @@ -16,8 +16,26 @@ * along with this program. If not, see . */ +#include #include "ta_math.h" double ta_rad(double degrees) { return degrees * (PI / (CIRCLE_DEG / 2)); +} + +void ta_matrix_identity(float *matrix) { + for (int index = 0; index < TA_MATRIX_ELEMENTS; index++) { + matrix[index] = index % 5 == 0 ? 1.0f : 0.0f; + } +} + +void ta_matrix_multiply(float *result, const float *left, const float *right) { + float product[TA_MATRIX_ELEMENTS]; + for (int column = 0; column < 4; column++) for (int row = 0; row < 4; row++) { + product[column * 4 + row] = 0.0f; + for (int inner = 0; inner < 4; inner++) { + product[column * 4 + row] += left[inner * 4 + row] * right[column * 4 + inner]; + } + } + memcpy(result, product, sizeof(product)); } \ No newline at end of file diff --git a/src/teapot.c b/src/teapot.c index 3d9cb6d..a8889c9 100644 --- a/src/teapot.c +++ b/src/teapot.c @@ -62,6 +62,11 @@ void ta_teapot_render(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *r ta_model_render(&ta_floor_model, command_buffer, render_pass, ta_window_width, ta_window_height); } +void ta_teapot_render_shadow(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass) { + ta_model_render_shadow(&ta_teapot_model, command_buffer, render_pass); + ta_model_render_shadow(&ta_floor_model, command_buffer, render_pass); +} + void ta_teapot_shutdown(void) { ta_model_destroy(&ta_teapot_model, ta_gpu_device); ta_model_destroy(&ta_floor_model, ta_gpu_device); -- cgit v1.2.3