diff --git a/config/Sources.cmake b/config/Sources.cmake index 6cf9c6f4663c4307ee6c9350941cbe6e2f2b5a2c..1b3af31e82a5475d7d0791848fee123d62bf917c 100644 --- a/config/Sources.cmake +++ b/config/Sources.cmake @@ -54,6 +54,8 @@ set(vkcv_sources ${vkcv_include}/vkcv/PipelineConfig.hpp ${vkcv_source}/vkcv/PipelineConfig.cpp + ${vkcv_include}/vkcv/Interpolation.hpp + ${vkcv_include}/vkcv/Logger.hpp ${vkcv_include}/vkcv/ShaderStage.hpp diff --git a/include/vkcv/Interpolation.hpp b/include/vkcv/Interpolation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c38a7aa746e36a3a58a0c3dd1c94db549ba101e7 --- /dev/null +++ b/include/vkcv/Interpolation.hpp @@ -0,0 +1,121 @@ +#pragma once +/** + * @authors Tobias Frisch + * @file vkcv/Interpolation.hpp + * @brief Structure for interpolation + */ + +#include <algorithm> +#include <cmath> +#include <functional> +#include <vector> + +namespace vkcv { + + template<typename V, typename T = double> + struct interpolation_function { + typedef std::function<V(const V&, const V&, T)> type; + }; + + template<typename V, typename T = double> + struct interpolation_state { + T time; + V value; + + bool operator<(const interpolation_state& other) const { + return time < other.time; + } + }; + + template<typename V, typename T = double> + struct interpolation { + private: + typename interpolation_function<V, T>::type m_function; + std::vector< interpolation_state<V, T> > m_states; + + public: + interpolation(const typename interpolation_function<V, T>::type& function) + : m_function(function), m_states() {} + + interpolation(const interpolation& other) = default; + interpolation(interpolation&& other) noexcept = default; + + ~interpolation() = default; + + interpolation& operator=(const interpolation& other) = default; + interpolation& operator=(interpolation&& other) noexcept = default; + + void clear() { + m_states.clear(); + } + + void add(T time, const V& value) { + interpolation_state<V, T> state; + state.time = time; + state.value = value; + m_states.insert( + std::lower_bound(m_states.begin(), m_states.end(), state), + state + ); + } + + V operator()(T time) const { + interpolation_state<V, T> state; + state.time = time; + + auto end = std::lower_bound(m_states.begin(), m_states.end(), state); + auto start = end != m_states.begin()? (end - 1) : m_states.begin(); + + if (end == m_states.end()) { + end = start; + } + + const T ratio = (time - (*start).time) / ((*end).time - (*start).time); + + return m_function( + (*start).value, + (*end).value, + std::clamp<T>( + ratio, + static_cast<T>(0), + static_cast<T>(1) + ) + ); + } + + }; + + template<typename V, typename T = double> + interpolation<V, T> linearInterpolation() { + return interpolation<V, T>([](const V& start, const V& end, T ratio) { + return start * (static_cast<T>(1) - ratio) + end * ratio; + }); + } + + template<typename V, typename T = double> + interpolation<V, T> cubicInterpolation() { + return interpolation<V, T>([](const V& start, const V& end, T ratio) { + const T r0 = (static_cast<T>(1) - ratio) * (static_cast<T>(1) - ratio); + const T r1 = ratio * ratio; + + return ( + start * r0 + + start * r0 * ratio * static_cast<T>(2) + + end * r1 * (static_cast<T>(1) - ratio) * static_cast<T>(2) + + end * r1 + ); + }); + } + + template<typename V, typename T = double> + interpolation<V, T> cosInterpolation() { + return interpolation<V, T>([](const V& start, const V& end, T ratio) { + const T cos_ratio = (static_cast<T>(1) - std::cos( + ratio * static_cast<T>(M_PI) + )) / static_cast<T>(2); + + return start * (static_cast<T>(1) - cos_ratio) + end * cos_ratio; + }); + } + +} \ No newline at end of file diff --git a/modules/camera/include/vkcv/camera/CameraController.hpp b/modules/camera/include/vkcv/camera/CameraController.hpp index d6efd8f2f300dead870b3546939dad2088cd466e..0b5a17faf178a01f6e1460fb5b86bff901576c68 100644 --- a/modules/camera/include/vkcv/camera/CameraController.hpp +++ b/modules/camera/include/vkcv/camera/CameraController.hpp @@ -22,7 +22,6 @@ namespace vkcv::camera { * PilotCameraController. */ class CameraController { - public: /** diff --git a/modules/camera/include/vkcv/camera/CameraManager.hpp b/modules/camera/include/vkcv/camera/CameraManager.hpp index 3c6c556d96210287e3be23d19346c5eca6f9c1cd..01b11eedcffcbbf588fa93c48f4762e4f4adbdc6 100644 --- a/modules/camera/include/vkcv/camera/CameraManager.hpp +++ b/modules/camera/include/vkcv/camera/CameraManager.hpp @@ -108,12 +108,6 @@ namespace vkcv::camera { * @return The specified camera controller object. */ CameraController& getControllerByType(ControllerType controllerType); - - /** - * @briof A method to get the currently active controller for the active camera. - * @return Reference to the active #CameraController - */ - CameraController& getActiveController(); public: @@ -152,12 +146,14 @@ namespace vkcv::camera { * @return The camera object by @p cameraHandle. * @throws std::runtime_error If @p cameraHandle is not a valid camera handle. */ + [[nodiscard]] Camera& getCamera(const CameraHandle& cameraHandle); /** * @brief Returns the stored camera object set as the active camera. * @return The active camera. */ + [[nodiscard]] Camera& getActiveCamera(); /** @@ -171,6 +167,7 @@ namespace vkcv::camera { * @brief Returns the handle of the stored active camera object. * @return The active camera handle. */ + [[nodiscard]] CameraHandle getActiveCameraHandle() const; /** @@ -188,6 +185,7 @@ namespace vkcv::camera { * @return The type of the camera controller of the specified camera object. * @throws std::runtime_error If @p cameraHandle is not a valid camera handle. */ + [[nodiscard]] ControllerType getControllerType(const CameraHandle& cameraHandle); /** diff --git a/modules/camera/include/vkcv/camera/ControllerType.hpp b/modules/camera/include/vkcv/camera/ControllerType.hpp index c1a5ef34bf8172956d5c9812f45b73f746c48838..3e85c1f9749ced9680030e95d08a1c6bc39aafb3 100644 --- a/modules/camera/include/vkcv/camera/ControllerType.hpp +++ b/modules/camera/include/vkcv/camera/ControllerType.hpp @@ -19,8 +19,7 @@ namespace vkcv::camera { enum class ControllerType { NONE, PILOT, - TRACKBALL, - TRACE + TRACKBALL }; /** @} */ diff --git a/modules/camera/src/vkcv/camera/CameraManager.cpp b/modules/camera/src/vkcv/camera/CameraManager.cpp index a2ead14ce30d8088ea77ecbfd20d5af29c2c181d..ad5de753a17e7f207cd3e538cb3b96a6c2753083 100644 --- a/modules/camera/src/vkcv/camera/CameraManager.cpp +++ b/modules/camera/src/vkcv/camera/CameraManager.cpp @@ -41,53 +41,78 @@ namespace vkcv::camera { } } - void CameraManager::mouseButtonCallback(int button, int action, int mods){ - if(button == GLFW_MOUSE_BUTTON_2 && action == GLFW_PRESS){ + void CameraManager::mouseButtonCallback(int button, int action, int mods) { + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if ((button == GLFW_MOUSE_BUTTON_2) && (action == GLFW_PRESS)) { glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_DISABLED); - } - else if(button == GLFW_MOUSE_BUTTON_2 && action == GLFW_RELEASE){ + } else + if ((button == GLFW_MOUSE_BUTTON_2) && (action == GLFW_RELEASE)) { glfwSetInputMode(m_window.getWindow(), GLFW_CURSOR, GLFW_CURSOR_NORMAL); } - getActiveController().mouseButtonCallback(button, action, mods, getActiveCamera()); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).mouseButtonCallback(button, action, mods, getActiveCamera()); } - void CameraManager::mouseMoveCallback(double x, double y){ + void CameraManager::mouseMoveCallback(double x, double y) { + const ControllerType type = getControllerType(getActiveCameraHandle()); + auto xoffset = static_cast<float>(x - m_lastX) / m_window.getWidth(); auto yoffset = static_cast<float>(y - m_lastY) / m_window.getHeight(); m_lastX = x; m_lastY = y; - getActiveController().mouseMoveCallback(xoffset, yoffset, getActiveCamera()); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).mouseMoveCallback(xoffset, yoffset, getActiveCamera()); } void CameraManager::scrollCallback(double offsetX, double offsetY) { - getActiveController().scrollCallback(offsetX, offsetY, getActiveCamera()); + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).scrollCallback(offsetX, offsetY, getActiveCamera()); } void CameraManager::keyCallback(int key, int scancode, int action, int mods) { - switch (action) { - case GLFW_RELEASE: - switch (key) { - case GLFW_KEY_TAB: - if (m_activeCameraIndex + 1 == m_cameras.size()) { - m_activeCameraIndex = 0; - } - else { - m_activeCameraIndex++; - } - return; - case GLFW_KEY_ESCAPE: - glfwSetWindowShouldClose(m_window.getWindow(), 1); - return; - default: - break; - } - default: - getActiveController().keyCallback(key, scancode, action, mods, getActiveCamera()); - break; + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if (action == GLFW_RELEASE) { + switch (key) { + case GLFW_KEY_TAB: + if (m_activeCameraIndex + 1 == m_cameras.size()) { + m_activeCameraIndex = 0; + } else { + m_activeCameraIndex++; + } + return; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(m_window.getWindow(), 1); + return; + default: + break; + } } + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).keyCallback(key, scancode, action, mods, getActiveCamera()); } void CameraManager::gamepadCallback(int gamepadIndex) { + const ControllerType type = getControllerType(getActiveCameraHandle()); + // handle camera switching GLFWgamepadstate gamepadState; glfwGetGamepadState(gamepadIndex, &gamepadState); @@ -96,22 +121,23 @@ namespace vkcv::camera { if (time - m_inputDelayTimer > 0.2) { int switchDirection = gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_RIGHT] - gamepadState.buttons[GLFW_GAMEPAD_BUTTON_DPAD_LEFT]; m_activeCameraIndex += switchDirection; - if (std::greater<int>{}(m_activeCameraIndex, m_cameras.size() - 1)) { + + if (std::greater<int>{}(m_activeCameraIndex, m_cameras.size() - 1)) { m_activeCameraIndex = 0; - } - else if (std::less<int>{}(m_activeCameraIndex, 0)) { + } else + if (std::less<int>{}(m_activeCameraIndex, 0)) { m_activeCameraIndex = m_cameras.size() - 1; } + uint32_t triggered = abs(switchDirection); m_inputDelayTimer = (1-triggered)*m_inputDelayTimer + triggered * time; // Only reset timer, if dpad was pressed - is this cheaper than if-clause? } - - getActiveController().gamepadCallback(gamepadIndex, getActiveCamera(), m_frameTime); // handle camera rotation, translation - } - - CameraController& CameraManager::getActiveController() { - const ControllerType type = getControllerType(getActiveCameraHandle()); - return getControllerByType(type); + + if (type == ControllerType::NONE) { + return; + } + + getControllerByType(type).gamepadCallback(gamepadIndex, getActiveCamera(), m_frameTime); // handle camera rotation, translation } CameraHandle CameraManager::addCamera(ControllerType controllerType) { @@ -185,9 +211,17 @@ namespace vkcv::camera { } void CameraManager::update(double deltaTime) { - m_frameTime = deltaTime; + const ControllerType type = getControllerType(getActiveCameraHandle()); + + if (type != ControllerType::NONE) { + m_frameTime = deltaTime; + } else { + m_frameTime = 0.0; + return; + } + if (glfwGetWindowAttrib(m_window.getWindow(), GLFW_FOCUSED) == GLFW_TRUE) { - getActiveController().updateCamera(deltaTime, getActiveCamera()); + getControllerByType(type).updateCamera(m_frameTime, getActiveCamera()); } } diff --git a/projects/first_mesh/src/main.cpp b/projects/first_mesh/src/main.cpp index 2705c61cdb8755136d7c660aeb4a5e494b35a55e..0070b51eb51f81422af49b641a860f75ec832b3f 100644 --- a/projects/first_mesh/src/main.cpp +++ b/projects/first_mesh/src/main.cpp @@ -1,9 +1,11 @@ #include <iostream> #include <vkcv/Core.hpp> #include <vkcv/Image.hpp> +#include <vkcv/Interpolation.hpp> #include <vkcv/Pass.hpp> #include <vkcv/Sampler.hpp> #include <vkcv/camera/CameraManager.hpp> + #include <vkcv/asset/asset_loader.hpp> #include <vkcv/shader/GLSLCompiler.hpp> @@ -104,9 +106,22 @@ int main(int argc, const char** argv) { drawcall.useDescriptorSet(0, descriptorSet); vkcv::camera::CameraManager cameraManager(window); - auto camHandle = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); - cameraManager.getCamera(camHandle).setPosition(glm::vec3(0, 0, -3)); + auto camHandle0 = cameraManager.addCamera(vkcv::camera::ControllerType::NONE); + auto camHandle1 = cameraManager.addCamera(vkcv::camera::ControllerType::PILOT); + auto camHandle2 = cameraManager.addCamera(vkcv::camera::ControllerType::TRACKBALL); + + cameraManager.getCamera(camHandle1).setPosition(glm::vec3(0, 0, -3)); + cameraManager.getCamera(camHandle2).setPosition(glm::vec3(0, 0, -3)); + + auto interp = vkcv::linearInterpolation<glm::vec3, float>(); + + interp.add( 0.0f, glm::vec3(+5, +5, -5)); + interp.add( 2.0f, glm::vec3(+0, +5, -5)); + interp.add( 4.0f, glm::vec3(+0, -3, -3)); + interp.add( 6.0f, glm::vec3(+3, +0, -6)); + interp.add( 8.0f, glm::vec3(+5, +5, +5)); + interp.add(10.0f, glm::vec3(+5, +5, -5)); core.run([&](const vkcv::WindowHandle &windowHandle, double t, double dt, uint32_t swapchainWidth, uint32_t swapchainHeight) { @@ -123,6 +138,10 @@ int main(int argc, const char** argv) { } cameraManager.update(dt); + cameraManager.getCamera(camHandle0).setPosition( + interp(static_cast<float>(std::fmod<double>(t, 10.0))) + ); + glm::mat4 mvp = cameraManager.getActiveCamera().getMVP(); vkcv::PushConstants pushConstants = vkcv::pushConstants<glm::mat4>();