3
0
mirror of https://github.com/hyprwm/Hyprland.git synced 2026-08-18 11:02:10 +00:00

renderer/wobble: fix wobble on transformed outputs (#15694)

* renderer/wobble: fix wobble on transformed outputs

* sc
This commit is contained in:
Vaxry
2026-08-01 17:01:50 +01:00
committed by GitHub
parent 3906d66afc
commit 2f9169f05f
5 changed files with 59 additions and 22 deletions

View File

@ -1,5 +1,7 @@
#include "DeformableMesh.hpp"
#include "memory/Memory.hpp"
#include <hyprutils/animation/Spring.hpp>
#include <algorithm>
@ -13,7 +15,7 @@ CDeformableMesh::CDeformableMesh(size_t verticesPerEdge) {
}
void CDeformableMesh::setSize(size_t verticesPerEdge) {
verticesPerEdge = std::clamp(verticesPerEdge, static_cast<size_t>(2), static_cast<size_t>(32));
verticesPerEdge = std::clamp(verticesPerEdge, sc<size_t>(2), sc<size_t>(32));
if (m_verticesPerEdge == verticesPerEdge && m_points.size() == verticesPerEdge * verticesPerEdge)
return;
@ -59,9 +61,9 @@ void CDeformableMesh::onPositionUpdate(const CBox& previous, const CBox& current
const double GRABMAXDISTANCE = grabPoint ? std::max(std::hypot(std::max(grabPoint->x, 1.0 - grabPoint->x), std::max(grabPoint->y, 1.0 - grabPoint->y)), 0.001) : 1.0;
for (size_t y = 0; y < m_verticesPerEdge; ++y) {
const double V = m_verticesPerEdge == 1 ? 0.0 : static_cast<double>(y) / static_cast<double>(m_verticesPerEdge - 1);
const double V = m_verticesPerEdge == 1 ? 0.0 : sc<double>(y) / sc<double>(m_verticesPerEdge - 1);
for (size_t x = 0; x < m_verticesPerEdge; ++x) {
const double U = m_verticesPerEdge == 1 ? 0.0 : static_cast<double>(x) / static_cast<double>(m_verticesPerEdge - 1);
const double U = m_verticesPerEdge == 1 ? 0.0 : sc<double>(x) / sc<double>(m_verticesPerEdge - 1);
const double MOVEWEIGHTX = 0.45 + 0.55 * std::sin(V * std::numbers::pi);
const double MOVEWEIGHTY = 0.45 + 0.55 * std::sin(U * std::numbers::pi);
@ -89,10 +91,10 @@ void CDeformableMesh::onPositionUpdate(const CBox& previous, const CBox& current
void CDeformableMesh::advance(const SSpringCurve& spring, std::chrono::duration<float> elapsed) {
for (auto& p : m_points) {
float valueX = 1.F + static_cast<float>(p.displacement.x);
float valueY = 1.F + static_cast<float>(p.displacement.y);
float velX = static_cast<float>(p.velocity.x);
float velY = static_cast<float>(p.velocity.y);
float valueX = 1.F + sc<float>(p.displacement.x);
float valueY = 1.F + sc<float>(p.displacement.y);
float velX = sc<float>(p.velocity.x);
float velY = sc<float>(p.velocity.y);
advanceSpring(valueX, velX, spring, elapsed);
advanceSpring(valueY, velY, spring, elapsed);
@ -132,7 +134,8 @@ CBox CDeformableMesh::transformedExtents(const CBox& box) const {
return {minX, minY, maxX - minX, maxY - minY};
}
std::vector<SMeshRenderVertex> CDeformableMesh::verticesForBox(const CBox& box, const CBox& outputBox, const Vector2D& textureSize, double displacementScale) const {
std::vector<SMeshRenderVertex> CDeformableMesh::verticesForBox(const CBox& box, const CBox& outputBox, const Vector2D& textureSize, double displacementScale,
eTransform textureTransform) const {
std::vector<SMeshRenderVertex> vertices;
if (m_verticesPerEdge < 2 || outputBox.w <= 0.F || outputBox.h <= 0.F || textureSize.x <= 0.F || textureSize.y <= 0.F)
return vertices;
@ -140,13 +143,14 @@ std::vector<SMeshRenderVertex> CDeformableMesh::verticesForBox(const CBox& box,
vertices.reserve((m_verticesPerEdge - 1) * (m_verticesPerEdge - 1) * 6);
const auto makeVertex = [&](size_t x, size_t y) -> SMeshRenderVertex {
const Vector2D REST = restPoint(box, x, y);
const Vector2D POS = REST + point(x, y).displacement * displacementScale;
const Vector2D REST = restPoint(box, x, y);
const Vector2D POS = REST + point(x, y).displacement * displacementScale;
const Vector2D TEXTUREPOS = REST.transform(textureTransform, textureSize);
return {
.x = static_cast<float>((POS.x - outputBox.x) / outputBox.w),
.y = static_cast<float>((POS.y - outputBox.y) / outputBox.h),
.u = static_cast<float>(REST.x / textureSize.x),
.v = static_cast<float>(REST.y / textureSize.y),
.x = sc<float>((POS.x - outputBox.x) / outputBox.w),
.y = sc<float>((POS.y - outputBox.y) / outputBox.h),
.u = sc<float>(TEXTUREPOS.x / textureSize.x),
.v = sc<float>(TEXTUREPOS.y / textureSize.y),
};
};
@ -173,8 +177,8 @@ const CDeformableMesh::SPoint& CDeformableMesh::point(size_t x, size_t y) const
}
Vector2D CDeformableMesh::restPoint(const CBox& box, size_t x, size_t y) const {
const double U = m_verticesPerEdge == 1 ? 0.0 : static_cast<double>(x) / static_cast<double>(m_verticesPerEdge - 1);
const double V = m_verticesPerEdge == 1 ? 0.0 : static_cast<double>(y) / static_cast<double>(m_verticesPerEdge - 1);
const double U = m_verticesPerEdge == 1 ? 0.0 : sc<double>(x) / sc<double>(m_verticesPerEdge - 1);
const double V = m_verticesPerEdge == 1 ? 0.0 : sc<double>(y) / sc<double>(m_verticesPerEdge - 1);
return {box.x + box.w * U, box.y + box.h * V};
}

View File

@ -28,7 +28,8 @@ class CDeformableMesh {
bool stable(float positionEpsilon, float velocityEpsilon) const;
CBox transformedExtents(const CBox& box) const;
std::vector<SMeshRenderVertex> verticesForBox(const CBox& box, const CBox& outputBox, const Vector2D& textureSize, double displacementScale = 1.0) const;
std::vector<SMeshRenderVertex> verticesForBox(const CBox& box, const CBox& outputBox, const Vector2D& textureSize, double displacementScale = 1.0,
eTransform textureTransform = HYPRUTILS_TRANSFORM_NORMAL) const;
private:
struct SPoint {

View File

@ -668,10 +668,11 @@ void IElementRenderer::drawTransformedWindow(WP<CTransformedWindowPassElement> e
outputBox = {0, 0, pMonitor->m_transformedSize.x, pMonitor->m_transformedSize.y};
CTexPassElement::SRenderData data;
data.tex = last->getTexture();
data.box = outputBox;
data.a = 1.F;
data.motionBlur = motionBlur;
data.tex = last->getTexture();
data.box = outputBox;
data.a = 1.F;
data.motionBlur = motionBlur;
data.flipEndFrame = !motionBlur.enabled;
const CRegion drawDamage = damage.copy().intersect(outputBox);

View File

@ -87,7 +87,7 @@ SP<Render::IFramebuffer> CWobbleTransformer::transform(SP<Render::IFramebuffer>
if (sourceBox.empty() || outputBox.empty())
return in;
const auto VERTICES = m_mesh.verticesForBox(sourceBox, sourceOutputBox, in->getTexture()->m_size, SCALE);
const auto VERTICES = m_mesh.verticesForBox(sourceBox, sourceOutputBox, in->getTexture()->m_size, SCALE, Math::wlTransformToHyprutils(context.monitor->m_transform));
if (VERTICES.empty())
return in;

View File

@ -3,6 +3,7 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <cmath>
TEST(Helpers, deformableMeshSetSizeClamps) {
@ -89,3 +90,33 @@ TEST(Helpers, deformableMeshVerticesForBoxBuildsTriangles) {
EXPECT_FLOAT_EQ(VERTICES.back().u, 1.F);
EXPECT_FLOAT_EQ(VERTICES.back().v, 1.F);
}
TEST(Helpers, deformableMeshVerticesForBoxTransformsUVs) {
struct STransformCase {
eTransform transform = HYPRUTILS_TRANSFORM_NORMAL;
Vector2D topLeft;
Vector2D bottomRight;
};
const CBox BOX = {100, 200, 300, 400};
constexpr Vector2D TEXTURESIZE = {1920, 1080};
const auto CASES = std::array{
STransformCase{HYPRUTILS_TRANSFORM_NORMAL, {100, 200}, {400, 600}}, STransformCase{HYPRUTILS_TRANSFORM_90, {200, 980}, {600, 680}},
STransformCase{HYPRUTILS_TRANSFORM_180, {1820, 880}, {1520, 480}}, STransformCase{HYPRUTILS_TRANSFORM_270, {1720, 100}, {1320, 400}},
STransformCase{HYPRUTILS_TRANSFORM_FLIPPED, {1820, 200}, {1520, 600}}, STransformCase{HYPRUTILS_TRANSFORM_FLIPPED_90, {200, 100}, {600, 400}},
STransformCase{HYPRUTILS_TRANSFORM_FLIPPED_180, {100, 880}, {400, 480}}, STransformCase{HYPRUTILS_TRANSFORM_FLIPPED_270, {1720, 980}, {1320, 680}},
};
CDeformableMesh mesh(2);
for (const auto& test : CASES) {
SCOPED_TRACE(static_cast<int>(test.transform));
const auto VERTICES = mesh.verticesForBox(BOX, BOX, TEXTURESIZE, 1.0, test.transform);
ASSERT_EQ(VERTICES.size(), 6u);
EXPECT_FLOAT_EQ(VERTICES.front().u, static_cast<float>(test.topLeft.x / TEXTURESIZE.x));
EXPECT_FLOAT_EQ(VERTICES.front().v, static_cast<float>(test.topLeft.y / TEXTURESIZE.y));
EXPECT_FLOAT_EQ(VERTICES.back().u, static_cast<float>(test.bottomRight.x / TEXTURESIZE.x));
EXPECT_FLOAT_EQ(VERTICES.back().v, static_cast<float>(test.bottomRight.y / TEXTURESIZE.y));
}
}