#include "camera.h" #include "ui.h" #include "render_graph.h" #include #include #include static vec3 up = {0, 1, 0}; VkResult create_camera(RenderContext* gpu, Camera* camera) { VkResult result; camera->texture_dynamic = true; camera->max_depth = 1; for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VK_RESULT(create_storage_buffer( gpu->allocator, 0, sizeof(GPUCamera), &camera->gpu_buffer[i], &camera->gpu_buffer_memory[i])); camera->gpu_address[i] = buffer_address(gpu->device, camera->gpu_buffer[i]); } return VK_SUCCESS; } void destroy_camera(RenderContext* gpu, Camera* camera) { if(camera->has_texture_target) { camera_destroy_texture_target(gpu, NULL, camera); } for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { vmaDestroyBuffer(gpu->allocator, camera->gpu_buffer[i], camera->gpu_buffer_memory[i]); } } void camera_update_view(Camera* camera) { vec3 eye = {}; eye[0] = camera->position[0] + camera->distance*cos(camera->rotation[1])*cos(camera->rotation[0]); eye[1] = camera->position[1] + camera->distance*sin(camera->rotation[1]); eye[2] = camera->position[2] + camera->distance*cos(camera->rotation[1])*sin(camera->rotation[0]); glm_lookat(eye, camera->position, up, camera->view); } void camera_update_proj(Camera* camera, float aspect) { glm_perspective( PERSPECTIVE_FOVY, aspect, PERSPECTIVE_NEARZ, PERSPECTIVE_FARZ, camera->proj); } VkResult camera_sync_gpu(Camera* camera, RenderContext* gpu) { GPUCamera data; glm_mat4_copy(camera->proj, data.proj); glm_mat4_copy(camera->view, data.view); return add_transfers(&data, camera->gpu_buffer, 0, sizeof(GPUCamera), gpu); } VkResult camera_init_texture_target( RenderContext* gpu, UIContext* ui, Camera* camera, uint32_t width, uint32_t height) { VkResult result; camera->texture.width = width; camera->texture.height = height; VkExtent2D extent = {width, height}; for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VK_RESULT(create_render_target_texture(gpu, ui, width, height, &camera->texture.texture_slot[i])); VK_RESULT(create_depth_image( gpu->device, gpu->depth_format, extent, gpu->allocator, gpu->extra_graphics_pool, gpu->graphics_queue, &camera->texture.depth_image[i], &camera->texture.depth_image_memory[i], &camera->texture.depth_image_view[i])); } camera->has_texture_target = true; return VK_SUCCESS; } void camera_destroy_texture_target(RenderContext* gpu, UIContext* ui, Camera* camera) { (void)ui; for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { vkDestroyImageView(gpu->device, camera->texture.depth_image_view[i], NULL); vmaDestroyImage(gpu->allocator, camera->texture.depth_image[i], camera->texture.depth_image_memory[i]); // TODO: free camera->texture.texture_slot[i] from the bindless descriptor // pool once destroy_render_target_texture is implemented. For now the // color slot leaks on destroy - only called from destroy_camera (shutdown). } camera->has_texture_target = false; } VkResult camera_recreate_texture_target( Camera* camera, RenderContext* gpu, UIContext* ui, uint32_t width, uint32_t height) { VkResult result; VkExtent2D extent = {width, height}; for(uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { VK_RESULT(recreate_render_target_texture(gpu, ui, camera->texture.texture_slot[i], width, height)); vkDestroyImageView(gpu->device, camera->texture.depth_image_view[i], NULL); vmaDestroyImage(gpu->allocator, camera->texture.depth_image[i], camera->texture.depth_image_memory[i]); VK_RESULT(create_depth_image( gpu->device, gpu->depth_format, extent, gpu->allocator, gpu->extra_graphics_pool, gpu->graphics_queue, &camera->texture.depth_image[i], &camera->texture.depth_image_memory[i], &camera->texture.depth_image_view[i])); } camera->texture.width = width; camera->texture.height = height; return VK_SUCCESS; } VkResult camera_display(Camera* camera, Container* container, RenderContext* gpu, UIContext* ui) { VkResult result; container_set_camera(container, camera); if(!camera->has_texture_target) { uint32_t w, h; if(camera->texture_dynamic) { VkRect2D rect; container_screen_rect(gpu, container, &rect); w = rect.extent.width > 0 ? rect.extent.width : 1; h = rect.extent.height > 0 ? rect.extent.height : 1; } else { w = camera->texture.width > 0 ? camera->texture.width : 1; h = camera->texture.height > 0 ? camera->texture.height : 1; } VK_RESULT(camera_init_texture_target(gpu, ui, camera, w, h)); } if(ui->render_graph != NULL) { camera->rg_node = render_graph_add_camera_node(ui->render_graph, camera); ui->render_graph_dirty = true; } else { camera->rg_node = UINT32_MAX; } GPUDrawable bg = { .pos = {0.0f, 0.0f}, .size = {container->data.size[0], container->data.size[1]}, .color = { {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}, }, .type = DRAWABLE_TYPE_IMAGE, .var = camera->texture.texture_slot[0], .events = 0, .z = -1e30f, }; uint32_t bg_slot; VK_RESULT(ui_create_drawable(container, &bg, gpu, &bg_slot)); // ui_create_drawable → add_transfers uploads var=texture_slot[0] to both // frames. Override frame 1's var in-place (the coalesce in add_transfer // rewrites the staging data without adding a new entry). VK_RESULT(add_transfer( &camera->texture.texture_slot[1], container->drawables[1], sizeof(GPUDrawable) * bg_slot + offsetof(GPUDrawable, var), sizeof(uint32_t), 1, gpu)); container->camera_background_drawable = bg_slot; return VK_SUCCESS; } VkResult camera_undisplay(Camera* camera, Container* container, RenderContext* gpu, UIContext* ui) { if(ui->render_graph != NULL && camera->rg_node != UINT32_MAX) { render_graph_remove_node(ui->render_graph, camera->rg_node); camera->rg_node = UINT32_MAX; ui->render_graph_dirty = true; } VkResult result = VK_SUCCESS; if(container->camera_background_drawable != UINT32_MAX) { result = ui_destroy_drawable(container, container->camera_background_drawable, gpu); container->camera_background_drawable = UINT32_MAX; } container_set_camera(container, NULL); return result; }