Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement --position-scaling option to AnbMaker #754

Merged
merged 6 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 63 additions & 15 deletions OpenKh.Command.AnbMaker/Commands/AnbCommand.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Assimp;
using McMaster.Extensions.CommandLineUtils;
using McMaster.Extensions.CommandLineUtils.Conventions;
using NLog;
using OpenKh.Command.AnbMaker.Commands.Interfaces;
using OpenKh.Command.AnbMaker.Commands.Utils;
Expand All @@ -14,6 +15,7 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using static OpenKh.Command.AnbMaker.Utils.Builder.RawMotionBuilder;
using static OpenKh.Kh2.Motion;

Expand All @@ -37,9 +39,12 @@ internal class AnbCommand : IFbxSourceItemSelector, IMsetInjector
[Option(Description = "specify mesh name to read bone data", ShortName = "m")]
public string MeshName { get; set; }

[Option(Description = "apply scaling to each source node", ShortName = "x")]
[Option(Description = "apply scaling to each source node", ShortName = "x", LongName = "node-scaling")]
public float NodeScaling { get; set; } = 1;

[Option(Description = "apply scaling to each bone position", ShortName = "p", LongName = "position-scaling")]
public float PositionScaling { get; set; } = 1;

[Option(Description = "specify animation name to read bone data", ShortName = "a")]
public string AnimationName { get; set; }

Expand All @@ -62,7 +67,8 @@ protected int OnExecute(CommandLineApplication app)
MeshName,
RootName,
AnimationName,
NodeScaling
NodeScaling,
PositionScaling
)
.Parameters;

Expand Down Expand Up @@ -111,10 +117,11 @@ public UseAssimpForRaw(
string meshName,
string rootName,
string animationName,
float nodeScaling
float nodeScaling,
float positionScaling
)
{
var assimp = new Assimp.AssimpContext();
using var assimp = new Assimp.AssimpContext();
var scene = assimp.ImportFile(inputModel, Assimp.PostProcessSteps.None);

var outputList = new List<RawMotionBuilder.Parameter>();
Expand All @@ -129,10 +136,33 @@ bool IsAnimationNameMatched(string it) =>
? true
: it == animationName;

foreach (var fbxMesh in scene.Meshes.Where(mesh => IsMeshNameMatched(mesh.Name)))
var hits = new List<(Mesh mesh, Matrix4x4 transform)>();

void WalkNode(Assimp.Node node, Matrix4x4 parentTransform)
{
var fbxArmatureRoot = scene.RootNode.FindNode(rootName ?? "bone000"); //"kh_sk"
var fbxArmatureNodes = AssimpHelper.FlattenNodes(fbxArmatureRoot);
var transform = node.Transform * parentTransform;

foreach (var childNode in node.Children)
{
WalkNode(childNode, transform);
}

foreach (var meshIdx in node.MeshIndices)
{
var fbxMesh = scene.Meshes[meshIdx];
if (IsMeshNameMatched(fbxMesh.Name))
{
hits.Add((fbxMesh, transform));
}
}
}

WalkNode(scene.RootNode, Matrix4x4.Identity);

foreach (var hit in hits.Take(1))
{
var fbxArmatureRoot = AssimpHelper.FindRootBone(scene.RootNode, rootName);
var fbxArmatureNodes = AssimpHelper.FlattenNodes(fbxArmatureRoot, hit.mesh);
var fbxArmatureBoneCount = fbxArmatureNodes.Length;

foreach (var fbxAnim in scene.Animations.Where(anim => IsAnimationNameMatched(anim.Name)))
Expand All @@ -156,27 +186,45 @@ bool IsAnimationNameMatched(string it) =>

GetRelativeMatrix = (frameIdx, boneIdx) =>
{
var name = fbxArmatureNodes[boneIdx].ArmatureNode.Name;
var nodeRef = fbxArmatureNodes[boneIdx];
var armatureNode = nodeRef.ArmatureNode;
var name = armatureNode.Name;

var oldTransform = armatureNode.Transform;

oldTransform.Decompose(
out Vector3D initialScaling,
out Quaternion initialQuaternion,
out Vector3D initialTranslation
);

var hit = fbxAnim.NodeAnimationChannels.FirstOrDefault(it => it.NodeName == name);

var translation = (hit == null)
? new Vector3D(0, 0, 0)
? initialTranslation
: hit.PositionKeys.GetInterpolatedVector(frameIdx);

var rotation = (hit == null)
? new Assimp.Quaternion(w: 1, 0, 0, 0)
? initialQuaternion
: hit.RotationKeys.GetInterpolatedQuaternion(frameIdx);

var scale = (hit == null)
? new Vector3D(1, 1, 1)
? initialScaling
: hit.ScalingKeys.GetInterpolatedVector(frameIdx);

return System.Numerics.Matrix4x4.Identity
* System.Numerics.Matrix4x4.CreateFromQuaternion(rotation.ToDotNetQuaternion())
* System.Numerics.Matrix4x4.CreateScale(scale.ToDotNetVector3())
* System.Numerics.Matrix4x4.CreateTranslation(translation.ToDotNetVector3())
Vector3D FixScaling(Vector3D value) =>
value;

Vector3D FixPos(Vector3D value) =>
value * positionScaling;

var newTransform = Matrix4x4.Identity
* new Matrix4x4(rotation.GetMatrix())
* Matrix4x4.FromScaling(FixScaling(scale))
* Matrix4x4.FromTranslation(FixPos(translation))
;

return newTransform.ToDotNetMatrix4x4();
}
}
);
Expand Down
8 changes: 6 additions & 2 deletions OpenKh.Command.AnbMaker/Commands/AnbExCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ internal class AnbExCommand : IFbxSourceItemSelector, IMsetInjector
[Option(Description = "specify animation name to read bone data", ShortName = "a")]
public string AnimationName { get; set; }

