diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml index ed435d9a..ff1d6a4d 100644 --- a/modules/drawers/Drawers.qml +++ b/modules/drawers/Drawers.qml @@ -153,7 +153,6 @@ Variants { PanelBg { id: dashBg - group: blobGroup panel: panels.dashboard bar: bar deformAmount: 0.1 @@ -162,7 +161,6 @@ Variants { PanelBg { id: launcherBg - group: blobGroup panel: panels.launcher bar: bar deformAmount: 0.1 @@ -171,7 +169,6 @@ Variants { PanelBg { id: sessionBg - group: blobGroup panel: panels.session bar: bar deformAmount: 0.25 @@ -180,7 +177,6 @@ Variants { PanelBg { id: sidebarBg - group: blobGroup panel: panels.sidebar bar: bar deformAmount: 0 @@ -189,7 +185,6 @@ Variants { PanelBg { id: osdBg - group: blobGroup panel: panels.osd bar: bar deformAmount: 0.3 @@ -198,7 +193,6 @@ Variants { PanelBg { id: notifsBg - group: blobGroup panel: panels.notifications bar: bar } @@ -206,7 +200,6 @@ Variants { PanelBg { id: utilsBg - group: blobGroup panel: panels.utilities bar: bar } @@ -214,7 +207,6 @@ Variants { PanelBg { id: popoutBg - group: blobGroup panel: panels.popouts bar: bar @@ -306,6 +298,7 @@ Variants { required property Item bar property real deformAmount: 0.15 + group: panel.width > 0 && panel.height > 0 ? blobGroup : null x: panel.x + bar.implicitWidth y: panel.y + Config.border.thickness implicitWidth: panel.width diff --git a/plugin/src/Caelestia/Blobs/blobinvertedrect.cpp b/plugin/src/Caelestia/Blobs/blobinvertedrect.cpp index 68024738..3e392b4a 100644 --- a/plugin/src/Caelestia/Blobs/blobinvertedrect.cpp +++ b/plugin/src/Caelestia/Blobs/blobinvertedrect.cpp @@ -1,9 +1,126 @@ #include "blobinvertedrect.hpp" #include "blobgroup.hpp" +#include "blobmaterial.hpp" + +#include +#include + +#include +#include BlobInvertedRect::BlobInvertedRect(QQuickItem* parent) : BlobShape(parent) {} +static void setFrameIndices(quint16* idx) { + // Top strip: 0-1-4, 1-5-4 + idx[0] = 0; idx[1] = 1; idx[2] = 4; + idx[3] = 1; idx[4] = 5; idx[5] = 4; + // Right strip: 1-2-5, 2-6-5 + idx[6] = 1; idx[7] = 2; idx[8] = 5; + idx[9] = 2; idx[10] = 6; idx[11] = 5; + // Bottom strip: 2-3-6, 3-7-6 + idx[12] = 2; idx[13] = 3; idx[14] = 6; + idx[15] = 3; idx[16] = 7; idx[17] = 6; + // Left strip: 3-0-7, 0-4-7 + idx[18] = 3; idx[19] = 0; idx[20] = 7; + idx[21] = 0; idx[22] = 4; idx[23] = 7; +} + +QSGNode* BlobInvertedRect::updatePaintNode( + QSGNode* oldNode, UpdatePaintNodeData*) { + if (!m_group) { + delete oldNode; + return nullptr; + } + + const float pad = static_cast(m_group->smoothing()); + + // Compute inner hole boundary in local coords + // Inset past the inner border edge by 2x smoothing to cover the blend zone + const float inset = pad * 2.0f; + const float holeLeft = static_cast(m_borderLeft) + inset; + const float holeTop = static_cast(m_borderTop) + inset; + const float holeRight = static_cast(width() - m_borderRight) - inset; + const float holeBot = static_cast(height() - m_borderBottom) - inset; + + // If the hole is too small or invalid, fall back to full quad + if (holeLeft >= holeRight || holeTop >= holeBot) + return BlobShape::updatePaintNode(oldNode, nullptr); + + auto* node = static_cast(oldNode); + + const bool needsRebuild = !node || node->geometry()->vertexCount() != 8; + + if (needsRebuild) { + delete oldNode; + node = new QSGGeometryNode; + + auto* geometry = new QSGGeometry( + QSGGeometry::defaultAttributes_TexturedPoint2D(), 8, 24, + QSGGeometry::UnsignedShortType); + geometry->setDrawingMode(QSGGeometry::DrawTriangles); + node->setGeometry(geometry); + node->setFlag(QSGNode::OwnsGeometry); + + setFrameIndices(geometry->indexDataAsUShort()); + + auto* material = new BlobMaterial; + material->setFlag(QSGMaterial::Blending); + node->setMaterial(material); + node->setFlag(QSGNode::OwnsMaterial); + } + + // Outer bounds (local coords) + const float x0 = static_cast(m_localPaddedRect.x()); + const float y0 = static_cast(m_localPaddedRect.y()); + const float x1 = x0 + static_cast(m_localPaddedRect.width()); + const float y1 = y0 + static_cast(m_localPaddedRect.height()); + const float w = x1 - x0; + const float h = y1 - y0; + + // Update vertex positions and texture coordinates + auto* v = node->geometry()->vertexDataAsTexturedPoint2D(); + + // Outer corners + v[0].set(x0, y0, 0.0f, 0.0f); + v[1].set(x1, y0, 1.0f, 0.0f); + v[2].set(x1, y1, 1.0f, 1.0f); + v[3].set(x0, y1, 0.0f, 1.0f); + // Inner corners (hole) + v[4].set(holeLeft, holeTop, (holeLeft - x0) / w, (holeTop - y0) / h); + v[5].set(holeRight, holeTop, (holeRight - x0) / w, (holeTop - y0) / h); + v[6].set(holeRight, holeBot, (holeRight - x0) / w, (holeBot - y0) / h); + v[7].set(holeLeft, holeBot, (holeLeft - x0) / w, (holeBot - y0) / h); + + node->markDirty(QSGNode::DirtyGeometry); + + // Update material uniforms + auto* material = static_cast(node->material()); + material->m_paddedX = m_cachedPaddedX; + material->m_paddedY = m_cachedPaddedY; + material->m_paddedW = m_cachedPaddedW; + material->m_paddedH = m_cachedPaddedH; + material->m_smoothFactor = pad; + material->m_myIndex = m_cachedMyIndex; + material->m_color = m_group->color(); + material->m_hasInverted = m_cachedHasInverted ? 1 : 0; + material->m_invertedRadius = m_cachedInvertedRadius; + memcpy(material->m_invertedOuter, m_cachedInvertedOuter, + sizeof(m_cachedInvertedOuter)); + memcpy(material->m_invertedInner, m_cachedInvertedInner, + sizeof(m_cachedInvertedInner)); + + const int count = + static_cast(qMin(m_cachedRects.size(), qsizetype(16))); + material->m_rectCount = count; + for (int i = 0; i < count; ++i) + material->m_rects[i] = m_cachedRects[i]; + + node->markDirty(QSGNode::DirtyMaterial); + + return node; +} + BlobInvertedRect::~BlobInvertedRect() { if (m_group) m_group->clearInvertedRect(this); diff --git a/plugin/src/Caelestia/Blobs/blobinvertedrect.hpp b/plugin/src/Caelestia/Blobs/blobinvertedrect.hpp index 958e2a8d..207244de 100644 --- a/plugin/src/Caelestia/Blobs/blobinvertedrect.hpp +++ b/plugin/src/Caelestia/Blobs/blobinvertedrect.hpp @@ -45,6 +45,8 @@ signals: protected: bool isInvertedRect() const override { return true; } + QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override; + void registerWithGroup() override; void unregisterFromGroup() override; diff --git a/plugin/src/Caelestia/Blobs/blobshape.cpp b/plugin/src/Caelestia/Blobs/blobshape.cpp index 73b5cfe6..b99bd8e1 100644 --- a/plugin/src/Caelestia/Blobs/blobshape.cpp +++ b/plugin/src/Caelestia/Blobs/blobshape.cpp @@ -102,10 +102,6 @@ void BlobShape::updatePolish() { // Ensure all shapes have up-to-date physics (only once per frame) m_group->ensurePhysicsUpdated(); - // When inverted rect renders everything, skip spatial query for others - if (!isInvertedRect() && m_group->invertedRect()) - return; - const QPointF scenePos = mapToScene(QPointF(0, 0)); const float pad = static_cast(m_group->smoothing()); @@ -142,6 +138,10 @@ void BlobShape::updatePolish() { if (other->isInvertedRect()) continue; + // Skip zero-size rects + if (other->width() <= 0 || other->height() <= 0) + continue; + const QPointF otherScene = other->mapToScene(QPointF(0, 0)); bool include = false; @@ -299,12 +299,6 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { return nullptr; } - // When an inverted rect exists, it renders everything in a single pass - if (!isInvertedRect() && m_group->invertedRect()) { - delete oldNode; - return nullptr; - } - auto* node = static_cast(oldNode); if (!node) { node = new QSGGeometryNode; diff --git a/plugin/src/Caelestia/Blobs/shaders/blob.frag b/plugin/src/Caelestia/Blobs/shaders/blob.frag index f4baa731..66912609 100644 --- a/plugin/src/Caelestia/Blobs/shaders/blob.frag +++ b/plugin/src/Caelestia/Blobs/shaders/blob.frag @@ -85,6 +85,11 @@ void main() { // Offset center for asymmetric deformation vec2 center = rect.xy + props.yz; + // AABB early-out: skip rects far from this pixel + vec2 extent = sh.xy + vec2(smoothFactor * 1.5); + if (abs(pixel.x - center.x) > extent.x || abs(pixel.y - center.y) > extent.y) + continue; + // Apply pre-computed inverse deformation to the evaluation point mat2 invDeform = mat2(invDm.xy, invDm.zw); vec2 transformedPixel = center + invDeform * (pixel - center); @@ -157,6 +162,12 @@ void main() { vec2 jSh = rectData[j * 5 + 3].xy; vec2 jC = jR.xy + jP.yz; + // Skip non-adjacent rects + float sinkRange = smoothFactor * 1.5; + if (abs(center.x - jC.x) > iSh.x + jSh.x + sinkRange || + abs(center.y - jC.y) > iSh.y + jSh.y + sinkRange) + continue; + // Penetration of j past i's edges (positive = past) float pT = (jC.y + jSh.y) - (center.y - iSh.y) - sinkOff; float pB = (center.y + iSh.y) - (jC.y - jSh.y) - sinkOff; @@ -266,9 +277,10 @@ void main() { } } - // myIndex == -1: inverted rect renders everything (frame + blobs) - // myIndex >= 0: individual rect renders only its owned pixels - if (myIndex >= 0 && owner != myIndex) + // Each renderer only outputs pixels it owns + // myIndex == -1: inverted rect renders border-owned pixels + // myIndex >= 0: individual rect renders its owned pixels + if (owner != myIndex) discard; float fw = fwidth(mergedSdf);