Actually fix background desync, blobs didn't follow final settle after deform shader
This commit is contained in:
@@ -14,15 +14,11 @@ BlobRect::~BlobRect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::updatePolish() {
|
void BlobRect::updatePolish() {
|
||||||
BlobShape::updatePolish();
|
|
||||||
|
|
||||||
if (m_physicsActive) {
|
if (m_physicsActive) {
|
||||||
// Check if deformation is visually imperceptible
|
|
||||||
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) + std::abs(m_dm11 - 1.0f);
|
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) + std::abs(m_dm11 - 1.0f);
|
||||||
float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
|
float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
|
||||||
|
|
||||||
if (totalDelta < 0.004f && totalVel < 0.05f) {
|
if (totalDelta < 0.004f && totalVel < 0.05f) {
|
||||||
// Snap to rest, no visible deformation
|
|
||||||
m_dm00 = 1.0f;
|
m_dm00 = 1.0f;
|
||||||
m_dm01 = 0.0f;
|
m_dm01 = 0.0f;
|
||||||
m_dm11 = 1.0f;
|
m_dm11 = 1.0f;
|
||||||
@@ -31,6 +27,16 @@ void BlobRect::updatePolish() {
|
|||||||
emit rawDeformMatrixChanged();
|
emit rawDeformMatrixChanged();
|
||||||
updateCenteredDeformMatrix();
|
updateCenteredDeformMatrix();
|
||||||
m_physicsActive = false;
|
m_physicsActive = false;
|
||||||
|
|
||||||
|
if (m_group) {
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
|
if (m_group)
|
||||||
|
m_group->markDirty();
|
||||||
|
},
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
@@ -41,6 +47,8 @@ void BlobRect::updatePolish() {
|
|||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlobShape::updatePolish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::updatePhysics() {
|
void BlobRect::updatePhysics() {
|
||||||
@@ -56,7 +64,6 @@ void BlobRect::updatePhysics() {
|
|||||||
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
|
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
|
||||||
if (dt > 0.1f || dt < 0.001f) {
|
if (dt > 0.1f || dt < 0.001f) {
|
||||||
m_prevScenePos = scenePos;
|
m_prevScenePos = scenePos;
|
||||||
// Still check atRest on skipped frames to avoid getting stuck
|
|
||||||
if (m_physicsActive)
|
if (m_physicsActive)
|
||||||
checkAtRest(0.0f);
|
checkAtRest(0.0f);
|
||||||
return;
|
return;
|
||||||
@@ -74,8 +81,6 @@ void BlobRect::updatePhysics() {
|
|||||||
m_physicsActive = true;
|
m_physicsActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute target deformation matrix from velocity
|
|
||||||
// R(θ) * diag(stretch, compress) * R(θ)^T
|
|
||||||
const float kStretchFactor = static_cast<float>(m_deformScale);
|
const float kStretchFactor = static_cast<float>(m_deformScale);
|
||||||
constexpr float kMaxStretch = 0.35f;
|
constexpr float kMaxStretch = 0.35f;
|
||||||
|
|
||||||
@@ -98,7 +103,6 @@ void BlobRect::updatePhysics() {
|
|||||||
target11 = targetStretch * sin2 + targetCompress * cos2;
|
target11 = targetStretch * sin2 + targetCompress * cos2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Underdamped spring on each matrix component
|
|
||||||
const float kStiffness = static_cast<float>(m_stiffness);
|
const float kStiffness = static_cast<float>(m_stiffness);
|
||||||
const float kDamping = static_cast<float>(m_damping);
|
const float kDamping = static_cast<float>(m_damping);
|
||||||
|
|
||||||
@@ -238,9 +242,19 @@ void BlobRect::checkAtRest(float speed) {
|
|||||||
m_dmVel00 = 0.0f;
|
m_dmVel00 = 0.0f;
|
||||||
m_dmVel01 = 0.0f;
|
m_dmVel01 = 0.0f;
|
||||||
m_dmVel11 = 0.0f;
|
m_dmVel11 = 0.0f;
|
||||||
m_deformMatrix = QMatrix4x4(); // identity
|
m_deformMatrix = QMatrix4x4();
|
||||||
emit rawDeformMatrixChanged();
|
emit rawDeformMatrixChanged();
|
||||||
updateCenteredDeformMatrix();
|
updateCenteredDeformMatrix();
|
||||||
m_physicsActive = false;
|
m_physicsActive = false;
|
||||||
|
|
||||||
|
if (m_group) {
|
||||||
|
QMetaObject::invokeMethod(
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
|
if (m_group)
|
||||||
|
m_group->markDirty();
|
||||||
|
},
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
static float deformPadding(const QMatrix4x4& dm, float hw, float hh) {
|
static float deformPadding(const QMatrix4x4& dm, float hw, float hh) {
|
||||||
// Bounding box of the deformed shape: |M * corners|
|
|
||||||
const float dm00 = dm(0, 0), dm01 = dm(0, 1);
|
const float dm00 = dm(0, 0), dm01 = dm(0, 1);
|
||||||
const float dm10 = dm(1, 0), dm11 = dm(1, 1);
|
const float dm10 = dm(1, 0), dm11 = dm(1, 1);
|
||||||
const float boundX = std::abs(dm00) * hw + std::abs(dm01) * hh;
|
const float boundX = std::abs(dm00) * hw + std::abs(dm01) * hh;
|
||||||
@@ -69,16 +68,17 @@ void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeome
|
|||||||
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||||
updateCenteredDeformMatrix();
|
updateCenteredDeformMatrix();
|
||||||
if (m_group) {
|
if (m_group) {
|
||||||
// Accumulate sub-pixel drift so slow movements don't desync the shader
|
|
||||||
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
|
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
|
||||||
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
|
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
|
||||||
// Accumulate size delta across multiple frames so incremental size
|
|
||||||
// changes that are each below the threshold still trigger a dirty
|
|
||||||
// mark once their accumulated delta exceeds it.
|
|
||||||
m_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width());
|
m_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width());
|
||||||
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height());
|
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height());
|
||||||
if (std::abs(m_pendingDx) > 0.5f || std::abs(m_pendingDy) > 0.5f ||
|
|
||||||
std::abs(m_pendingDw) > 0.5f || std::abs(m_pendingDh) > 0.5f) {
|
const float deformMag = std::abs(m_deformMatrix(0, 0) - 1.0f) + std::abs(m_deformMatrix(0, 1)) +
|
||||||
|
std::abs(m_deformMatrix(1, 0)) + std::abs(m_deformMatrix(1, 1) - 1.0f);
|
||||||
|
const float syncThreshold = deformMag > 0.001f ? 0.05f : 0.5f;
|
||||||
|
|
||||||
|
if (std::abs(m_pendingDx) > syncThreshold || std::abs(m_pendingDy) > syncThreshold ||
|
||||||
|
std::abs(m_pendingDw) > syncThreshold || std::abs(m_pendingDh) > syncThreshold) {
|
||||||
m_pendingDx = 0;
|
m_pendingDx = 0;
|
||||||
m_pendingDy = 0;
|
m_pendingDy = 0;
|
||||||
m_pendingDw = 0;
|
m_pendingDw = 0;
|
||||||
@@ -124,7 +124,6 @@ void BlobShape::updatePolish() {
|
|||||||
if (!m_group)
|
if (!m_group)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Ensure all shapes have up-to-date physics (only once per frame)
|
|
||||||
m_group->ensurePhysicsUpdated();
|
m_group->ensurePhysicsUpdated();
|
||||||
|
|
||||||
const QPointF scenePos = mapToScene(QPointF(0, 0));
|
const QPointF scenePos = mapToScene(QPointF(0, 0));
|
||||||
@@ -149,13 +148,11 @@ void BlobShape::updatePolish() {
|
|||||||
width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad));
|
width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter nearby normal rects
|
|
||||||
m_cachedRects.clear();
|
m_cachedRects.clear();
|
||||||
m_cachedMyIndex = -2;
|
m_cachedMyIndex = -2;
|
||||||
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
||||||
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
|
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
|
||||||
|
|
||||||
// Track shape pointers parallel to m_cachedRects for pairwise exclusion lookups
|
|
||||||
QVector<BlobShape*> rectShapes;
|
QVector<BlobShape*> rectShapes;
|
||||||
rectShapes.reserve(m_group->shapes().size());
|
rectShapes.reserve(m_group->shapes().size());
|
||||||
|
|
||||||
@@ -163,7 +160,6 @@ void BlobShape::updatePolish() {
|
|||||||
if (other->isInvertedRect())
|
if (other->isInvertedRect())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Skip zero-size rects
|
|
||||||
if (other->width() <= 0 || other->height() <= 0)
|
if (other->width() <= 0 || other->height() <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -202,7 +198,6 @@ void BlobShape::updatePolish() {
|
|||||||
r.offsetX = dm(0, 3);
|
r.offsetX = dm(0, 3);
|
||||||
r.offsetY = dm(1, 3);
|
r.offsetY = dm(1, 3);
|
||||||
|
|
||||||
// Pre-compute inverse deformation matrix
|
|
||||||
const float det = a * d - c * b;
|
const float det = a * d - c * b;
|
||||||
const float invDet = std::abs(det) > 1e-6f ? 1.0f / det : 1.0f;
|
const float invDet = std::abs(det) > 1e-6f ? 1.0f / det : 1.0f;
|
||||||
r.invDeform[0] = d * invDet;
|
r.invDeform[0] = d * invDet;
|
||||||
@@ -210,12 +205,10 @@ void BlobShape::updatePolish() {
|
|||||||
r.invDeform[2] = -c * invDet;
|
r.invDeform[2] = -c * invDet;
|
||||||
r.invDeform[3] = a * invDet;
|
r.invDeform[3] = a * invDet;
|
||||||
|
|
||||||
// Pre-compute minimum eigenvalue (avoids per-pixel sqrt)
|
|
||||||
const float halfTr = 0.5f * (a + d);
|
const float halfTr = 0.5f * (a + d);
|
||||||
const float halfDiff = 0.5f * (a - d);
|
const float halfDiff = 0.5f * (a - d);
|
||||||
r.minEig = halfTr - std::sqrt(halfDiff * halfDiff + c * c);
|
r.minEig = halfTr - std::sqrt(halfDiff * halfDiff + c * c);
|
||||||
|
|
||||||
// Pre-compute screen-space AABB half-extents
|
|
||||||
r.screenHalfX = std::abs(a) * r.hw + std::abs(c) * r.hh;
|
r.screenHalfX = std::abs(a) * r.hw + std::abs(c) * r.hh;
|
||||||
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
|
r.screenHalfY = std::abs(b) * r.hw + std::abs(d) * r.hh;
|
||||||
|
|
||||||
@@ -227,8 +220,6 @@ void BlobShape::updatePolish() {
|
|||||||
if (isInvertedRect())
|
if (isInvertedRect())
|
||||||
m_cachedMyIndex = -1;
|
m_cachedMyIndex = -1;
|
||||||
|
|
||||||
// Compute pairwise exclude masks. Bit j in entry i is set iff rect i excludes rect j
|
|
||||||
// or rect j excludes rect i. The shader uses this to avoid smin between excluded pairs.
|
|
||||||
const auto cachedCount = m_cachedRects.size();
|
const auto cachedCount = m_cachedRects.size();
|
||||||
for (qsizetype i = 0; i < cachedCount; ++i) {
|
for (qsizetype i = 0; i < cachedCount; ++i) {
|
||||||
int mask = 0;
|
int mask = 0;
|
||||||
@@ -243,7 +234,6 @@ void BlobShape::updatePolish() {
|
|||||||
m_cachedRects[i].excludeMask = mask;
|
m_cachedRects[i].excludeMask = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache inverted rect data
|
|
||||||
m_cachedHasInverted = false;
|
m_cachedHasInverted = false;
|
||||||
m_cachedInvertedRadius = 0;
|
m_cachedInvertedRadius = 0;
|
||||||
memset(m_cachedInvertedOuter, 0, sizeof(m_cachedInvertedOuter));
|
memset(m_cachedInvertedOuter, 0, sizeof(m_cachedInvertedOuter));
|
||||||
@@ -262,7 +252,6 @@ void BlobShape::updatePolish() {
|
|||||||
const float innerHW = outerHW - static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
|
const float innerHW = outerHW - static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
|
||||||
const float innerHH = outerHH - static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
|
const float innerHH = outerHH - static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
|
||||||
|
|
||||||
// Check if this rect is near the border (within 2x smoothing of inner edge)
|
|
||||||
bool nearBorder = isInvertedRect();
|
bool nearBorder = isInvertedRect();
|
||||||
if (!nearBorder) {
|
if (!nearBorder) {
|
||||||
const float margin = pad * 2.0f;
|
const float margin = pad * 2.0f;
|
||||||
@@ -270,7 +259,6 @@ void BlobShape::updatePolish() {
|
|||||||
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
|
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
|
||||||
const float myHW = m_cachedPaddedW * 0.5f;
|
const float myHW = m_cachedPaddedW * 0.5f;
|
||||||
const float myHH = m_cachedPaddedH * 0.5f;
|
const float myHH = m_cachedPaddedH * 0.5f;
|
||||||
// Near border if any edge of padded rect is within margin of inner edge
|
|
||||||
nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) ||
|
nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) ||
|
||||||
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - margin);
|
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - margin);
|
||||||
}
|
}
|
||||||
@@ -291,7 +279,6 @@ void BlobShape::updatePolish() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-compute effective per-corner radii (moves O(N²) work from GPU to CPU)
|
|
||||||
const float smoothFactor = pad;
|
const float smoothFactor = pad;
|
||||||
constexpr float minR = 2.0f;
|
constexpr float minR = 2.0f;
|
||||||
const auto rectCount = m_cachedRects.size();
|
const auto rectCount = m_cachedRects.size();
|
||||||
@@ -328,7 +315,6 @@ void BlobShape::updatePolish() {
|
|||||||
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
|
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Combine base radii with fill factors into effective per-corner radii
|
|
||||||
ri.radius[0] = std::max(ri.radius[0] * fTr, minR);
|
ri.radius[0] = std::max(ri.radius[0] * fTr, minR);
|
||||||
ri.radius[1] = std::max(ri.radius[1] * fBr, minR);
|
ri.radius[1] = std::max(ri.radius[1] * fBr, minR);
|
||||||
ri.radius[2] = std::max(ri.radius[2] * fBl, minR);
|
ri.radius[2] = std::max(ri.radius[2] * fBl, minR);
|
||||||
@@ -357,7 +343,6 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
|||||||
node->setFlag(QSGNode::OwnsMaterial);
|
node->setFlag(QSGNode::OwnsMaterial);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update geometry
|
|
||||||
auto* geometry = node->geometry();
|
auto* geometry = node->geometry();
|
||||||
auto* v = geometry->vertexDataAsTexturedPoint2D();
|
auto* v = geometry->vertexDataAsTexturedPoint2D();
|
||||||
|
|
||||||
@@ -373,7 +358,6 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
|||||||
|
|
||||||
node->markDirty(QSGNode::DirtyGeometry);
|
node->markDirty(QSGNode::DirtyGeometry);
|
||||||
|
|
||||||
// Update material
|
|
||||||
auto* material = static_cast<BlobMaterial*>(node->material());
|
auto* material = static_cast<BlobMaterial*>(node->material());
|
||||||
material->m_paddedX = m_cachedPaddedX;
|
material->m_paddedX = m_cachedPaddedX;
|
||||||
material->m_paddedY = m_cachedPaddedY;
|
material->m_paddedY = m_cachedPaddedY;
|
||||||
|
|||||||
Reference in New Issue
Block a user