Skip to content

Commit

Permalink
Merge pull request #1400 from ergoxiv/hotfix/flip-bones-positions-ena…
Browse files Browse the repository at this point in the history
…bled

Resolved atypical skeleton deformations when flipping bone rotations
  • Loading branch information
StoiaCode authored Nov 12, 2024
2 parents 6f5311d + a727531 commit 9c90442
Showing 1 changed file with 113 additions and 40 deletions.
153 changes: 113 additions & 40 deletions Anamnesis/Actor/Pages/PosePage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,44 @@ public List<BoneView> GetBoneViews(BoneVisual3d bone)
return results;
}

private void FlipBone(BoneVisual3d? targetBone, bool shouldFlip = true)
{
if (this.Skeleton == null)
throw new Exception("Skeleton is null");

if (targetBone == null)
throw new ArgumentException("The target bone cannot be null");

// Save the positions of the target bone and its children
// The transform memory is used to retrieve the parent-relative position of the bone
Dictionary<BoneVisual3d, Vector3> bonePositions = new()
{
{ targetBone, targetBone.Position },
};

if (PoseService.Instance.EnableParenting)
{
List<BoneVisual3d> boneChildren = new();
targetBone.GetChildren(ref boneChildren);
foreach (BoneVisual3d childBone in boneChildren)
{
bonePositions.Add(childBone, childBone.Position);
}
}

this.FlipBoneInternal(targetBone, shouldFlip);

// Restore positions after flipping
foreach ((BoneVisual3d bone, Vector3 parentRelPos) in bonePositions)
{
foreach (TransformMemory transformMemory in bone.TransformMemories)
{
bone.Position = parentRelPos;
bone.WriteTransform(this.Skeleton, false);
}
}
}

/* Basic Idea:
* get mirrored quat of targetBone
* check if its a 'left' bone
Expand All @@ -161,58 +199,62 @@ public List<BoneView> GetBoneViews(BoneVisual3d bone)
* - store the quat on the target bone
* - recursively flip on all child bones
*/

