摄像机系统,和子对象绑定

This commit is contained in:
ArchZer0
2026-08-17 16:15:05 +08:00
parent 63d97f058a
commit 7b7756f0fe
20 changed files with 744 additions and 132 deletions
+194 -79
View File
@@ -1,4 +1,5 @@
#include <render.hpp>
#include <camera.hpp>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
@@ -197,6 +198,7 @@ struct Renderer::Impl {
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
VkPipeline pipeline = VK_NULL_HANDLE;
std::atomic<bool> framebuffer_resized{false};
VkCommandPool command_pool = VK_NULL_HANDLE;
std::vector<FrameData> frames;
@@ -221,11 +223,12 @@ struct Renderer::Impl {
Renderer::InputCallback input_callback;
void init(int width, int height, const char* title);
void draw(const Actor* const* actors, size_t actor_count);
void draw(const Actor* const* actors, size_t actor_count, const Camera* camera);
void upload_mesh(const Mesh& mesh);
void create_texture(MeshResource& resource, const std::string& path);
VkCommandBuffer begin_transfer();
void end_transfer(VkCommandBuffer command_buffer);
void recreate_swapchain();
void destroy();
};
@@ -240,7 +243,11 @@ void Renderer::init(int width, int height, const char* title) {
}
void Renderer::draw(const Actor* const* actors, size_t actor_count) {
impl_->draw(actors, actor_count);
impl_->draw(actors, actor_count, nullptr);
}
void Renderer::draw(const Actor* const* actors, size_t actor_count, const Camera* camera) {
impl_->draw(actors, actor_count, camera);
}
bool Renderer::should_close() const {
@@ -277,6 +284,7 @@ void Renderer::destroy() {
impl_->destroy();
}
#if 0 // InputMap implementation moved to input.cpp.
void InputMap::add_action(const std::string& action) {
std::lock_guard<std::mutex> lock(mutex_);
actions_.try_emplace(action);
@@ -428,72 +436,7 @@ float InputMap::get_axis(const std::string& negative_action,
const std::string& positive_action) const {
return get_action_strength(positive_action) - get_action_strength(negative_action);
}
RenderLoop::~RenderLoop() {
stop();
}
void RenderLoop::start(int width, int height, const char* title) {
if (running_) {
return;
}
renderer_.init(width, height, title);
renderer_.set_input_callback([this](const InputEvent& event) {
input_map_.handle_event(event);
});
running_ = true;
thread_ = std::thread(&RenderLoop::run, this);
}
void RenderLoop::stop() {
if (!running_) {
return;
}
running_ = false;
if (thread_.joinable()) {
thread_.join();
}
renderer_.destroy();
}
void RenderLoop::set_actors(std::vector<Actor*> actors) {
std::lock_guard<std::mutex> lock(actors_mutex_);
actors_ = std::move(actors);
}
void RenderLoop::poll_events() const {
renderer_.poll_events();
}
bool RenderLoop::should_close() const {
return renderer_.should_close();
}
InputMap& RenderLoop::input() {
return input_map_;
}
const InputMap& RenderLoop::input() const {
return input_map_;
}
void RenderLoop::run() {
while (running_) {
std::vector<Actor*> actors;
{
std::lock_guard<std::mutex> lock(actors_mutex_);
actors = actors_;
}
if (!actors.empty()) {
renderer_.draw(actors.data(), actors.size());
} else {
std::this_thread::yield();
}
}
}
#endif
void Renderer::Impl::init(int width, int height, const char* title) {
if (initialized) {
@@ -509,8 +452,11 @@ void Renderer::Impl::init(int width, int height, const char* title) {
if (window == nullptr) {
throw std::runtime_error("failed to create GLFW window");
}
glfwSetWindowUserPointer(window, this);
glfwSetFramebufferSizeCallback(window, [](GLFWwindow* target, int, int) {
auto* impl = static_cast<Renderer::Impl*>(glfwGetWindowUserPointer(target));
if (impl != nullptr) impl->framebuffer_resized = true;
});
glfwSetKeyCallback(window, [](GLFWwindow* target, int key, int scancode, int action,
int mods) {
auto* impl = static_cast<Renderer::Impl*>(glfwGetWindowUserPointer(target));
@@ -1051,7 +997,15 @@ void Renderer::Impl::init(int width, int height, const char* title) {
pipeline_info.pMultisampleState = &multisampling;
pipeline_info.pDepthStencilState = nullptr;
pipeline_info.pColorBlendState = &color_blending;
pipeline_info.pDynamicState = nullptr;
const std::array<VkDynamicState, 2> dynamic_states = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamic_state{};
dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
dynamic_state.pDynamicStates = dynamic_states.data();
pipeline_info.pDynamicState = &dynamic_state;
pipeline_info.layout = pipeline_layout;
pipeline_info.renderPass = render_pass;
pipeline_info.subpass = 0;
@@ -1232,7 +1186,139 @@ void Renderer::Impl::upload_mesh(const Mesh& mesh) {
create_texture(resource, mesh.texture);
}
void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
void Renderer::Impl::recreate_swapchain() {
int width = 0;
int height = 0;
glfwGetFramebufferSize(window, &width, &height);
if (width == 0 || height == 0) return;
check(vkDeviceWaitIdle(device), "vkDeviceWaitIdle");
VkSurfaceCapabilitiesKHR capabilities{};
check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities),
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR");
uint32_t format_count = 0;
check(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, nullptr),
"vkGetPhysicalDeviceSurfaceFormatsKHR");
std::vector<VkSurfaceFormatKHR> formats(format_count);
check(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &format_count, formats.data()),
"vkGetPhysicalDeviceSurfaceFormatsKHR");
VkSurfaceFormatKHR surface_format = formats.front();
for (const VkSurfaceFormatKHR& format : formats) {
if (format.format == VK_FORMAT_B8G8R8A8_SRGB &&
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
surface_format = format;
break;
}
}
if (surface_format.format != swapchain_format) {
throw std::runtime_error("swapchain format changed; renderer recreation is required");
}
uint32_t present_mode_count = 0;
check(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count,
nullptr),
"vkGetPhysicalDeviceSurfacePresentModesKHR");
std::vector<VkPresentModeKHR> present_modes(present_mode_count);
check(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &present_mode_count,
present_modes.data()),
"vkGetPhysicalDeviceSurfacePresentModesKHR");
VkPresentModeKHR present_mode = VK_PRESENT_MODE_FIFO_KHR;
for (VkPresentModeKHR mode : present_modes) {
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
present_mode = mode;
break;
}
}
VkExtent2D extent{};
if (capabilities.currentExtent.width != UINT32_MAX) {
extent = capabilities.currentExtent;
} else {
extent.width = std::clamp(static_cast<uint32_t>(width), capabilities.minImageExtent.width,
capabilities.maxImageExtent.width);
extent.height = std::clamp(static_cast<uint32_t>(height), capabilities.minImageExtent.height,
capabilities.maxImageExtent.height);
}
uint32_t image_count = capabilities.minImageCount + 1;
if (capabilities.maxImageCount > 0 && image_count > capabilities.maxImageCount) {
image_count = capabilities.maxImageCount;
}
for (VkFramebuffer framebuffer : framebuffers) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
framebuffers.clear();
for (VkImageView view : swapchain_image_views) {
vkDestroyImageView(device, view, nullptr);
}
swapchain_image_views.clear();
const VkSwapchainKHR old_swapchain = swapchain;
if (old_swapchain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(device, old_swapchain, nullptr);
swapchain = VK_NULL_HANDLE;
}
VkSwapchainCreateInfoKHR create_info{};
create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
create_info.surface = surface;
create_info.minImageCount = image_count;
create_info.imageFormat = surface_format.format;
create_info.imageColorSpace = surface_format.colorSpace;
create_info.imageExtent = extent;
create_info.imageArrayLayers = 1;
create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
std::array<uint32_t, 2> families = {graphics_family, present_family};
create_info.imageSharingMode = graphics_family == present_family
? VK_SHARING_MODE_EXCLUSIVE
: VK_SHARING_MODE_CONCURRENT;
create_info.queueFamilyIndexCount = graphics_family == present_family ? 0 : 2;
create_info.pQueueFamilyIndices = families.data();
create_info.preTransform = capabilities.currentTransform;
create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
create_info.presentMode = present_mode;
create_info.clipped = VK_TRUE;
create_info.oldSwapchain = VK_NULL_HANDLE;
check(vkCreateSwapchainKHR(device, &create_info, nullptr, &swapchain), "vkCreateSwapchainKHR");
swapchain_extent = extent;
uint32_t actual_image_count = 0;
check(vkGetSwapchainImagesKHR(device, swapchain, &actual_image_count, nullptr),
"vkGetSwapchainImagesKHR");
if (actual_image_count > frames.size()) {
throw std::runtime_error("resized swapchain needs more frame resources");
}
swapchain_images.resize(actual_image_count);
check(vkGetSwapchainImagesKHR(device, swapchain, &actual_image_count, swapchain_images.data()),
"vkGetSwapchainImagesKHR");
swapchain_image_views.resize(actual_image_count);
for (uint32_t i = 0; i < actual_image_count; ++i) {
VkImageViewCreateInfo view_info{};
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = swapchain_images[i];
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = swapchain_format;
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.layerCount = 1;
check(vkCreateImageView(device, &view_info, nullptr, &swapchain_image_views[i]),
"vkCreateImageView");
}
framebuffers.resize(actual_image_count);
for (uint32_t i = 0; i < actual_image_count; ++i) {
VkFramebufferCreateInfo framebuffer_info{};
framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebuffer_info.renderPass = render_pass;
framebuffer_info.attachmentCount = 1;
framebuffer_info.pAttachments = &swapchain_image_views[i];
framebuffer_info.width = swapchain_extent.width;
framebuffer_info.height = swapchain_extent.height;
framebuffer_info.layers = 1;
check(vkCreateFramebuffer(device, &framebuffer_info, nullptr, &framebuffers[i]),
"vkCreateFramebuffer");
}
images_in_flight.assign(actual_image_count, VK_NULL_HANDLE);
framebuffer_resized = false;
}
void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count, const Camera* camera) {
if (!initialized || actor_count == 0) {
return;
}
@@ -1245,6 +1331,7 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
frame.image_available,
VK_NULL_HANDLE, &image_index);
if (acquire_result == VK_ERROR_OUT_OF_DATE_KHR) {
recreate_swapchain();
return;
}
if (acquire_result != VK_SUCCESS && acquire_result != VK_SUBOPTIMAL_KHR) {
@@ -1286,6 +1373,15 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
vkCmdBeginRenderPass(frame.command_buffer, &render_pass_begin, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindPipeline(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkViewport viewport{};
viewport.width = static_cast<float>(swapchain_extent.width);
viewport.height = static_cast<float>(swapchain_extent.height);
viewport.minDepth = 0.0F;
viewport.maxDepth = 1.0F;
VkRect2D scissor{};
scissor.extent = swapchain_extent;
vkCmdSetViewport(frame.command_buffer, 0, 1, &viewport);
vkCmdSetScissor(frame.command_buffer, 0, 1, &scissor);
for (size_t i = 0; i < actor_count; ++i) {
const Actor& actor = *actors[i];
if (actor.mesh() == nullptr) continue;
@@ -1301,9 +1397,26 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
vkCmdBindDescriptorSets(frame.command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout,
0, 1, &resource.descriptor_set, 0, nullptr);
const auto position = actor.position();
const auto facing = actor.facing();
const float scale = 0.35F;
auto position = actor.global_position();
auto facing = actor.global_facing();
float camera_zoom = 1.0F;
if (camera != nullptr) {
const auto camera_position = camera->global_position();
const auto camera_facing = camera->global_facing();
const float camera_length = std::sqrt(camera_facing[0] * camera_facing[0] +
camera_facing[1] * camera_facing[1]);
const float camera_x = camera_length > 0.0001F ? camera_facing[0] / camera_length : 1.0F;
const float camera_y = camera_length > 0.0001F ? camera_facing[1] / camera_length : 0.0F;
const float dx = position[0] - camera_position[0];
const float dy = position[1] - camera_position[1];
position = {camera_x * dx + camera_y * dy, -camera_y * dx + camera_x * dy};
facing = {camera_x * facing[0] + camera_y * facing[1],
-camera_y * facing[0] + camera_x * facing[1]};
camera_zoom = camera->zoom();
}
const float scale = 0.35F * camera_zoom;
const float horizontal_projection =
static_cast<float>(swapchain_extent.height) / static_cast<float>(swapchain_extent.width);
float facing_x = facing[0];
float facing_y = facing[1];
const float facing_length = std::sqrt(facing_x * facing_x + facing_y * facing_y);
@@ -1316,10 +1429,10 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
}
std::array<float, 20> constants = {{
scale * facing_x, scale * facing_y, 0.0F, 0.0F,
-scale * facing_y, scale * facing_x, 0.0F, 0.0F,
scale * facing_x * horizontal_projection, scale * facing_y, 0.0F, 0.0F,
-scale * facing_y * horizontal_projection, scale * facing_x, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F,
position[0], position[1], 0.0F, 1.0F,
position[0] * horizontal_projection, position[1], 0.0F, 1.0F,
0.0F, 0.0F, 0.0F, 0.0F,
}};
const std::array<float, 4> color = actor.mesh()->color();
@@ -1354,8 +1467,10 @@ void Renderer::Impl::draw(const Actor* const* actors, size_t actor_count) {
present_info.pSwapchains = &swapchain;
present_info.pImageIndices = &image_index;
VkResult present_result = vkQueuePresentKHR(present_queue, &present_info);
if (present_result != VK_SUCCESS && present_result != VK_SUBOPTIMAL_KHR &&
present_result != VK_ERROR_OUT_OF_DATE_KHR) {
if (present_result == VK_ERROR_OUT_OF_DATE_KHR || present_result == VK_SUBOPTIMAL_KHR ||
framebuffer_resized) {
recreate_swapchain();
} else if (present_result != VK_SUCCESS) {
check(present_result, "vkQueuePresentKHR");
}
(void)present_result;