From f19e59f819edbbe3688ab07be73d68898539b82d Mon Sep 17 00:00:00 2001 From: Draklaw Date: Thu, 24 Feb 2022 22:38:09 +0100 Subject: [PATCH] Gjk, FPS view. --- CMakeLists.txt | 1 + src/Simplex.cpp | 141 +++++++++++++++++++++++++++++++++++++++++ src/Simplex.h | 75 ++++++++++++++++++++++ src/VkExpe.cpp | 78 ++++++++++++++++++++++- src/VkExpe.h | 10 +++ src/VulkanTutorial.cpp | 30 ++++----- src/VulkanTutorial.h | 8 +++ src/core.h | 13 ++++ 8 files changed, 339 insertions(+), 17 deletions(-) create mode 100644 src/Simplex.cpp create mode 100644 src/Simplex.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ac8f11c..fb610f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ add_executable(vk_expe src/Vulkan/Swapchain.cpp src/main.cpp src/utils.cpp + src/Simplex.cpp src/Logger.cpp src/Planet.cpp src/VkExpe.cpp diff --git a/src/Simplex.cpp b/src/Simplex.cpp new file mode 100644 index 0000000..8f27bdb --- /dev/null +++ b/src/Simplex.cpp @@ -0,0 +1,141 @@ + +#include + + +Vector3 polyhedron_support(Vector3AV polyhedron, const Vector3& direction) { + assert(polyhedron.size() > 0); + + Index best = 0; + Real best_dot = polyhedron[0].dot(direction); + + for (Index index = 1; index < polyhedron.size(); index += 1) { + Real dot = polyhedron[index].dot(direction); + if (dot > best_dot) { + best = index; + best_dot = dot; + } + } + + return polyhedron[best]; +} + + +Simplex::Simplex() = default; +Simplex::Simplex(const Vector3& point) + : m_points{ point, Vector3{}, Vector3{}, Vector3{} } + , m_point_count(1) +{} + + +Gjk::Gjk(Vector3AV polyhedron0, Vector3AV polyhedron1, const Vector3& direction) + : m_polyhedron0(polyhedron0) + , m_polyhedron1(polyhedron1) + , m_direction(direction) +{} + +Gjk::~Gjk() = default; + +const Vector3& Gjk::direction() const { + return m_direction; +} + +bool Gjk::operator()() { + while (true) { + m_simplex.push(support(m_direction)); + + if (m_simplex.last().dot(m_direction) < Real(0)) + return false; + + if (nextSimplex()) + return true; + } +} + +bool Gjk::nextSimplex() { + switch(m_simplex.point_count()) { + case 1: return nextFromPoint(); + case 2: return nextFromLine(); + case 3: return nextFromTriangle(); + case 4: return nextFromTetrahedron(); + } + return false; +} + +bool Gjk::nextFromPoint() { + m_direction = -m_simplex.last(); + + return false; +} + +bool Gjk::nextFromLine() { + Vector3 v01 = m_simplex.point(1) - m_simplex.point(0); + Vector3 v1o = -m_simplex.point(1); + + m_direction = v01.cross(v1o).cross(v01); + + return false; +} + +bool Gjk::nextFromTriangle() { + Vector3 v20 = m_simplex.point(0) - m_simplex.point(2); + Vector3 v21 = m_simplex.point(1) - m_simplex.point(2); + Vector3 v2o = -m_simplex.point(2); + + Vector3 n = v20.cross(v21); + Vector3 n0 = n.cross(v21); + Vector3 n1 = v20.cross(n); + + if(n0.dot(v2o) > Real(0)) { + m_simplex.pop(0); + m_direction = n0; + } + else if(n1.dot(v2o) > Real(0)) { + m_simplex.pop(1); + m_direction = n1; + } + else if(n.dot(v2o) > Real(0)) { + m_direction = n; + } + else { + m_simplex.swap(0, 1); + m_direction = -n; + } + + return false; +} + +bool Gjk::nextFromTetrahedron() { + Vector3 v30 = m_simplex.point(0) - m_simplex.point(3); + Vector3 v31 = m_simplex.point(1) - m_simplex.point(3); + Vector3 v32 = m_simplex.point(2) - m_simplex.point(3); + Vector3 v3o = -m_simplex.point(3); + + Vector3 n[3] = { + v31.cross(v32), + v32.cross(v30), + v30.cross(v31), + }; + + Real d[3] = { + n[0].dot(v3o), + n[1].dot(v3o), + n[2].dot(v3o), + }; + + Index best = (d[0] > d[1])? ((d[0] > d[2])? 0: 2): + ((d[1] > d[2])? 1: 2); + + if(d[best] <= Real(0)) + return true; + + m_simplex.pop(best); + m_direction = n[best]; + + return false; +} + +Vector3 Gjk::support(const Vector3& direction) const +{ + return polyhedron_support(m_polyhedron0, direction) + - polyhedron_support(m_polyhedron1, -direction); +} diff --git a/src/Simplex.h b/src/Simplex.h new file mode 100644 index 0000000..f1e7b91 --- /dev/null +++ b/src/Simplex.h @@ -0,0 +1,75 @@ +#pragma once + +#include + + +class Simplex { +public: + Simplex(); + Simplex(const Vector3& point); + + inline Index point_count() const { + return m_point_count; + } + + inline const Vector3& point(Index index) const { + assert(index < m_point_count); + return m_points[index]; + } + + inline const Vector3& last() const { + assert(m_point_count > 0); + return point(m_point_count - 1); + } + + inline void push(const Vector3& point) { + assert(m_point_count < MaxPointCount); + m_points[m_point_count] = point; + m_point_count += 1; + } + + inline void pop(Index index) { + assert(index < m_point_count); + for(Index i = index; i + 1 < m_point_count; i += 1) + m_points[i] = m_points[i + 1]; + m_point_count -= 1; + } + + inline void swap(Index index0, Index index1) { + using std::swap; + swap(m_points[index0], m_points[index1]); + } + +private: + static constexpr Index MaxPointCount = 4; + +private: + Vector3 m_points[MaxPointCount]; + Index m_point_count = 0; +}; + + +class Gjk { +public: + Gjk(Vector3AV polyhedron0, Vector3AV polyhedron1, const Vector3& direction=Vector3::UnitX()); + ~Gjk(); + + const Vector3& direction() const; + + bool operator()(); + +private: + bool nextSimplex(); + bool nextFromPoint(); + bool nextFromLine(); + bool nextFromTriangle(); + bool nextFromTetrahedron(); + + Vector3 support(const Vector3& direction) const; + +private: + Vector3AV m_polyhedron0; + Vector3AV m_polyhedron1; + Simplex m_simplex; + Vector3 m_direction; +}; diff --git a/src/VkExpe.cpp b/src/VkExpe.cpp index 7834306..08b0948 100644 --- a/src/VkExpe.cpp +++ b/src/VkExpe.cpp @@ -5,6 +5,7 @@ #include #include +#include void SdlWindowDeleter::operator()(SDL_Window* window) const { @@ -25,7 +26,7 @@ void VkExpe::initialize() { auto const window = SDL_CreateWindow( "vk_expe", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - 800, 600, + 1920, 1080, SDL_WINDOW_VULKAN | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_SHOWN @@ -60,6 +61,8 @@ void VkExpe::run() { m_running = false; }); + auto last_time = std::chrono::high_resolution_clock::now(); + SDL_Event event; while(m_running) { while(SDL_PollEvent(&event)) { @@ -72,17 +75,88 @@ void VkExpe::run() { m_running = false; break; } + case SDL_MOUSEMOTION: { + m_mouse_offset += Vector2( + event.motion.xrel, + event.motion.yrel + ); + break; + } case SDL_WINDOWEVENT: { switch(event.window.event) { case SDL_WINDOWEVENT_RESIZED: { m_vulkan.invalidate_swapchain(); + break; + } + case SDL_WINDOWEVENT_FOCUS_GAINED: { + SDL_SetRelativeMouseMode(SDL_TRUE); + break; } + case SDL_WINDOWEVENT_FOCUS_LOST: { + SDL_SetRelativeMouseMode(SDL_FALSE); + break; } + } + break; } } } + const auto new_time = std::chrono::high_resolution_clock::now(); + const auto elapsed = new_time - last_time; + last_time = new_time; + + update(std::chrono::duration(elapsed).count()); + + m_vulkan.set_camera(m_camera_position, m_camera_z, m_camera_y); m_vulkan.draw_frame(); - // m_running = false; + + m_mouse_offset = Vector2::Zero(); + } +} + +void VkExpe::update(double elapsed) { + int key_count = 0; + const auto keys = SDL_GetKeyboardState(&key_count); + + const auto test_key = [key_count, keys](int scan_code) { + return scan_code < key_count && keys[scan_code]; + }; + + if (!m_mouse_offset.isZero()) { + const Real x_sensi = 0.001; + const Real y_sensi = -0.001; + + const Vector3 camera_x = m_camera_y.cross(m_camera_z); + Vector3 axis = + x_sensi * m_mouse_offset[0] * m_camera_y + + y_sensi * m_mouse_offset[1] * camera_x; + Real rot_norm = axis.norm(); + AngleAxis rot(rot_norm, axis / rot_norm); + m_camera_y = rot * m_camera_y; + m_camera_z = rot * m_camera_z; + } + + Vector3 walk_direction = Vector3::Zero(); + if(test_key(SDL_SCANCODE_W)) + walk_direction += Vector3::UnitZ(); + if(test_key(SDL_SCANCODE_S)) + walk_direction -= Vector3::UnitZ(); + if(test_key(SDL_SCANCODE_A)) + walk_direction -= Vector3::UnitX(); + if(test_key(SDL_SCANCODE_D)) + walk_direction += Vector3::UnitX(); + if(test_key(SDL_SCANCODE_SPACE)) + walk_direction -= Vector3::UnitY(); + if(test_key(SDL_SCANCODE_LCTRL)) + walk_direction += Vector3::UnitY(); + + if(!walk_direction.isZero()) { + walk_direction.normalize(); + const Real base_velocity = 1; + + Matrix3 camera_basis; + camera_basis << m_camera_y.cross(m_camera_z), m_camera_y, m_camera_z; + m_camera_position += elapsed * base_velocity * (camera_basis * walk_direction); } } diff --git a/src/VkExpe.h b/src/VkExpe.h index 6cafae5..be26486 100644 --- a/src/VkExpe.h +++ b/src/VkExpe.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -26,8 +27,17 @@ public: void run(); + void update(double elapsed); + private: VulkanTutorial m_vulkan; + + Vector3 m_camera_position = Vector3(0.0f, 0.0f, -3.0f); + Vector3 m_camera_z = Vector3(0.0f, 0.0f, 1.0f); + Vector3 m_camera_y = Vector3(0.0f, 1.0f, 0.0f); + WindowUP m_window; bool m_running = false; + + Vector2 m_mouse_offset = Vector2::Zero(); }; diff --git a/src/VulkanTutorial.cpp b/src/VulkanTutorial.cpp index 2e15086..c227b13 100644 --- a/src/VulkanTutorial.cpp +++ b/src/VulkanTutorial.cpp @@ -196,6 +196,12 @@ void VulkanTutorial::shutdown() { m_context.shutdown(); } +void VulkanTutorial::set_camera(const Vector3& camera_position, const Vector3& camera_z, const Vector3& camera_y) { + m_camera_position = camera_position; + m_camera_z = camera_z; + m_camera_y = camera_y; +} + void VulkanTutorial::draw_frame() { m_swapchain.begin_frame(); auto const image_index = m_swapchain.current_image_index(); @@ -206,15 +212,14 @@ void VulkanTutorial::draw_frame() { } m_last_frame_time = now; - const float alpha = SecondsD(m_time).count() * (2.0 * M_PI) / 10.0; - const float dist = 2.0f; - const Eigen::Matrix4f view = look_at_matrix( - // Eigen::Vector3f(0.0f, 0.0f, -dist), - Eigen::Vector3f(0.0f, -dist, -dist), - // dist * Eigen::Vector3f(std::cos(alpha), std::sin(alpha), -1.0), - Eigen::Vector3f::Zero(), - -Eigen::Vector3f::UnitY() - ); + Matrix3 linear_view; + linear_view << + m_camera_y.cross(m_camera_z).transpose(), + m_camera_y.transpose(), + m_camera_z.transpose(); + Matrix4 view; + view << linear_view, linear_view * -m_camera_position, + Vector4::UnitW().transpose(); const float fov = M_PI / 3.0f; const float near = 0.1f; @@ -227,12 +232,7 @@ void VulkanTutorial::draw_frame() { near, far ); - using Transform = Eigen::Transform; Transform model = Transform::Identity(); - model.rotate(Eigen::AngleAxisf( - alpha, - Eigen::Vector3f::UnitY() - )); const Uniforms uniforms = { .scene_from_model = model.matrix(), @@ -241,7 +241,7 @@ void VulkanTutorial::draw_frame() { 0.5 * m_swapchain.extent().width, 0.5 * m_swapchain.extent().height, }, - .lod = std::cos(alpha) * 0.5f + 0.5f, + // .lod = std::cos(alpha) * 0.5f + 0.5f, }; void* uniform_buffer; diff --git a/src/VulkanTutorial.h b/src/VulkanTutorial.h index c06b631..97c7586 100644 --- a/src/VulkanTutorial.h +++ b/src/VulkanTutorial.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -35,6 +37,8 @@ public: void initialize(SDL_Window* window); void shutdown(); + void set_camera(const Vector3& camera_position, const Vector3& camera_z, const Vector3& camera_y); + void draw_frame(); void invalidate_swapchain(); @@ -78,6 +82,10 @@ private: std::vector m_command_buffers; std::vector m_render_done; + Vector3 m_camera_position = Vector3(0.0f, 0.0f, -3.0f); + Vector3 m_camera_z = Vector3(0.0f, 0.0f, 1.0f); + Vector3 m_camera_y = Vector3(0.0f, 1.0f, 0.0f); + TimePoint m_last_frame_time; Duration m_time = Duration(0); }; diff --git a/src/core.h b/src/core.h index 4b2c02c..892daeb 100644 --- a/src/core.h +++ b/src/core.h @@ -8,8 +8,21 @@ using Byte = unsigned char; using Index = uint32_t; + using Real = float; + +using Vector2 = Eigen::Matrix; using Vector3 = Eigen::Matrix; +using Vector4 = Eigen::Matrix; + +using Matrix2 = Eigen::Matrix; +using Matrix3 = Eigen::Matrix; +using Matrix4 = Eigen::Matrix; + +using Transform = Eigen::Transform; +using AngleAxis = Eigen::AngleAxis; +using Quaternion = Eigen::Quaternion; + using Triangle = Eigen::Array;