[Option(Description = "apply scaling to each source node", ShortName = "x")]
[Option(Description = "apply scaling to each source node", ShortName = "x", LongName = "node-scaling")]
public float NodeScaling { get; set; } = 1;

[Option(Description = "apply scaling to each bone position", ShortName = "p", LongName = "position-scaling")]
public float PositionScaling { get; set; } = 1;

[Option(Description = "optionally inject new motion into mset directly", ShortName = "w")]
public string MsetFile { get; set; }

Expand Down Expand Up @@ -75,7 +78,8 @@ protected int OnExecute(CommandLineApplication app)
meshName: MeshName,
rootName: RootName,
animationName: AnimationName,
nodeScaling: NodeScaling
nodeScaling: NodeScaling,
positionScaling: PositionScaling
)
.Parameters;
}
Expand Down
127 changes: 127 additions & 0 deletions OpenKh.Command.AnbMaker/Commands/DumpNodeTreeCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using McMaster.Extensions.CommandLineUtils;
using OpenKh.Command.AnbMaker.Extensions;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace OpenKh.Command.AnbMaker.Commands
{
[HelpOption]
[Command(Description = "fbx file: dump node tree")]
internal class DumpNodeTreeCommand
{
[Required]
[FileExists]
[Argument(0, Description = "fbx input")]
public string? InputModel { get; set; }

[Option(Description = "print decomposed scale-rotation-translation", ShortName = "d", LongName = "decompose")]
public bool Decompose { get; set; }

[Option(Description = "print transform matrix", ShortName = "m", LongName = "matrix")]
public bool Matrix { get; set; }

[Option(Description = "print absolute", ShortName = "a", LongName = "absolute")]
public bool Absolute { get; set; }

protected int OnExecute(CommandLineApplication app)
{
var assimp = new Assimp.AssimpContext();
var scene = assimp.ImportFile(InputModel, Assimp.PostProcessSteps.None);

void Walk(Assimp.Node node, Matrix4x4 parent, int depth)
{
var more = "";

var thisMatrix = node.Transform.ToDotNetMatrix4x4() * parent;

if (Decompose)
{
System.Numerics.Matrix4x4.Decompose(
thisMatrix,
out Vector3 scale,
out System.Numerics.Quaternion rotation,
out Vector3 translation
);

more += $" S{scale} R{(ToDegree(rotation.ToEulerAngles()))} T{translation}";
}

if (Matrix)
{
more += thisMatrix;
}

Console.WriteLine(new string(' ', depth) + "+ " + node.Name + more);

if (node.HasMeshes)
{
foreach (var meshIdx in node.MeshIndices)
{
var mesh = scene.Meshes[meshIdx];
Console.WriteLine(new string(' ', depth + 1) + "$ " + mesh.Name);

if (mesh.HasBones)
{
foreach (var bone in mesh.Bones)
{
var boneMore = "";

var boneMatrix = bone.OffsetMatrix.ToDotNetMatrix4x4();

if (Decompose)
{
System.Numerics.Matrix4x4.Decompose(
boneMatrix,
out Vector3 scale,
out System.Numerics.Quaternion rotation,
out Vector3 translation
);

boneMore += $" S{scale} R{(ToDegree(rotation.ToEulerAngles()))} T{translation}";
}

if (Matrix)
{
boneMore += " " + boneMatrix;
}

Console.WriteLine(new string(' ', depth + 1) + " + " + bone.Name + boneMore);
}
}
}
}

if (node.HasChildren)
{
foreach (var child in node.Children)
{
Walk(
child,
Absolute ? thisMatrix : Matrix4x4.Identity,
depth + 1
);
}
}
}

Walk(scene.RootNode, Matrix4x4.Identity, 0);

return 0;
}

private static Vector3 ToDegree(Vector3 vec)
{
var f = (float)(1.0f / Math.PI * 180);
return new Vector3(
(int)(vec.X * f),
(int)(vec.Y * f),
(int)(vec.Z * f)
);
}
}
}
Loading