diff options
| author | Connor Thomson <blumatrikz@gmail.com> | 2026-09-12 14:16:55 -0700 |
|---|---|---|
| committer | Connor Thomson <blumatrikz@gmail.com> | 2026-09-12 14:16:55 -0700 |
| commit | 251b611fef77533082165bab0b88eefa05b49fbe (patch) | |
| tree | a3286e1fb991f5b0ecbace6208cb6c0ef1dcb845 /src/model.c | |
| parent | 8fc46365d6bdcde0c18a8b413b9f7a80fa487107 (diff) | |
Diffstat (limited to 'src/model.c')
| -rw-r--r-- | src/model.c | 179 |
1 files changed, 129 insertions, 50 deletions
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)); |