[Scummvm-git-logs] scummvm master -> d0804ce5068af04894becf87bd091d91930ba4c1

aquadran noreply at scummvm.org
Sun Aug 9 04:20:17 UTC 2026


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
d0804ce506 GRIM: Correct the calculation for attached joints.


Commit: d0804ce5068af04894becf87bd091d91930ba4c1
    https://github.com/scummvm/scummvm/commit/d0804ce5068af04894becf87bd091d91930ba4c1
Author: Oliver Kucharzewski (oliver at olidev.com.au)
Date: 2026-08-09T06:20:12+02:00

Commit Message:
GRIM: Correct the calculation for attached joints.

Fix an issue where attached actors could receive an incorrect rotation when being attached to a joint, causing characters such as Guybrush to flip unexpectedly.

Joint final rotations are already calculated during skeleton animation updates in Skeleton::commitAnim(). The joint's _finalQuat contains the accumulated rotation from its parent hierarchy, so applying an additional transformation was incorrectly modifying an already calculated rotation.

Changed paths:
    engines/grim/actor.cpp
    engines/grim/actor.h


diff --git a/engines/grim/actor.cpp b/engines/grim/actor.cpp
index e235540b395..e42493f7601 100644
--- a/engines/grim/actor.cpp
+++ b/engines/grim/actor.cpp
@@ -2459,6 +2459,16 @@ void Actor::activateShadow(bool active, SetShadow *setShadow) {
 	}
 }
 
+Joint *Actor::getAttachedJoint(Actor *parent) const {
+	EMICostume *cost = static_cast<EMICostume *>(parent->getCurrentCostume());
+
+	if (!cost || !cost->_emiSkel || !cost->_emiSkel->_obj)
+		return nullptr;
+
+	assert(cost->_emiSkel->_obj->hasJoint(_attachedJoint));
+	return cost->_emiSkel->_obj->getJointNamed(_attachedJoint);
+}
+
 void Actor::attachToActor(Actor *parent, const char *joint) {
 	assert(parent != nullptr);
 	// No need to attach if we're already attached to this parent
@@ -2468,39 +2478,43 @@ void Actor::attachToActor(Actor *parent, const char *joint) {
 	if (_attachedActor != 0)
 		detach();
 
-	// Find the new rotation relative to the parent actor's rotation
-	// Note: Any joint rotation is a part of the parent actor's rotation Quat
-	Math::Quaternion newRot = getRotationQuat().inverse() * parent->getRotationQuat();
-
 	// Find the new position coordinates
 	Math::Matrix4 parentMatrix = parent->getFinalMatrix();
 
-	// If the parent has a skeleton, check if it has the requested joint
-	// Some models (pile o' boulders) don't have a skeleton
+	// If the parent has a skeleton and the requested joint exists, use the joint transform.
+	// Some models (pile o' boulders) don't have a skeleton.
 	Common::String jointStr = joint ? joint : "";
-	EMICostume *cost = static_cast<EMICostume *>(parent->getCurrentCostume());
-	if (cost && cost->_emiSkel && cost->_emiSkel->_obj) {
-		assert(cost->_emiSkel->_obj->hasJoint(jointStr));
+	const Joint *attachedJoint = getAttachedJoint(parent);
+
+	Math::Quaternion newRot;
 
-		// Add the rotation from the attached actor's joint
-		Joint *j = cost->_emiSkel->_obj->getJointNamed(_attachedJoint);
-		newRot = newRot.inverse() * j->_finalQuat;
+	// A joint attachment already provides the final joint orientation calculated by
+	// Skeleton::commitAnim(). Use it directly instead of applying an additional
+	// quaternion adjustment, which can introduce an incorrect rotation.
+	if (attachedJoint) {
+		// Use the joint's final calculated orientation as the attached object's starting rotation.
+		newRot = attachedJoint->_finalQuat;
 
-		// Get the final position coordinates
-		_pos = _pos - j->_finalMatrix.getPosition();
-		j->_finalMatrix.transpose();
-		j->_finalMatrix.transform(&_pos, true);
+		// Convert position into the joint's local space.
+		_pos = _pos - attachedJoint->_finalMatrix.getPosition();
+
+		Math::Matrix4 jointMatrix = attachedJoint->_finalMatrix;
+		jointMatrix.transpose();
+		jointMatrix.transform(&_pos, true);
+	} else {
+		// Otherwise, use the actor's rotation relative to its parent.
+		newRot = getRotationQuat().inverse() * parent->getRotationQuat();
 	}
 
-	// Get the final rotation euler coordinates
+	// Get the final rotation Euler coordinates
 	newRot.getEuler(&_roll, &_yaw, &_pitch, Math::EO_ZYX);
 
-	// Get the final position coordinates
+	// Convert position into the parent actor's local space.
 	_pos = _pos - parentMatrix.getPosition();
 	parentMatrix.transpose();
 	parentMatrix.transform(&_pos, true);
 
-	// Save the attachement info
+	// Save the attachment info
 	_attachedActor = parent->getId();
 	_attachedJoint = jointStr;
 
diff --git a/engines/grim/actor.h b/engines/grim/actor.h
index bfe2f2f33a2..7fa30f5ed73 100644
--- a/engines/grim/actor.h
+++ b/engines/grim/actor.h
@@ -534,6 +534,7 @@ public:
 
 	bool isAttached() const { return _attachedActor != 0; }
 	Math::Vector3d getWorldPos() const;
+	Grim::Joint *getAttachedJoint(Actor *parent) const;
 	void attachToActor(Actor *other, const char *joint);
 	void detach();
 	Math::Quaternion getRotationQuat() const;




More information about the Scummvm-git-logs mailing list