roleplay/client/src/camera.c

201 lines
6.4 KiB
C

#include "camera.h"
#include "ui.h"
#include "render_graph.h"
#include <cglm/cam.h>
#include <math.h>
#include <stddef.h>
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;
}