feat: add working wobble! but there's a ton of rendering weirdness

This commit is contained in:
Matias 2025-07-16 12:17:35 +02:00
parent 168c43269e
commit 92438a9bce
No known key found for this signature in database
GPG Key ID: ED35A6AC65A06B69

View File

@ -1,18 +1,21 @@
#include "globals.hpp"
#include <GLES3/gl32.h>
#include <algorithm>
#include <hyprgraphics/color/Color.hpp>
// 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 <hyprland/src/Compositor.hpp>
#include <hyprland/src/desktop/DesktopTypes.hpp>
#include <hyprland/src/desktop/Window.hpp>
#include <hyprland/src/helpers/Monitor.hpp>
#include <hyprland/src/helpers/time/Time.hpp>
#include <hyprland/src/managers/input/InputManager.hpp>
#include <hyprland/src/plugins/PluginAPI.hpp>
#include <hyprland/src/render/Framebuffer.hpp>
#include <hyprland/src/render/OpenGL.hpp>
#include <hyprland/src/render/Renderer.hpp>
#include <hyprland/src/render/pass/PassElement.hpp>
#include <hyprutils/math/Box.hpp>
#include <hyprutils/math/Region.hpp>
#include <hyprutils/math/Vector2D.hpp>
@ -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<float> 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<SParticle> m_particles;
std::vector<Vector2D> m_targetPositions;
std::optional<Vector2D> 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<std::chrono::microseconds>(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<PHLWINDOWREF, CFramebuffer> g_windowFramebuffers;
inline std::map<PHLWINDOWREF, Vector2D> g_windowPositions;
inline std::map<PHLWINDOWREF, CWobblyWindow> 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<float> 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<PHLWINDOW>(data);
void registerWindow(PHLWINDOW pWindow) {
g_windowPositions[pWindow] = pWindow->m_realPosition->value();
}
inline std::map<PHLWINDOWREF, CFramebuffer> g_windowFramebuffers;
static SP<HOOK_CALLBACK_FN> 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<CRenderWobblyWindowPassElement>(pOldFramebuffer, pWindow)
);
@ -243,14 +382,13 @@ void initGPUObjects() {
// std::vector<float> finalVerts;
const unsigned int vertsPerRow = g_SUBDIVS + 1;
std::vector<float> 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<PHLWINDOW>(data));
}
);
static const auto METHODS = HyprlandAPI::findFunctionsByName(PHANDLE, "renderWindow");