瀏覽代碼

it gets better

UwU 1 月之前
父節點
當前提交
54a87649c9
共有 1 個文件被更改,包括 23 次插入 和 13 次删除
  1. 23 13
      src/WallpaperEngine/Render/Objects/CImage.cpp

+ 23 - 13
src/WallpaperEngine/Render/Objects/CImage.cpp

@@ -701,18 +701,28 @@ CImage::ResolvedTransform CImage::resolveTransform (const Object& object) const
 	ResolvedTransform local = localTransform (*chain[i]);
 
 	// scene.json's "attachment" follows a named point on the direct parent's puppet rig (see
-	// PuppetAttachmentPoint), not the parent's own origin. A puppet's mesh-space coordinates get
-	// mapped into the parent's resolved origin/scale by CImage::updateScenePosition as
-	// screenX = parentOrigin + meshX * parentScale (and screenY = parentOrigin - meshY * parentScale,
-	// since updatePuppetPositionBuffer flips Y) - deriving from that formula and inverting it gives
-	// exactly the point on screen the attachment bone currently sits at, which becomes this object's
-	// anchor instead of the parent's own origin. The object's own "origin" is then a small local nudge
-	// around that anchor, scaled the same way a normal child's origin would be.
+	// PuppetAttachmentPoint), not the parent's own origin. This mirrors the real engine's attachment
+	// resolution (confirmed via disassembly of wallpaper64.exe's sub_140148A20, the function that
+	// actually builds an object's world matrix): parentWorldMatrix * boneLocalMatrix, composed with NO
+	// Y-axis sign flip anywhere in the chain - the real engine uses one consistent Y convention all the
+	// way from JSON through every level of parent/child composition, flipping (if at all) exactly once,
+	// at the very end in the camera projection.
 	//
-	// This mirrors the real engine's attachment resolution: a full matrix multiply of the parent's
-	// world transform with the bone's local one, so both the parent's and the bone's current rotation
-	// need to carry into the anchor. The bone's angle is negated alongside its Y-flipped position to
-	// stay consistent (mirroring an axis negates a rotation).
+	// resolveTransform's own "origin" space already works this same unflipped way for ordinary
+	// (non-attachment) children two lines below (`local.origin.y = anchorOrigin.y + offset.y`, no
+	// negation) - it's only the FINAL CImage-constructor/updateScenePosition step that ever flips Y, to
+	// go from this consistent origin-space into screen/pixel space. The bone's meshPosition, however,
+	// comes from getAttachmentPointMeshTransform() already in that same unflipped origin-space
+	// convention (see its own doc comment) - so it must be folded in raw, exactly like a normal child's
+	// local.origin is, not re-flipped a second time. A previous version of this code negated
+	// meshTransform->position.y (and, to stay "consistent" with that, meshTransform->angle) by analogy
+	// with updatePuppetPositionBuffer's Y-flip - but that flip belongs to a *different* pipeline (baking
+	// mesh-space directly to screen-space for the puppet's own vertices), not to this one, and mixing it
+	// into the otherwise-unflipped origin-space chain was very likely a real bug: large/rotated
+	// attachments (e.g. an eye attached to a puppet with a real bone rotation) landed far from the
+	// correct position, while small/near-zero-rotation cases happened to look close enough to right to
+	// go unnoticed. Not yet re-verified against a real capture - if this turns out wrong, the negation
+	// this replaced is in this file's git history.
 	glm::vec3 anchorOrigin = resolved.origin;
 	float anchorAngle = resolved.angle;
 	glm::vec2 anchorScale = { 1.0f, 1.0f };
@@ -722,12 +732,12 @@ CImage::ResolvedTransform CImage::resolveTransform (const Object& object) const
 		if (const auto meshTransform = parentImage->getAttachmentPointMeshTransform (*chain[i]->attachment);
 		    meshTransform.has_value ()) {
 		    const glm::vec2 meshOffset = rotateVec2 (
-			{ meshTransform->position.x * resolved.scale.x, -meshTransform->position.y * resolved.scale.y },
+			{ meshTransform->position.x * resolved.scale.x, meshTransform->position.y * resolved.scale.y },
 			resolved.angle
 		    );
 		    anchorOrigin.x = resolved.origin.x + meshOffset.x;
 		    anchorOrigin.y = resolved.origin.y + meshOffset.y;
-		    anchorAngle = resolved.angle - meshTransform->angle;
+		    anchorAngle = resolved.angle + meshTransform->angle;
 		    // the bone's own scale (possibly negative, i.e. a mirrored bone) carries into whatever
 		    // rides it, same as position/rotation
 		    anchorScale = meshTransform->scale;