// TODO: This doesn't seem to be working correctly after the skeleton upgrade. not sure why...
private void FlipBone(BoneVisual3d? targetBone, bool shouldFlip = true)
private void FlipBoneInternal(BoneVisual3d? targetBone, bool shouldFlip = true)
{
if (targetBone != null)
if (targetBone == null)
throw new ArgumentException("The target bone cannot be null");

CmQuaternion newRotation = targetBone!.TransformMemory.Rotation.Mirror(); // character-relative transform
if (shouldFlip && targetBone.BoneName.EndsWith("_l"))
{
CmQuaternion newRotation = targetBone.TransformMemory.Rotation.Mirror(); // character-relative transform
if (shouldFlip && targetBone.BoneName.EndsWith("_l"))
string rightBoneString = targetBone.BoneName.Substring(0, targetBone.BoneName.Length - 2) + "_r"; // removes the "_l" and replaces it with "_r"
/* Useful debug lines to make sure the correct bones are grabbed...
* Log.Information("flipping: " + targetBone.BoneName);
* Log.Information("right flip target: " + rightBoneString); */
BoneVisual3d? rightBone = targetBone.Skeleton.GetBone(rightBoneString);
if (rightBone != null)
{
string rightBoneString = targetBone.BoneName.Substring(0, targetBone.BoneName.Length - 2) + "_r"; // removes the "_l" and replaces it with "_r"
/* Useful debug lines to make sure the correct bones are grabbed...
* Log.Information("flipping: " + targetBone.BoneName);
* Log.Information("right flip target: " + rightBoneString); */
BoneVisual3d? rightBone = targetBone.Skeleton.GetBone(rightBoneString);
if (rightBone != null)
CmQuaternion rightRot = rightBone.TransformMemory.Rotation.Mirror();
foreach (TransformMemory transformMemory in targetBone.TransformMemories)
{
CmQuaternion rightRot = rightBone.TransformMemory.Rotation.Mirror();
foreach (TransformMemory transformMemory in targetBone.TransformMemories)
{
transformMemory.Rotation = rightRot;
}

foreach (TransformMemory transformMemory in rightBone.TransformMemories)
{
transformMemory.Rotation = newRotation;
}
transformMemory.Rotation = rightRot;
}
else

targetBone.ReadTransform();

foreach (TransformMemory transformMemory in rightBone.TransformMemories)
{
Log.Warning("could not find right bone of: " + targetBone.BoneName);
transformMemory.Rotation = newRotation;
}

rightBone.ReadTransform();
}
else if (shouldFlip && targetBone.BoneName.EndsWith("_r"))
else
{
// do nothing so it doesn't revert...
Log.Warning("could not find right bone of: " + targetBone.BoneName);
}
else
}
else if (shouldFlip && targetBone.BoneName.EndsWith("_r"))
{
// do nothing so it doesn't revert...
}
else
{
foreach (TransformMemory transformMemory in targetBone.TransformMemories)
{
foreach (TransformMemory transformMemory in targetBone.TransformMemories)
{
transformMemory.Rotation = newRotation;
}
transformMemory.Rotation = newRotation;
}

if (PoseService.Instance.EnableParenting)
targetBone.ReadTransform();
}

if (PoseService.Instance.EnableParenting)
{
foreach (Visual3D? child in targetBone.Children)
{
foreach (Visual3D? child in targetBone.Children)
if (child is BoneVisual3d childBone)
{
if (child is BoneVisual3d childBone)
{
this.FlipBone(childBone, shouldFlip);
}
this.FlipBoneInternal(childBone, shouldFlip);
}
}
}
Expand Down Expand Up @@ -573,9 +615,31 @@ private void OnSelectChildrenClicked(object sender, RoutedEventArgs e)

private void OnFlipClicked(object sender, RoutedEventArgs e)
{
if (this.Skeleton != null && !this.IsFlipping)
if (this.Actor == null)
throw new ArgumentNullException(nameof(this.Actor));

if (this.Actor.ModelObject == null || this.Actor.ModelObject.Transform == null)
throw new Exception("Actor has no model");

if (this.Actor.ModelObject.Skeleton == null)
throw new Exception("Actor model has no skeleton. Are you trying to load a pose outside of GPose?");

if (this.Skeleton == null || this.IsFlipping)
return;

SkeletonMemory skeletonMem = this.Actor.ModelObject.Skeleton;
skeletonMem.PauseSynchronization = true;

bool originalAutoCommitEnabled = this.Actor.History.AutoCommitEnabled;

try
{
// if no bone selected, flip both lumbar and waist bones
// Disable auto-commit at the beginning
// Commit any changes if they are present to avoid falsely grouping actions
this.Actor.History.AutoCommitEnabled = false;
this.Actor.History.Commit();

// If no bone selected, flip both lumbar and waist bones
this.IsFlipping = true;
if (this.Skeleton.CurrentBone == null)
{
Expand All @@ -588,7 +652,7 @@ private void OnFlipClicked(object sender, RoutedEventArgs e)
}
else
{
// if targeted bone is a limb don't switch the respective left and right sides
// If targeted bone is a limb don't switch the respective left and right sides
BoneVisual3d targetBone = this.Skeleton.CurrentBone;
if (targetBone.BoneName.EndsWith("_l") || targetBone.BoneName.EndsWith("_r"))
{
Expand All @@ -603,6 +667,15 @@ private void OnFlipClicked(object sender, RoutedEventArgs e)
}

this.IsFlipping = false;

skeletonMem.PauseSynchronization = false;
skeletonMem.WriteDelayedBinds();
}
finally
{
// Re-enable auto-commit and commit changes
this.Actor.History.Commit();
this.Actor.History.AutoCommitEnabled = originalAutoCommitEnabled;
}
}

Expand Down

0 comments on commit 9c90442

Please sign in to comment.