摄像机系统,和子对象绑定
This commit is contained in:
+194
-79
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user