Skip to content

Commit

Permalink
Merge pull request #1370 from CaptainSticky/feature/pose-import-tweaks
Browse files Browse the repository at this point in the history
Tweaks for Importing Poses and Expressions
  • Loading branch information
StoiaCode authored Aug 17, 2024
2 parents 225a3b4 + 6fa82a2 commit c101046
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 16 deletions.
22 changes: 12 additions & 10 deletions Anamnesis/Actor/Pages/PosePage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -250,33 +250,32 @@ private async Task Refresh()

private async void OnImportClicked(object sender, RoutedEventArgs e)
{
await this.ImportPose(false, PoseFile.Mode.Rotation);
await this.ImportPose(false, PoseFile.Mode.Rotation, true);
}

private async void OnImportScaleClicked(object sender, RoutedEventArgs e)
{
await this.ImportPose(false, PoseFile.Mode.Scale);
await this.ImportPose(false, PoseFile.Mode.Scale, true);
}

private async void OnImportSelectedClicked(object sender, RoutedEventArgs e)
{
await this.ImportPose(true, PoseFile.Mode.Rotation);
await this.ImportPose(true, PoseFile.Mode.Rotation, false);
}

private async void OnImportAllClicked(object sender, RoutedEventArgs e)
{
await this.ImportPose(false, PoseFile.Mode.All);
await this.ImportPose(false, PoseFile.Mode.All, true);
}

private async void OnImportBodyClicked(object sender, RoutedEventArgs e)
{
if (this.Skeleton == null)
return;

this.Skeleton.SelectHead();
this.Skeleton.InvertSelection();
this.Skeleton.SelectBody();

await this.ImportPose(true, PoseFile.Mode.Rotation);
await this.ImportPose(true, PoseFile.Mode.Rotation, false);
this.Skeleton.ClearSelection();
}

Expand All @@ -296,11 +295,14 @@ private async void OnImportExpressionClicked(object sender, RoutedEventArgs e)
}

this.Skeleton.SelectHead();
await this.ImportPose(true, PoseFile.Mode.Rotation | PoseFile.Mode.Scale);
await this.ImportPose(true, PoseFile.Mode.Rotation | PoseFile.Mode.Scale | PoseFile.Mode.Position, true);
this.Skeleton.ClearSelection();
}

private async Task ImportPose(bool selectionOnly, PoseFile.Mode mode)
// We want to skip doing the "Facial Expression Hack" if we're posing by selected bones or by body.
// Otherwise, the head won't pose as intended and will return to its original position.
// Not quite !selectedOnly, due to posing by expression actually needing the hack.
private async Task ImportPose(bool selectionOnly, PoseFile.Mode mode, bool doFacialExpressionHack)
{
try
{
Expand Down Expand Up @@ -351,7 +353,7 @@ private async Task ImportPose(bool selectionOnly, PoseFile.Mode mode)
}
}

await poseFile.Apply(this.Actor, this.Skeleton, bones, mode);
await poseFile.Apply(this.Actor, this.Skeleton, bones, mode, doFacialExpressionHack);
}
}
catch (Exception ex)
Expand Down
12 changes: 12 additions & 0 deletions Anamnesis/Actor/Posing/Visuals/SkeletonVisual3d.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,18 @@ public bool GetIsBoneParentsHovered(BoneVisual3d? bone)
return bone;
}

public void SelectBody()
{
this.SelectHead();
this.InvertSelection();

List<BoneVisual3d> additionalBones = new List<BoneVisual3d>();
BoneVisual3d? headBone = this.GetBone("j_kao");
if (headBone != null)
additionalBones.Add(headBone);
this.Select(additionalBones, SkeletonVisual3d.SelectMode.Add);
}

public void SelectHead()
{
this.ClearSelection();
Expand Down
2 changes: 1 addition & 1 deletion Anamnesis/Actor/Refresh/BrioActorRefresher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public async Task RefreshActor(ActorMemory actor)
// Restore current pose
skeletonVisual3D = new SkeletonVisual3d();
await skeletonVisual3D.SetActor(actor);
await poseFile.Apply(actor, skeletonVisual3D, null, PoseFile.Mode.All);
await poseFile.Apply(actor, skeletonVisual3D, null, PoseFile.Mode.All, true);
}).Start();
}
}
Expand Down
11 changes: 8 additions & 3 deletions Anamnesis/Files/PoseFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public void WriteToFile(ActorMemory actor, SkeletonVisual3d skeleton, HashSet<st
}
}

public async Task Apply(ActorMemory actor, SkeletonVisual3d skeleton, HashSet<string>? bones, Mode mode)
public async Task Apply(ActorMemory actor, SkeletonVisual3d skeleton, HashSet<string>? bones, Mode mode, bool doFacialExpressionHack)
{
if (actor == null)
throw new ArgumentNullException(nameof(actor));
Expand Down Expand Up @@ -154,15 +154,19 @@ public async Task Apply(ActorMemory actor, SkeletonVisual3d skeleton, HashSet<st
// Since all facial bones are parented to the head, if we load the head rotation from
// the pose that matches the expression, it wont break.
// We then just set the head back to where it should be afterwards.
// We can skip this if we actually intend to pose the head
// in the case of posing by selected bones or by body.
BoneVisual3d? headBone = skeleton.GetBone("j_kao");
Quaternion? originalHeadRotation = null;
if (bones != null && bones.Contains("j_kao"))
Vector? originalHeadPosition = null;
if (doFacialExpressionHack && bones != null && bones.Contains("j_kao"))
{
if (headBone == null)
throw new Exception("Unable to find head (j_kao) bone.");

headBone.Tick();
originalHeadRotation = headBone?.Rotation;
originalHeadPosition = headBone?.Position;
}

// Apply all transforms a few times to ensure parent-inherited values are caluclated correctly, and to ensure
Expand Down Expand Up @@ -223,9 +227,10 @@ public async Task Apply(ActorMemory actor, SkeletonVisual3d skeleton, HashSet<st
}

// Restore the head bone rotation if we were only loading an expression
if (headBone != null && originalHeadRotation != null)
if (headBone != null && originalHeadRotation != null && originalHeadPosition != null)
{
headBone.Rotation = (Quaternion)originalHeadRotation;
headBone.Position = (Vector)originalHeadPosition;
headBone.WriteTransform(skeleton, true);
}

Expand Down
2 changes: 1 addition & 1 deletion Anamnesis/Files/SceneFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public async Task Apply(Mode mode)

if (mode.HasFlag(Mode.Pose))
{
await entry.Pose!.Apply(actor, skeleton, null, PoseFile.Mode.Rotation);
await entry.Pose!.Apply(actor, skeleton, null, PoseFile.Mode.Rotation, true);
}
}

Expand Down
2 changes: 1 addition & 1 deletion Anamnesis/Views/TargetSelectorView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ private async void OnCreateActorClicked(object sender, RoutedEventArgs e)
fullActor.SetAddress(newActor.Address);
fullActor.Tick();
await skeletonVisual3D.SetActor(fullActor);
await poseFile.Apply(fullActor, skeletonVisual3D, null, PoseFile.Mode.Rotation);
await poseFile.Apply(fullActor, skeletonVisual3D, null, PoseFile.Mode.Rotation, true);
}
}

Expand Down

0 comments on commit c101046

Please sign in to comment.