From 3521ddeb1df05b1c488bf3f006c9f9e4eef32b3e Mon Sep 17 00:00:00 2001 From: ohyzha Date: Wed, 6 Nov 2024 09:48:53 +0200 Subject: [PATCH] rework API --- .clang-format | 2 +- examples/ExampleApps/MovingCubeApp.cpp | 20 +++++ examples/main.cpp | 5 +- .../Controller/CameraController.cpp | 41 --------- .../Controller/CameraController.hpp | 18 +--- .../Controller/FreeCamCameraController.cpp | 4 - openVulkanoCpp/Host/GraphicsAppManager.cpp | 87 +++++++++---------- openVulkanoCpp/Host/GraphicsAppManager.hpp | 7 -- openVulkanoCpp/Input/InputDeviceMouse.cpp | 6 +- openVulkanoCpp/Input/InputDeviceMouse.hpp | 7 +- openVulkanoCpp/Input/InputKey.hpp | 4 +- openVulkanoCpp/Input/InputManager.cpp | 43 ++++++++- openVulkanoCpp/Input/InputManager.hpp | 4 + .../Input/Touch/InputDeviceTouch.hpp | 1 - openVulkanoCpp/Scene/Camera.cpp | 45 +--------- openVulkanoCpp/Scene/Camera.hpp | 31 +------ openVulkanoCpp/Scene/Ray.cpp | 25 ++++-- openVulkanoCpp/Scene/Ray.hpp | 19 +++- .../Scene/SceneIntersectionTestController.cpp | 68 +++++++++++++++ .../Scene/SceneIntersectionTestController.hpp | 33 +++++++ tests/RayTests.cpp | 56 +++++++----- 21 files changed, 296 insertions(+), 230 deletions(-) delete mode 100644 openVulkanoCpp/Controller/CameraController.cpp create mode 100644 openVulkanoCpp/Scene/SceneIntersectionTestController.cpp create mode 100644 openVulkanoCpp/Scene/SceneIntersectionTestController.hpp diff --git a/.clang-format b/.clang-format index 0a4a8f9..ed47f3a 100644 --- a/.clang-format +++ b/.clang-format @@ -37,7 +37,7 @@ SortIncludes: Never SortUsingDeclarations: Never SpaceAfterCStyleCast: true SpaceAfterTemplateKeyword: false -SpaceBeforeRangeBasedForLoopColon: false +SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: false SpacesInAngles: false SpacesInContainerLiterals: false diff --git a/examples/ExampleApps/MovingCubeApp.cpp b/examples/ExampleApps/MovingCubeApp.cpp index a866e9f..d33dcd8 100644 --- a/examples/ExampleApps/MovingCubeApp.cpp +++ b/examples/ExampleApps/MovingCubeApp.cpp @@ -19,11 +19,15 @@ #include "Scene/MorphableCameraController.hpp" #include "Scene/PlaneCameraController.hpp" #include "Scene/UI/PerformanceInfo.hpp" +#include "Scene/SceneIntersectionTestController.hpp" #include "Input/InputManager.hpp" #include "Host/GraphicsAppManager.hpp" #include "Base/EngineConfiguration.hpp" #include "Base/Logger.hpp" #include "Controller/FreeCamCameraController.hpp" +#include "Scene/Prefabs/LabelDrawable.hpp" +#include "Scene/SimpleDrawable.hpp" +#include "Scene/Ray.hpp" #define USE_PLANE_CAM_CONTROL 0 @@ -54,6 +58,7 @@ namespace OpenVulkano Scene::SimpleAnimationController m_simpleAnimationController; Scene::SequenceAnimationController m_sequenceAnimationController; + Scene::SceneIntersectionTestController intersectionTestController; Scene::UI::SimpleUi m_ui; std::shared_ptr m_perfInfo; @@ -154,6 +159,10 @@ namespace OpenVulkano m_shader.AddShaderProgram(ShaderProgramType::FRAGMENT, "Shader/basic"); m_shader.AddVertexInputDescription(Vertex::GetVertexInputDescription()); + intersectionTestController.OnHit += EventHandler(this, &MovingCubeAppImpl::OnRayHit); + intersectionTestController.SetCamera(&m_camera); + intersectionTestController.SetDefaultKeybindings(); + GetGraphicsAppManager()->GetRenderer()->SetScene(&m_scene); #if USE_PLANE_CAM_CONTROL m_cameraControl.Init(&m_camera, PLANE_NORMAL); @@ -219,12 +228,23 @@ namespace OpenVulkano anim->Reset(); } + void OnRayHit(const Scene::DrawableRayHit &hit) + { + using namespace Scene; + Drawable *d = hit.drawable; + if (SimpleDrawable *sd = dynamic_cast(d)) + { + Logger::APP->info("Ray intersects object {}", sd->GetMesh()->name); + } + } + void Tick() override { m_cameraControl.Tick(); m_morphableCameraControl.Tick(); m_simpleAnimationController.Tick(); m_sequenceAnimationController.Tick(); + intersectionTestController.Tick(); } void Close() override {} diff --git a/examples/main.cpp b/examples/main.cpp index 6d53bf2..002e431 100644 --- a/examples/main.cpp +++ b/examples/main.cpp @@ -19,8 +19,6 @@ using namespace OpenVulkano; int main(int argc, char** argv) { - GraphicsAppManager manager; - std::vector examples; for (const auto& e : EXAMPLE_APPS) { @@ -38,7 +36,8 @@ int main(int argc, char** argv) if (selectedExample >= examples.size()) throw std::runtime_error("Invalid menu selection!"); std::unique_ptr app( EXAMPLE_APPS[selectedExample].second() ); - manager.SetApp(app.get()); + + GraphicsAppManager manager(app.get()); manager.Run(); return 0; } diff --git a/openVulkanoCpp/Controller/CameraController.cpp b/openVulkanoCpp/Controller/CameraController.cpp deleted file mode 100644 index 1a8d195..0000000 --- a/openVulkanoCpp/Controller/CameraController.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "CameraController.hpp" -#include "Scene/Camera.hpp" -#include "Input/InputDeviceMouse.hpp" -#include "Input/InputDeviceController.hpp" -#include "Input/InputManager.hpp" -#include "Input/Touch/InputDeviceTouch.hpp" - - -namespace OpenVulkano -{ - CameraController::CameraController(Scene::Camera* camera) - { - m_inputManager = Input::InputManager::GetInstance(); - m_camera = camera; - m_actionCastRay = m_inputManager->GetAction("cast ray"); - m_actionCastRay->BindKey(Input::InputKey::Mouse::BUTTON_1); - m_actionCastRay->BindKey(Input::InputKey::Touch::AXIS_TAP_X); - m_actionCastRay->BindKey(Input::InputKey::Touch::AXIS_TAP_Y); - } - - void CameraController::Tick() - { - Input::InputDeviceMouse* mouse = static_cast(m_inputManager->GetDevice(Input::InputDeviceType::MOUSE)); - Input::InputDeviceTouch* touch = static_cast(m_inputManager->GetDevice(Input::InputDeviceType::TOUCH)); - if (m_camera->CanCastRay() && m_inputManager->GetButton(m_actionCastRay)) - { - if (mouse && mouse->GetButtonDown(Input::InputKey::Mouse::BUTTON_1)) - { - Math::Vector2i pos = mouse->GetMousePosition(); - mouse->onLeftButtonClick.NotifyAll(pos); - } - // not sure about second condition here, but here should be something - else if (touch && (touch->GetAxis(Input::InputKey::Touch::AXIS_TAP_X) != 0 - || touch->GetAxis(Input::InputKey::Touch::AXIS_TAP_Y) != 0)) - { - Math::Vector2i pos = touch->GetTapPosition(); - touch->OnTap.NotifyAll(pos); - } - } - } -} diff --git a/openVulkanoCpp/Controller/CameraController.hpp b/openVulkanoCpp/Controller/CameraController.hpp index 9650b04..80bfa2d 100644 --- a/openVulkanoCpp/Controller/CameraController.hpp +++ b/openVulkanoCpp/Controller/CameraController.hpp @@ -11,12 +11,6 @@ namespace OpenVulkano { - namespace Input - { - class InputAction; - class InputManager; - } - namespace Scene { class Camera; @@ -24,26 +18,22 @@ namespace OpenVulkano class CameraController : public ITickable, ICloseable { - protected: Scene::Camera* m_camera; - Input::InputAction* m_actionCastRay; - Input::InputManager* m_inputManager; - CameraController(Scene::Camera* camera = nullptr); + protected: + CameraController(Scene::Camera* camera = nullptr) + : m_camera(camera) + {} public: ~CameraController() override = default; - void Tick() override; - virtual void Init(Scene::Camera* camera) { m_camera = camera; } void Close() override { m_camera = nullptr; } void SetCamera(Scene::Camera* camera) { m_camera = camera; } - Input::InputAction* GetActionCastRay() { return m_actionCastRay; } - Scene::Camera* GetCamera() { return m_camera; } virtual void SetActive() {} diff --git a/openVulkanoCpp/Controller/FreeCamCameraController.cpp b/openVulkanoCpp/Controller/FreeCamCameraController.cpp index 7f0a082..8f94d3e 100644 --- a/openVulkanoCpp/Controller/FreeCamCameraController.cpp +++ b/openVulkanoCpp/Controller/FreeCamCameraController.cpp @@ -8,7 +8,6 @@ #include "Base/FrameMetadata.hpp" #include "Scene/Camera.hpp" #include "Input/InputManager.hpp" -#include "Input/InputDeviceMouse.hpp" #include "Input/InputKey.hpp" namespace OpenVulkano @@ -40,8 +39,6 @@ namespace OpenVulkano vec *= m_boostFactor; } - CameraController::Tick(); - m_yaw -= input->GetAxis(m_actionLookSide) * timeScale / 2.0f; m_pitch -= input->GetAxis(m_actionLookUp) * timeScale / 2.0f; m_pitch = std::min(1.4f, std::max(-1.4f, m_pitch)); @@ -51,7 +48,6 @@ namespace OpenVulkano Math::Matrix4f camTransformation = Math::Utils::toMat4(rot); camTransformation[3] = Math::Vector4f(m_position, 1); GetCamera()->SetMatrix(camTransformation); - //CameraController::Tick(); CURRENT_FRAME.needsRedraw = true; } diff --git a/openVulkanoCpp/Host/GraphicsAppManager.cpp b/openVulkanoCpp/Host/GraphicsAppManager.cpp index 3b07ddd..8457e07 100644 --- a/openVulkanoCpp/Host/GraphicsAppManager.cpp +++ b/openVulkanoCpp/Host/GraphicsAppManager.cpp @@ -24,64 +24,65 @@ namespace OpenVulkano { using clock = std::chrono::steady_clock; - GraphicsAppManager::GraphicsAppManager(RenderAPI::RenderApi renderApi) { Init(nullptr, nullptr, renderApi); } - GraphicsAppManager::GraphicsAppManager(OpenVulkano::IGraphicsApp* app, RenderAPI::RenderApi renderApi) : app(app), renderApi(renderApi) - { - Init(app, nullptr, renderApi); - } - - GraphicsAppManager::GraphicsAppManager(IGraphicsApp* app, IWindow* window, RenderAPI::RenderApi renderApi) - : app(app), renderApi(renderApi) - { - Init(app, window, renderApi); - } - - void GraphicsAppManager::SetApp(IGraphicsApp* app) - { - this->app = app; - app->SetGraphicsAppManager(this); - } - - void GraphicsAppManager::Init(IGraphicsApp* app, IWindow* window, RenderAPI::RenderApi renderApi) { Utils::SetThreadName("Main"); #ifdef HAS_TRACY ZoneScoped; #endif + + Logger::SetupLogger(); - Logger::MANAGER->info("Initializing graphics app manager ..."); - this->renderApi = renderApi; + if (!app) + { + constexpr auto msg = "The app must not be null!"; + Logger::MANAGER->error(msg); + throw std::runtime_error(msg); + } platform = std::unique_ptr(PlatformProducer::CreatePlatform(renderApi)); + window = platform->MakeWindow(); renderer = std::unique_ptr(PlatformProducer::CreateRenderManager(renderApi)); + app->SetGraphicsAppManager(this); + window->SetWindowHandler(this); inputManager = Input::InputManager::GetInstance(); engineConfig = EngineConfiguration::GetEngineConfiguration(); engineConfig->OnFpsCapChanged += EventHandler(this, &GraphicsAppManager::UpdateCappedFpsInfo); - if (window) - { - this->window = window; - } - else - { - this->window = platform->MakeWindow(); - } - if (app) - { - app->SetGraphicsAppManager(this); - } - this->window->SetWindowHandler(this); - // set initial values if (engineConfig->GetFpsCap() > 0) { UpdateCappedFpsInfo(engineConfig->GetFpsCap()); } - if (platform) + } + + GraphicsAppManager::GraphicsAppManager(IGraphicsApp* app, IWindow* window, RenderAPI::RenderApi renderApi) + : app(app), renderApi(renderApi) + { + Utils::SetThreadName("Main"); +#ifdef HAS_TRACY + ZoneScoped; +#endif + + Logger::SetupLogger(); + if (!app) { - platform->Init(); + constexpr auto msg = "The app must not be null!"; + Logger::MANAGER->error(msg); + throw std::runtime_error(msg); + } + //platform = std::unique_ptr(PlatformProducer::CreatePlatform(renderApi)); + this->window = window; + renderer = std::unique_ptr(PlatformProducer::CreateRenderManager(renderApi)); + app->SetGraphicsAppManager(this); + window->SetWindowHandler(this); + inputManager = OpenVulkano::Input::InputManager::GetInstance(); + engineConfig = OpenVulkano::EngineConfiguration::GetEngineConfiguration(); + engineConfig->OnFpsCapChanged += EventHandler(this, &GraphicsAppManager::UpdateCappedFpsInfo); + // set initial values + if (engineConfig->GetFpsCap() > 0) + { + UpdateCappedFpsInfo(engineConfig->GetFpsCap()); } - Logger::MANAGER->info("Initialized..."); } void GraphicsAppManager::UpdateCappedFpsInfo(int32_t newFpsCap) @@ -143,12 +144,6 @@ namespace OpenVulkano void GraphicsAppManager::StartUp() { - if (!app) - { - constexpr auto msg = "The app must not be null!"; - Logger::MANAGER->error(msg); - throw std::runtime_error(msg); - } #ifdef HAS_TRACY ZoneScoped; #endif @@ -156,9 +151,7 @@ namespace OpenVulkano { Logger::MANAGER->info("Initializing ..."); app->Init(); - - //if (platform) platform->Init(); - + if (platform) platform->Init(); window->Init(renderApi); //TODO restore window settings if there are any set renderer->Init(static_cast(this), window); diff --git a/openVulkanoCpp/Host/GraphicsAppManager.hpp b/openVulkanoCpp/Host/GraphicsAppManager.hpp index d07b626..d48704a 100644 --- a/openVulkanoCpp/Host/GraphicsAppManager.hpp +++ b/openVulkanoCpp/Host/GraphicsAppManager.hpp @@ -48,17 +48,10 @@ namespace OpenVulkano void OnCappedFPS(const auto& frameStartTime); void UpdateCappedFpsInfo(int32_t newFpsCap); public: - - GraphicsAppManager(RenderAPI::RenderApi renderApi = RenderAPI::Vulkan); - explicit GraphicsAppManager(IGraphicsApp* app, RenderAPI::RenderApi renderApi = RenderAPI::Vulkan); explicit GraphicsAppManager(IGraphicsApp* app, IWindow* window, RenderAPI::RenderApi renderApi = RenderAPI::Vulkan); - void SetApp(IGraphicsApp* app); - - void Init(IGraphicsApp* app, IWindow* window, RenderAPI::RenderApi renderApi); - ~GraphicsAppManager() noexcept override; public: // Getter diff --git a/openVulkanoCpp/Input/InputDeviceMouse.cpp b/openVulkanoCpp/Input/InputDeviceMouse.cpp index 626e905..a320e89 100644 --- a/openVulkanoCpp/Input/InputDeviceMouse.cpp +++ b/openVulkanoCpp/Input/InputDeviceMouse.cpp @@ -33,6 +33,8 @@ namespace OpenVulkano::Input { axes[InputKey::Mouse::AXIS_X] = static_cast(posX - mousePosX); axes[InputKey::Mouse::AXIS_Y] = static_cast(posY - mousePosY); + axes[InputKey::Mouse::AXIS_X_ABS] = posX; + axes[InputKey::Mouse::AXIS_Y_ABS] = posY; mousePosX = posX; mousePosY = posY; Logger::INPUT->trace("Mouse moved posX: {0} posY: {1}, relativeX: {2}, relativeY: {3}", posX, posY, axes[InputKey::Mouse::AXIS_X], axes[InputKey::Mouse::AXIS_Y]); @@ -53,9 +55,9 @@ namespace OpenVulkano::Input void InputDeviceMouse::ClearAxes() { - for (float& axis : axes) + for (int i = 2; i < AXES_SIZE; i++) { - axis = 0; + axes[i] = 0; } } } \ No newline at end of file diff --git a/openVulkanoCpp/Input/InputDeviceMouse.hpp b/openVulkanoCpp/Input/InputDeviceMouse.hpp index 29f2cf1..21a5944 100644 --- a/openVulkanoCpp/Input/InputDeviceMouse.hpp +++ b/openVulkanoCpp/Input/InputDeviceMouse.hpp @@ -8,7 +8,6 @@ #include "InputDevice.hpp" #include "Math/Math.hpp" -#include "Base/Event.hpp" namespace OpenVulkano { @@ -18,7 +17,8 @@ namespace OpenVulkano { class InputDeviceMouse : public InputDevice { - float axes[InputKey::Mouse::Axis::AXIS_LAST + 2] = { 0 }; + static constexpr int AXES_SIZE = InputKey::Mouse::Axis::AXIS_LAST + 2; + float axes[AXES_SIZE] = { 0 }; uint8_t pressedButtons = 0, lastPressedButtons = 0; double mousePosX = 0, mousePosY = 0; IWindow* lastWindow = nullptr; @@ -144,9 +144,6 @@ namespace OpenVulkano { return window == lastWindow; } - - Event onLeftButtonClick; - }; } } \ No newline at end of file diff --git a/openVulkanoCpp/Input/InputKey.hpp b/openVulkanoCpp/Input/InputKey.hpp index 7640e4a..8e37b7b 100644 --- a/openVulkanoCpp/Input/InputKey.hpp +++ b/openVulkanoCpp/Input/InputKey.hpp @@ -177,7 +177,9 @@ namespace OpenVulkano::Input enum Axis : int16_t { - AXIS_X = 0, + AXIS_X_ABS = 0, + AXIS_Y_ABS, + AXIS_X, AXIS_Y, AXIS_WHEEL_X, AXIS_WHEEL_Y, diff --git a/openVulkanoCpp/Input/InputManager.cpp b/openVulkanoCpp/Input/InputManager.cpp index af918a3..b93efbd 100644 --- a/openVulkanoCpp/Input/InputManager.cpp +++ b/openVulkanoCpp/Input/InputManager.cpp @@ -75,6 +75,23 @@ namespace OpenVulkano::Input return false; } + bool InputManager::GetButtonDown(InputAction* action) const + { + const std::vector& testDevices = action->GetDevices().empty() ? devices : action->GetDevices(); + for (const InputDevice* device : testDevices) + { + for (const KeyBinding binding : action->GetKeys()) + { + if (binding.key.GetInputDeviceType() != device->GetType()) + { + continue; + } + return device->GetButtonDown(binding.key); + } + } + return false; + } + bool InputManager::GetButton(InputKey key) const { for(const InputDevice* device : devices) @@ -87,9 +104,13 @@ namespace OpenVulkano::Input InputDevice* InputManager::GetDevice(InputDeviceType type) const { - for (InputDevice* device: devices) + if (type == InputDeviceType::UNKNOWN) { - if (device->GetType() == type && type != InputDeviceType::UNKNOWN) + return nullptr; + } + for (InputDevice* device : devices) + { + if (device->GetType() == type) { return device; } @@ -97,6 +118,24 @@ namespace OpenVulkano::Input return nullptr; } + std::vector InputManager::GetDevices(InputDeviceType type) const + { + if (type == InputDeviceType::UNKNOWN) + { + return {}; + } + std::vector devices; + devices.reserve(this->devices.size()); + for (InputDevice* device : this->devices) + { + if (device->GetType() == type) + { + devices.push_back(device); + } + } + return devices; + } + void InputManager::Tick() { for(InputDevice* device : devices) diff --git a/openVulkanoCpp/Input/InputManager.hpp b/openVulkanoCpp/Input/InputManager.hpp index 774b65c..116effb 100644 --- a/openVulkanoCpp/Input/InputManager.hpp +++ b/openVulkanoCpp/Input/InputManager.hpp @@ -40,10 +40,14 @@ namespace OpenVulkano::Input [[nodiscard]] bool GetButton(InputAction* action) const; + [[nodiscard]] bool GetButtonDown(InputAction* action) const; + [[nodiscard]] bool GetButton(InputKey key) const; [[nodiscard]] InputDevice* GetDevice(InputDeviceType type) const; + [[nodiscard]] std::vector GetDevices(InputDeviceType type) const; + [[nodiscard]] InputDevice* GetLastActiveDevice() const { return lastActiveDevice; diff --git a/openVulkanoCpp/Input/Touch/InputDeviceTouch.hpp b/openVulkanoCpp/Input/Touch/InputDeviceTouch.hpp index 8fde139..7d0dfa7 100644 --- a/openVulkanoCpp/Input/Touch/InputDeviceTouch.hpp +++ b/openVulkanoCpp/Input/Touch/InputDeviceTouch.hpp @@ -84,6 +84,5 @@ namespace OpenVulkano::Input [[nodiscard]] bool IsMultiTouch() const { return m_multiTouch; } Event OnTouchAdded, OnTouchDown, OnTouchUp, OnTouchMoved, OnTouchRemoved; - Event OnTap; }; } diff --git a/openVulkanoCpp/Scene/Camera.cpp b/openVulkanoCpp/Scene/Camera.cpp index 34d4533..a0d45b8 100644 --- a/openVulkanoCpp/Scene/Camera.cpp +++ b/openVulkanoCpp/Scene/Camera.cpp @@ -5,24 +5,14 @@ */ #include "Camera.hpp" -#include "Scene/Scene.hpp" -#include "Scene/SimpleDrawable.hpp" -#include "Scene/Geometry.hpp" -#include "Scene/Shader/Shader.hpp" -#include "Scene/TextDrawable.hpp" -#include "Scene/Prefabs/LabelDrawable.hpp" -#include "Scene/Ray.hpp" -#include "Scene/TextDrawable.hpp" namespace OpenVulkano::Scene { - std::optional PerspectiveCamera::CastRay(const Math::Vector2i& xy) const + Ray Camera::CastRay(const Math::Vector2i& xy) const { using namespace Math::Utils; - auto scene = GetScene(); - // nds float ndsX = (2.f * xy.x) / m_width - 1.0f; float ndsY = (2.f * xy.y) / m_height - 1.0f; @@ -34,35 +24,8 @@ namespace OpenVulkano::Scene rayEye.a = 0; Math::Vector3f rayWorld = normalize(inverse(m_view) * rayEye); - - std::optional res; - for (Drawable* d: scene->rayHittableDrawables) - { - const auto& m = d->GetNodes()[0]->GetWorldMatrix(); - const Math::Vector3f rayLocalDir = normalize(inverse(m) * Math::Vector4f(rayWorld, 0)); - const Math::Vector4f rayLocalPos = inverse(m) * m_camPosition; - const Ray ray(rayLocalPos, rayLocalDir); - if (auto hit = d->Intersect(ray)) - { - // choose the closest one - if (!res || (hit->distance < res->distance)) - { - res = hit; - res->drawable = d; - } - } - } - //if (res) - //{ - // if (SimpleDrawable* sd = dynamic_cast(res->drawable)) - // { - // Logger::APP->info("Ray intersects object {}", sd->GetMesh()->name); - // } - // else if (LabelDrawable* sd = dynamic_cast(res->drawable)) - // { - // Logger::APP->info("Ray intersects label {}", sd->GetTexts().front().GetText()); - // } - //} - return res; + + Ray r(GetPosition(), rayWorld); + return r; } } diff --git a/openVulkanoCpp/Scene/Camera.hpp b/openVulkanoCpp/Scene/Camera.hpp index 2788cf6..3735dfe 100644 --- a/openVulkanoCpp/Scene/Camera.hpp +++ b/openVulkanoCpp/Scene/Camera.hpp @@ -9,10 +9,7 @@ #include "Node.hpp" #include "Math/Math.hpp" #include "Math/Frustum.hpp" -#include "Input/InputDeviceMouse.hpp" -#include "Input/Touch/InputDeviceTouch.hpp" -#include "Input/InputManager.hpp" -#include "Base/Logger.hpp" +#include "Scene/Ray.hpp" #include namespace OpenVulkano::Scene @@ -31,7 +28,6 @@ namespace OpenVulkano::Scene float m_contentScaleFactor = 1, m_zoom = 1; // For use with ortho camera float m_interfaceOrientation = 0; float m_padding = 0; //Unused - bool m_canCastRay = true; Camera() : m_nearPlane(0), m_farPlane(0), m_width(0), m_height(0) {} @@ -50,20 +46,6 @@ namespace OpenVulkano::Scene m_farPlane = farPlane; Node::Init(); UpdateProjectionMatrix(); - if (m_canCastRay) - { - Input::InputDeviceMouse* mouse = static_cast(Input::InputManager::GetInstance()->GetDevice(Input::InputDeviceType::MOUSE)); - Input::InputDeviceTouch* touch = static_cast(Input::InputManager::GetInstance()->GetDevice(Input::InputDeviceType::TOUCH)); - if (mouse) - { - mouse->onLeftButtonClick += EventHandler(this, &Camera::CastRay); - } - else if (touch) - { - touch->OnTap += EventHandler(this, &Camera::CastRay); - } - } - } public: @@ -85,8 +67,6 @@ namespace OpenVulkano::Scene [[nodiscard]] float FarPlane() const { return m_farPlane; } - [[nodiscard]] bool CanCastRay() const { return m_canCastRay; } - void SetContentScaleFactor(float contentScale = 1) { m_contentScaleFactor = 1.0f / contentScale; @@ -104,7 +84,7 @@ namespace OpenVulkano::Scene virtual void UpdateProjectionMatrix() = 0; - virtual std::optional CastRay(const Math::Vector2i& xy) const = 0; + Ray CastRay(const Math::Vector2i& xy) const; void UpdateViewProjectionMatrix() { @@ -264,8 +244,6 @@ namespace OpenVulkano::Scene SetProjectionMatrix(Math::Utils::perspectiveRH_ZO(m_fov, m_aspect, m_nearPlane, m_farPlane)); } - std::optional CastRay(const Math::Vector2i& xy) const override; - [[nodiscard]] bool IsPerspective() const override { return true; } }; @@ -279,11 +257,6 @@ namespace OpenVulkano::Scene SetProjectionMatrix(Math::Utils::orthoRH_ZO(-widthHalf, widthHalf, -heightHalf, heightHalf, m_nearPlane, m_farPlane)); } - std::optional CastRay(const Math::Vector2i& xy) const override - { - throw std::runtime_error("Not implemented yet"); - } - [[nodiscard]] bool IsOrtho() const override { return true; } }; } diff --git a/openVulkanoCpp/Scene/Ray.cpp b/openVulkanoCpp/Scene/Ray.cpp index c082328..289e46d 100644 --- a/openVulkanoCpp/Scene/Ray.cpp +++ b/openVulkanoCpp/Scene/Ray.cpp @@ -41,7 +41,7 @@ namespace OpenVulkano::Scene RayHit hitRes; if (intersectRaySphere(m_origin, m_dir, center, radius, hitRes.point, hitRes.normal)) { - hitRes.distance = distance(m_origin, hitRes.point); + hitRes.distance2 = distance2(m_origin, hitRes.point); return hitRes; } return {}; @@ -57,6 +57,7 @@ namespace OpenVulkano::Scene Math::Vector3f e = v1 - v0; Math::Vector3f e2 = v2 - v0; // triangle normal. won't work if triangle is smoothly shaded. use other overloaded method instead. + hitRes.distance2 = distance2(m_origin, hitRes.point); hitRes.normal = normalize(cross(e, e2)); return hitRes; } @@ -99,7 +100,7 @@ namespace OpenVulkano::Scene case 1: return h1; case 2: - return (h1.distance < h2.distance) ? h1 : h2; + return (h1.distance2 < h2.distance2) ? h1 : h2; } return {}; } @@ -169,8 +170,8 @@ namespace OpenVulkano::Scene p1.point = m_origin + txmin * m_dir; p2.point = m_origin + txmax * m_dir; - p1.distance = distance(m_origin, p1.point); - p2.distance = distance(m_origin, p2.point); + p1.distance2 = distance2(m_origin, p1.point); + p2.distance2 = distance2(m_origin, p2.point); p1.normal = p2.normal = Math::Vector3f(0); return intersections; } @@ -182,6 +183,7 @@ namespace OpenVulkano::Scene if (intersectRayPlane(m_origin, m_dir, pOrigin, pNorm, hit.distance)) { hit.point = m_origin + m_dir * hit.distance; + hit.distance2 = distance2(m_origin, hit.point); hit.normal = norm; return hit; } @@ -214,7 +216,7 @@ namespace OpenVulkano::Scene return 0; } p1.point = m_origin + x1 * m_dir; - p1.distance = distance(m_origin, p1.point); + p1.distance2 = distance2(m_origin, p1.point); p1.normal = normalize(p1.point - center); p2 = p1; } @@ -228,10 +230,10 @@ namespace OpenVulkano::Scene if (x1 >= 0 && x2 >= 0) { p1.point = m_origin + x1 * m_dir; - p1.distance = distance(m_origin, p1.point); + p1.distance2 = distance2(m_origin, p1.point); p1.normal = normalize(p1.point - center); p2.point = m_origin + x2 * m_dir; - p2.distance = distance(m_origin, p2.point); + p2.distance2 = distance2(m_origin, p2.point); p2.normal = normalize(p2.point - center); } else @@ -242,11 +244,18 @@ namespace OpenVulkano::Scene x1 = x2; } p1.point = m_origin + x1 * m_dir; - p1.distance = distance(m_origin, p1.point); + p1.distance2 = distance2(m_origin, p1.point); p1.normal = normalize(p1.point - center); p2 = p1; } } return roots; } + + bool RayHit::operator==(const RayHit& other) const + { + return distance2 == other.distance2 && point == other.point && normal == other.normal; + } + + bool RayHit::operator!=(const RayHit& other) const { return !(*this == other); } } diff --git a/openVulkanoCpp/Scene/Ray.hpp b/openVulkanoCpp/Scene/Ray.hpp index 044c14a..6413eaa 100644 --- a/openVulkanoCpp/Scene/Ray.hpp +++ b/openVulkanoCpp/Scene/Ray.hpp @@ -13,14 +13,26 @@ namespace OpenVulkano::Scene { class Drawable; + class Node; struct RayHit { Math::Vector3f point; Math::Vector3f normal; - float distance; - bool operator==(const RayHit& other) const = default; - bool operator!=(const RayHit& other) const = default; + float distance2; + [[nodiscard]] float GetDistance() const + { + if (distance == -1) + { + distance = std::sqrt(distance2); + } + return distance; + } + bool operator==(const RayHit& other) const; + bool operator!=(const RayHit& other) const; + friend class Ray; + private: + mutable float distance = -1; }; struct DrawableRayHit : RayHit @@ -28,6 +40,7 @@ namespace OpenVulkano::Scene DrawableRayHit() = default; DrawableRayHit(const RayHit& hit) : RayHit(hit) {}; Drawable* drawable = nullptr; + Node* node = nullptr; }; class Ray diff --git a/openVulkanoCpp/Scene/SceneIntersectionTestController.cpp b/openVulkanoCpp/Scene/SceneIntersectionTestController.cpp new file mode 100644 index 0000000..bb18d68 --- /dev/null +++ b/openVulkanoCpp/Scene/SceneIntersectionTestController.cpp @@ -0,0 +1,68 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "Input/InputManager.hpp" +#include "SceneIntersectionTestController.hpp" +#include "Scene.hpp" +#include "SimpleDrawable.hpp" +#include "Prefabs/LabelDrawable.hpp" + +namespace OpenVulkano::Scene +{ + void SceneIntersectionTestController::Tick() + { + Input::InputManager* input = Input::InputManager::GetInstance(); + if (input->GetButtonDown(m_actionClick)) + { + const float x = input->GetAxis(m_actionClickX); + const float y = input->GetAxis(m_actionClickY); + const Ray ray = m_camera->CastRay(Math::Vector2i(x, y)); + const auto& camPos = m_camera->GetPosition(); + const auto scene = m_camera->GetScene(); + std::optional res; + for (Drawable* d : scene->rayHittableDrawables) + { + for (Node* n : d->GetNodes()) + { + const auto& m = n->GetWorldMatrix(); + const Math::Vector3f rayLocalDir = normalize(inverse(m) * Math::Vector4f(ray.GetDir(), 0)); + const Math::Vector4f rayLocalPos = inverse(m) * camPos; + const Ray ray(rayLocalPos, rayLocalDir); + if (auto hit = d->Intersect(ray)) + { + // choose the closest one + if (!res || (hit->distance2 < res->distance2)) + { + res = hit; + res->drawable = d; + res->node = n; + } + } + } + } + + if (res) + { + OnHit.NotifyAll(*res); + } + } + } + + void SceneIntersectionTestController::SetDefaultKeybindings() + { + auto input = Input::InputManager::GetInstance(); + m_actionClick = input->GetAction("ClickIntersection"); + m_actionClick->BindKey(Input::InputKey::Mouse::BUTTON_1); + m_actionClick->BindKey(Input::InputKey::Touch::BUTTON_TAP); + + m_actionClickX = input->GetAction("ClickIntersectionPosX"); + m_actionClickX->BindKey(Input::InputKey::Touch::Axis::AXIS_TAP_X); + m_actionClickX->BindKey(Input::InputKey::Mouse::Axis::AXIS_X_ABS); + m_actionClickY = input->GetAction("ClickIntersectionPosY"); + m_actionClickY->BindKey(Input::InputKey::Touch::Axis::AXIS_TAP_Y); + m_actionClickY->BindKey(Input::InputKey::Mouse::Axis::AXIS_Y_ABS); + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/SceneIntersectionTestController.hpp b/openVulkanoCpp/Scene/SceneIntersectionTestController.hpp new file mode 100644 index 0000000..f731c8c --- /dev/null +++ b/openVulkanoCpp/Scene/SceneIntersectionTestController.hpp @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "Base/ITickable.hpp" +#include "Base/Event.hpp" +#include "Scene/Ray.hpp" +#include "Input/InputAction.hpp" +#include "Camera.hpp" + +namespace OpenVulkano::Scene +{ + class SceneIntersectionTestController : public ITickable + { + public: + SceneIntersectionTestController() = default; + SceneIntersectionTestController(Camera* camera) : m_camera(camera) {} + void SetCamera(Camera* camera) { m_camera = camera; } + [[nodiscard]] Camera* GetCamera() const { return m_camera; } + void Tick() override; + void SetDefaultKeybindings(); + Event OnHit; + + private: + Input::InputAction* m_actionClick = nullptr; + Input::InputAction* m_actionClickX = nullptr; + Input::InputAction* m_actionClickY = nullptr; + Camera* m_camera; + }; +} \ No newline at end of file diff --git a/tests/RayTests.cpp b/tests/RayTests.cpp index 0f25abd..73a6eca 100644 --- a/tests/RayTests.cpp +++ b/tests/RayTests.cpp @@ -28,48 +28,56 @@ namespace TEST_CASE("RaySphereIntersection") { auto sphere = GeometryFactory::MakeSphere(1, 32, 16); - RayHit h1, h2; // 2 intersections { + RayHit h1, h2; Ray ray(Vector3f(0, 0, -5), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 2); REQUIRE((h1.point == Vector3f(0, 0, -1) && h2.point == Vector3f(0, 0, 1))); REQUIRE((h1.normal == Vector3f(0, 0, -1) && h2.normal == Vector3f(0, 0, 1))); - REQUIRE(h1.distance < h2.distance); - REQUIRE((h1.distance == distance(ray.GetOrigin(), h1.point) && h2.distance == distance(ray.GetOrigin(), h2.point))); + REQUIRE(h1.distance2 < h2.distance2); + REQUIRE((h1.GetDistance() == distance(ray.GetOrigin(), h1.point) + && h2.GetDistance() == distance(ray.GetOrigin(), h2.point))); + REQUIRE_THAT(h1.GetDistance(), Catch::Matchers::WithinRel(std::sqrt(h1.distance2))); // this returns just closest point if (auto opt = ray.IntersectSphere(Vector3f(0), 1)) { + opt->GetDistance(); REQUIRE(opt.value() == h1); } } // 1 intersection { + RayHit h1, h2; Ray ray(Vector3f(1, 0, -1), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 1); REQUIRE(h1 == h2); REQUIRE(h1.point == Vector3f(1, 0, 0)); - REQUIRE(ray.IntersectSphere(Vector3f(0), 1).value() == h1); + REQUIRE(ray.IntersectSphere(Vector3f(0), 1) == h1); } // 0 intersections { + RayHit h1, h2; Ray ray(Vector3f(2, 0, -1), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 0); REQUIRE(!ray.IntersectSphere(Vector3f(0), 1).has_value()); } // ray is inside sphere { + RayHit h1, h2; Ray ray(Vector3f(0, 0, 0), Vector3f(0.5, 0.5, 1)); REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 1); REQUIRE(h1 == h2); ::CompareVec3Approx(h1.normal, h1.point); auto value = ray.IntersectSphere(Vector3f(0), 1); - REQUIRE(value->distance == h1.distance); + REQUIRE(value->GetDistance() == h1.GetDistance()); + REQUIRE_THAT(value->distance2, Catch::Matchers::WithinRel(h1.distance2)); ::CompareVec3Approx(value->normal, h1.normal); ::CompareVec3Approx(value->point, h1.point); } // ray intersects sphere behind the origin { + RayHit h1, h2; Ray ray(Vector3f(0, 0, 3), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectSphere(Vector3f(0), 1, h1, h2) == 0); REQUIRE(!ray.IntersectSphere(Vector3f(0), 1).has_value()); @@ -79,31 +87,30 @@ TEST_CASE("RaySphereIntersection") TEST_CASE("RayTriangleIntersection") { auto tri = GeometryFactory::MakeTriangle(Vector3f(0), Vector3f(3, 0, 0), Vector3f(1.5, 2, 0)); - std::optional hit; // intersects { Ray ray(Vector3f(1.5, 2, -5), Vector3f(0, 0, 1)); - hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); + std::optional hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); REQUIRE(hit.has_value()); - REQUIRE(hit->distance == distance(ray.GetOrigin(), hit->point)); + REQUIRE(hit->GetDistance() == distance(ray.GetOrigin(), hit->point)); REQUIRE(hit->point == Vector3f(1.5, 2, 0)); } { Ray ray(Vector3f(1.5, 1, -1), Vector3f(0, 0, 1)); - hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); + std::optional hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); REQUIRE(hit.has_value()); - REQUIRE(hit->distance == distance(ray.GetOrigin(), hit->point)); + REQUIRE(hit->GetDistance() == distance(ray.GetOrigin(), hit->point)); REQUIRE(hit->point == Vector3f(1.5, 1, 0)); } // no intersections { Ray ray(Vector3f(5, 0, 0), Vector3f(0, 0, 1)); - hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); + std::optional hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); REQUIRE(!hit.has_value()); } { Ray ray(Vector3f(1.5, 1, 0.5), Vector3f(0, 0, 1)); - hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); + std::optional hit = ray.IntersectTriangle(tri.vertices[0].position, tri.vertices[1].position, tri.vertices[2].position); REQUIRE(!hit.has_value()); } } @@ -141,52 +148,59 @@ TEST_CASE("RayAABBIntersection") auto sphere = GeometryFactory::MakeSphere(1, 32, 16); sphere.CalculateAABB(); std::optional hit; - RayHit h1, h2; // intersects { + RayHit h1, h2; Ray ray(Vector3f(0, 0, -2), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 2); - REQUIRE(h1.distance < h2.distance); + REQUIRE(h1.distance2 < h2.distance2); REQUIRE(h1.point == Vector3f(0, 0, -1)); REQUIRE(h2.point == Vector3f(0, 0, 1)); auto p = ray.IntersectAABB(sphere.aabb); REQUIRE(p->point == h1.point); - REQUIRE(p->distance == h1.distance); + REQUIRE(p->distance2 == h1.distance2); + REQUIRE(p->GetDistance() == h1.GetDistance()); } { + RayHit h1, h2; Ray ray(Vector3f(0, 0, 1), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 1); - REQUIRE(h1.distance == h2.distance); + REQUIRE(h1.distance2 == h2.distance2); CompareVec3Approx(h1.point, h2.point); REQUIRE(h1.point == Vector3f(0, 0, 1)); - REQUIRE(ray.IntersectAABB(sphere.aabb)->distance == h1.distance); + REQUIRE(ray.IntersectAABB(sphere.aabb)->distance2 == h1.distance2); } { + RayHit h1, h2; // inside sphere Ray ray(Vector3f(0), Vector3f(0.3)); REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 1); REQUIRE(h1 == h2); auto val = ray.IntersectAABB(sphere.aabb); REQUIRE(val.has_value()); - REQUIRE_THAT(val->distance, Catch::Matchers::WithinRel(h1.distance)); + REQUIRE_THAT(val->distance2, Catch::Matchers::WithinRel(h1.distance2)); CompareVec3Approx(val->point, h1.point); } { + RayHit h1, h2; Ray ray(Vector3f(2, -0.5, 1.5), Vector3f(-2, 0.5, -1.5)); REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 2); - REQUIRE(h1.distance < h2.distance); + REQUIRE(h1.distance2 < h2.distance2); auto val = ray.IntersectAABB(sphere.aabb); REQUIRE(val.has_value()); - REQUIRE(val->distance == h1.distance); + REQUIRE(val->distance2 == h1.distance2); + REQUIRE_THAT(val->GetDistance(), Catch::Matchers::WithinRel(h1.GetDistance())); CompareVec3Approx(val->point, h1.point); } // no intersections { + RayHit h1, h2; Ray ray(Vector3f(3, 0, 1), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 0); REQUIRE(!ray.IntersectAABB(sphere.aabb).has_value()); } { + RayHit h1, h2; Ray ray(Vector3f(0, 0, 1.1), Vector3f(0, 0, 1)); REQUIRE(ray.IntersectAABB(sphere.aabb, h1, h2) == 0); REQUIRE(!ray.IntersectAABB(sphere.aabb).has_value()); @@ -202,7 +216,7 @@ TEST_CASE("RayPlaneIntersection") auto hit = ray.IntersectPlane(pOrigin, pNorm); REQUIRE(hit.has_value()); REQUIRE(hit->normal == pNorm); - REQUIRE(hit->distance == 2.f); + REQUIRE(hit->GetDistance() == 2.f); REQUIRE(hit->point == Vector3f(2, 0, 2)); } {