From 92438a9bcecf7596b19d6e1ecc58c63379a01fcc Mon Sep 17 00:00:00 2001 From: Matias Date: Wed, 16 Jul 2025 12:17:35 +0200 Subject: [PATCH] feat: add working wobble! but there's a ton of rendering weirdness --- src/main.cpp | 187 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 165 insertions(+), 22 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 52369fc..72d007d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,21 @@ #include "globals.hpp" #include -#include #include // version.hpp will be generated by meson -#include "src/plugins/PluginAPI.hpp" -#include "src/render/Framebuffer.hpp" -#include "src/render/OpenGL.hpp" -#include "src/render/pass/PassElement.hpp" #include "version.hpp" #include +#include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include @@ -27,10 +30,11 @@ APICALL EXPORT std::string PLUGIN_API_VERSION() { SShader* g_shader {}; -constexpr unsigned int g_SUBDIVS = 2; +constexpr unsigned int g_SUBDIVS = 8; static_assert(g_SUBDIVS > 0); GLuint g_VAO, g_VBO, g_VBO_UVs, g_EBO; +std::vector g_baseVerts; unsigned int g_indexCount = 0; class CBindOwnFramebufferPassElement final: public IPassElement { @@ -66,6 +70,95 @@ class CBindOwnFramebufferPassElement final: public IPassElement { CFramebuffer* m_pFramebuffer; }; +float map_value_in_range( + float value, + float inMin, + float inMax, + float outMin = 0.0, + float outMax = 1.0 +) { + return (value - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; +} + +class CWobblyWindow { + struct SParticle { + Vector2D position; + Vector2D velocity; + }; + + Time::steady_tp m_lastTime {Time::steadyNow()}; + + public: + // static wobble parameters + static inline float s_springStrength = 500.f; + static inline float s_dampingStrength = 8.f; + static inline float s_drag = 0.4f; + + std::vector m_particles; + std::vector m_targetPositions; + std::optional m_grabPosition {std::nullopt}; + Vector2D m_windowMovement; + + CWobblyWindow() { + m_particles.reserve(g_baseVerts.size() / 2); + m_targetPositions.reserve(g_baseVerts.size() / 2); + + for (unsigned int i {}; i < g_baseVerts.size(); i += 2) { + m_particles.push_back(SParticle {Vector2D {g_baseVerts[i], g_baseVerts[i + 1]}, {}}); + m_targetPositions.push_back(Vector2D {g_baseVerts[i], g_baseVerts[i + 1]}); + } + } + + bool step(Time::steady_tp time) { + const auto usec = + std::chrono::duration_cast(time - m_lastTime).count(); + const float dt = usec * 0.000001f; + m_lastTime = time; + + Vector2D totalVel {}; + + for (unsigned int i {}; i < m_particles.size(); i++) { + auto&& particle = m_particles[i]; + auto&& particlePositon = particle.position; + auto&& targetPosition = m_targetPositions[i]; + + auto&& directionToTarget = targetPosition - particlePositon; + + if (m_grabPosition.has_value()) { + auto&& distanceToDragPosition = m_grabPosition.value().distance(particlePositon); + auto&& dragStrength = std::clamp((float)distanceToDragPosition, 0.f, 1.f); + + particle.position += m_windowMovement * dragStrength; + } + + auto&& springForce = + directionToTarget * s_springStrength - particle.velocity * s_dampingStrength; + particle.velocity += springForce * dt; + + // Apply drag + auto&& dragForceVector = (particle.velocity * particle.velocity) * -s_drag; + + // * Area + auto dir = particle.velocity; + float magnitude = dir.normalize(); + + if (magnitude != 0) + particle.velocity += (dragForceVector * dir) * dt; + + // Apply velocity + particle.position += particle.velocity * dt; + + totalVel += particle.velocity; + } + + return totalVel.distanceSq(Vector2D {}) < 0.1f; + } +}; + +inline std::map g_windowFramebuffers; +inline std::map g_windowPositions; +inline std::map g_wobblyWindows; + class CRenderWobblyWindowPassElement final: public IPassElement { public: explicit CRenderWobblyWindowPassElement(CFramebuffer* pOldFramebuffer, PHLWINDOWREF pWindow) : @@ -102,6 +195,7 @@ class CRenderWobblyWindowPassElement final: public IPassElement { GLCALL(glBindVertexArray(g_VAO)); GLCALL(glBindBuffer(GL_ARRAY_BUFFER, g_VBO)); + // TODO: can we avoid this entirely by rendering the window to framebuffer pos 0,0? const Vector2D UVTopLeft = Vector2D {windowBox.x, windowBox.y} / pWindowFB->m_size; const Vector2D UVBottomRight = Vector2D {windowBox.x + windowBox.width, windowBox.y + windowBox.height} @@ -126,6 +220,21 @@ class CRenderWobblyWindowPassElement final: public IPassElement { GLCALL(glBindBuffer(GL_ARRAY_BUFFER, g_VBO_UVs)); GLCALL(glBufferSubData(GL_ARRAY_BUFFER, 0, UVs.size() * sizeof(float), UVs.data())); + if (g_wobblyWindows.contains(m_pWindow)) { + auto&& wobble = g_wobblyWindows[m_pWindow]; + + // TODO: we need the particle positions in their own vec, to avoid this copy + std::vector verts; + verts.reserve(vertsPerRow * vertsPerRow * 2); + for (auto&& particle : wobble.m_particles) { + verts.push_back(particle.position.x); + verts.push_back(particle.position.y); + } + + GLCALL(glBindBuffer(GL_ARRAY_BUFFER, g_VBO)); + GLCALL(glBufferSubData(GL_ARRAY_BUFFER, 0, verts.size() * sizeof(float), verts.data())); + } + GLCALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_EBO)); GLCALL(glDrawElements(GL_TRIANGLE_STRIP, g_indexCount, GL_UNSIGNED_INT, 0)); @@ -154,13 +263,10 @@ class CRenderWobblyWindowPassElement final: public IPassElement { PHLWINDOWREF m_pWindow; }; -void onNewWindow(std::any data) { - // data is guaranteed - auto PWINDOW = std::any_cast(data); +void registerWindow(PHLWINDOW pWindow) { + g_windowPositions[pWindow] = pWindow->m_realPosition->value(); } -inline std::map g_windowFramebuffers; - static SP g_openWindow = nullptr; inline CFunctionHook* g_pRenderWindowHook = nullptr; @@ -188,11 +294,40 @@ void hkRenderWindow( CHyprRenderer* pRenderer = (CHyprRenderer*)thisptr; const bool shouldWobble = [&]() -> bool { - if (mode == RENDER_PASS_MAIN or mode == RENDER_PASS_ALL) { + if (mode != RENDER_PASS_MAIN and mode != RENDER_PASS_ALL) { + return false; + } + + // did the window move + if (const auto pos = pWindow->m_realPosition->value(); pos != g_windowPositions[pWindow]) { + auto&& wobble = g_wobblyWindows[pWindow]; + + const auto windowBox = pWindow->getFullWindowBoundingBox(); + const auto windowSize = Vector2D {windowBox.width, windowBox.height}; + + if (g_pInputManager->m_currentlyDraggedWindow == pWindow) { + if (not wobble.m_grabPosition.has_value()) { + auto&& mousePos = g_pInputManager->getMouseCoordsInternal(); + wobble.m_grabPosition = (mousePos - pos) / windowSize; + } + } else { + wobble.m_grabPosition = std::nullopt; + } + + wobble.m_windowMovement = (pos - g_windowPositions[pWindow]) / windowSize; + for (auto&& particle : wobble.m_particles) { + particle.position -= wobble.m_windowMovement; + } + + // update last pos + g_windowPositions[pWindow] = pos; + } + + if (g_wobblyWindows.contains(pWindow)) { return true; } - return false; + return true; }(); auto* const pOldFramebuffer = g_pHyprOpenGL->m_renderData.currentFB; @@ -232,6 +367,10 @@ void hkRenderWindow( ); if (shouldWobble) { + const bool shouldStop = g_wobblyWindows[pWindow].step(time); + if (shouldStop) + g_wobblyWindows.erase(pWindow); + pRenderer->m_renderPass.add( makeUnique(pOldFramebuffer, pWindow) ); @@ -243,14 +382,13 @@ void initGPUObjects() { // std::vector finalVerts; const unsigned int vertsPerRow = g_SUBDIVS + 1; - std::vector verts; - verts.reserve(vertsPerRow * vertsPerRow * 2); + g_baseVerts.reserve(vertsPerRow * vertsPerRow * 2); const float step = 1.f / (g_SUBDIVS); for (unsigned int y = 0; y < vertsPerRow; ++y) { for (unsigned int x = 0; x < vertsPerRow; ++x) { - verts.push_back(x * step); - verts.push_back(y * step); + g_baseVerts.push_back(x * step); + g_baseVerts.push_back(y * step); } } @@ -289,8 +427,8 @@ void initGPUObjects() { { GLCALL(glBufferData( GL_ARRAY_BUFFER, - verts.size() * sizeof(float), - verts.data(), + g_baseVerts.size() * sizeof(float), + g_baseVerts.data(), GL_DYNAMIC_DRAW )); @@ -309,8 +447,8 @@ void initGPUObjects() { { GLCALL(glBufferData( GL_ARRAY_BUFFER, - verts.size() * sizeof(float), - verts.data(), + g_baseVerts.size() * sizeof(float), + g_baseVerts.data(), GL_DYNAMIC_DRAW )); // Initial dummy UVs GLCALL(glEnableVertexAttribArray(g_shader->uniformLocations[SHADER_TEX_ATTRIB])); @@ -347,10 +485,15 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { initGPUObjects(); + for (auto&& window : g_pCompositor->m_windows) + registerWindow(window); + g_openWindow = HyprlandAPI::registerCallbackDynamic( PHANDLE, "openWindow", - [](void* self, SCallbackInfo& info, std::any data) { onNewWindow(data); } + [](void* self, SCallbackInfo& info, std::any data) { + registerWindow(std::any_cast(data)); + } ); static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "renderWindow");