Skip to content

Commit

Permalink
Model space support
Browse files Browse the repository at this point in the history
- package updates
- cleanup unused parts
- move export type to common options instead of just layout
- use export type for character exports
- add option to toggle pose export
- add option to include model scaling (used by c+ in meddle exports)
  • Loading branch information
PassiveModding committed Sep 27, 2024
1 parent 7994aa5 commit beae632
Show file tree
Hide file tree
Showing 27 changed files with 482 additions and 2,476 deletions.
2 changes: 1 addition & 1 deletion Meddle/Meddle.Plugin/Meddle.Plugin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"/>
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0"/>
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0"/>
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="0.5.0-beta.6"/>
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="0.5.0-beta.7" />
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.9.0"/>
<PackageReference Include="Vortice.Direct3D11" Version="3.5.0"/>
<Reference Include="Dalamud">
Expand Down
10 changes: 7 additions & 3 deletions Meddle/Meddle.Plugin/Models/Composer/CharacterComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@ public class CharacterComposer
private static TexFile? CubeMapTex;
private static PbdFile? PbdFile;
private static readonly object StaticFileLock = new();
private readonly SkeletonUtils.PoseMode poseMode;
private readonly bool includePose;

public CharacterComposer(DataProvider dataProvider, Action<ProgressEvent>? progress = null)
public CharacterComposer(DataProvider dataProvider, Configuration config, Action<ProgressEvent>? progress = null)
{
this.dataProvider = dataProvider;
this.progress = progress;
includePose = config.IncludePose;
poseMode = config.PoseMode;

lock (StaticFileLock)
{
Expand Down Expand Up @@ -306,7 +310,7 @@ private bool HandleRootAttach(ParsedCharacterInfo characterInfo, SceneBuilder sc
BoneNodeBuilder? rootBone;
try
{
bones = SkeletonUtils.GetBoneMap(characterInfo.Skeleton, true, out rootBone);
bones = SkeletonUtils.GetBoneMap(characterInfo.Skeleton, includePose ? poseMode : null, out rootBone);
if (rootBone == null)
{
Plugin.Logger?.LogWarning("Root bone not found");
Expand Down Expand Up @@ -402,7 +406,7 @@ public void ComposeModels(ParsedModelInfo[] models, GenderRace genderRace, Custo
BoneNodeBuilder? rootBone;
try
{
bones = SkeletonUtils.GetBoneMap(skeleton, true, out rootBone);
bones = SkeletonUtils.GetBoneMap(skeleton, includePose ? poseMode : null, out rootBone);
if (rootBone == null)
{
Plugin.Logger?.LogWarning("Root bone not found");
Expand Down
18 changes: 18 additions & 0 deletions Meddle/Meddle.Plugin/Models/Enums.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Meddle.Plugin.Models;

public enum MenuType
{
Default = 0,
Debug = 1,
Testing = 2,
}

[Flags]
public enum ExportType
{
// ReSharper disable InconsistentNaming
GLTF = 1,
GLB = 2,
OBJ = 4
// ReSharper restore InconsistentNaming
}
40 changes: 0 additions & 40 deletions Meddle/Meddle.Plugin/Models/Groups.cs

This file was deleted.

8 changes: 0 additions & 8 deletions Meddle/Meddle.Plugin/Models/MenuType.cs

This file was deleted.

25 changes: 17 additions & 8 deletions Meddle/Meddle.Plugin/Models/Skeletons/ParsedHkaPose.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,32 @@ public unsafe ParsedHkaPose(Pointer<hkaPose> pose) : this(pose.Value) { }

public unsafe ParsedHkaPose(hkaPose* pose)
{
var boneCount = pose->Skeleton->Bones.Length;

var transforms = new List<Transform>();

var boneCount = pose->LocalPose.Length;
var syncedLocalPose = pose->GetSyncedPoseLocalSpace()->Data;
for (var i = 0; i < boneCount; ++i)
{
var localSpace = pose->AccessBoneLocalSpace(i);
if (localSpace == null)
{
throw new Exception("Failed to access bone local space");
}
var localSpace = syncedLocalPose[i];
transforms.Add(new Transform(localSpace));
}

transforms.Add(new Transform(*localSpace));
var modelTransforms = new List<Transform>();
var modelSpace = pose->GetSyncedPoseModelSpace()->Data;
for (var i = 0; i < boneCount; ++i)
{
var model = modelSpace[i];
modelTransforms.Add(new Transform(model));
}


Pose = transforms;
ModelPose = modelTransforms;
}

[JsonIgnore]
public IReadOnlyList<Transform> Pose { get; }

[JsonIgnore]
public IReadOnlyList<Transform> ModelPose { get; }
}
32 changes: 29 additions & 3 deletions Meddle/Meddle.Plugin/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
using Dalamud.Configuration;
using Dalamud.IoC;
using Dalamud.Plugin;
using Meddle.Plugin.Models;
using Meddle.Plugin.Services;
using Meddle.Plugin.UI.Layout;
using Meddle.Plugin.Utils;
using Meddle.Utils.Files.SqPack;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand Down Expand Up @@ -96,6 +98,9 @@ public void Dispose()

public class Configuration : IPluginConfiguration
{
public const ExportType DefaultExportType = ExportType.GLTF;
public const SkeletonUtils.PoseMode DefaultPoseMode = SkeletonUtils.PoseMode.Model;

[PluginService]
[JsonIgnore]
private IDalamudPluginInterface PluginInterface { get; set; } = null!;
Expand All @@ -106,12 +111,33 @@ public class Configuration : IPluginConfiguration
public bool OpenOnLoad { get; set; }
public bool DisableUserUiHide { get; set; }
public bool DisableAutomaticUiHide { get; set; }
public bool DisableCutsceneUiHide { get; set; }
public bool DisableGposeUiHide { get; set; }
public bool DisableCutsceneUiHide { get; set; } = true;
public bool DisableGposeUiHide { get; set; } = true;
public float WorldCutoffDistance { get; set; } = 100;
public Vector4 WorldDotColor { get; set; } = new(1f, 1f, 1f, 0.5f);

/// <summary>
/// Used to hide names in the UI
/// </summary>
public string PlayerNameOverride { get; set; } = string.Empty;


/// <summary>
/// If enabled, pose will be included at 0 on the timeline under the 'pose' track.
/// </summary>
public bool IncludePose { get; set; } = true;

/// <summary>
/// Indicates whether scaling should be taken from the model pose rather than the local pose.
/// </summary>
public SkeletonUtils.PoseMode PoseMode { get; set; } = DefaultPoseMode;

/// <summary>
/// GLTF = GLTF JSON
/// GLB = GLTF Binary
/// OBJ = Wavefront OBJ
/// </summary>
public ExportType ExportType { get; set; } = DefaultExportType;

public int Version { get; set; } = 1;

public LayoutWindow.LayoutConfig LayoutConfig { get; set; } = new();
Expand Down
106 changes: 106 additions & 0 deletions Meddle/Meddle.Plugin/Services/AnimationExportService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.Diagnostics;
using System.Numerics;
using Meddle.Plugin.Models;
using Meddle.Plugin.Utils;
using Microsoft.Extensions.Logging;
using SharpGLTF.Geometry;
using SharpGLTF.Geometry.VertexTypes;
using SharpGLTF.Materials;
using SharpGLTF.Scenes;

namespace Meddle.Plugin.Services;

public class AnimationExportService : IDisposable, IService
{
private static readonly ActivitySource ActivitySource = new("Meddle.Plugin.Utils.ExportUtil");
private readonly Configuration configuration;
private readonly ILogger<AnimationExportService> logger;

public AnimationExportService(Configuration configuration, ILogger<AnimationExportService> logger)
{
this.configuration = configuration;
this.logger = logger;
}

public void Dispose()
{
logger.LogDebug("Disposing ExportUtil");
}

public void ExportAnimation(List<(DateTime, AttachSet[])> frames, bool includePositionalData, string path, CancellationToken token = default)
{
try
{
using var activity = ActivitySource.StartActivity();
var boneSets = SkeletonUtils.GetAnimatedBoneMap(frames.ToArray(), configuration.PoseMode);
var startTime = frames.Min(x => x.Item1);
//var folder = GetPathForOutput();
var folder = path;
Directory.CreateDirectory(folder);
foreach (var (id, (bones, root, timeline)) in boneSets)
{
var scene = new SceneBuilder();
if (root == null) throw new InvalidOperationException("Root bone not found");
logger.LogInformation("Adding bone set {Id}", id);

if (includePositionalData)
{
var startPos = timeline.First().Attach.Transform.Translation;
foreach (var frameTime in timeline)
{
if (token.IsCancellationRequested) return;
var pos = frameTime.Attach.Transform.Translation;
var rot = frameTime.Attach.Transform.Rotation;
var scale = frameTime.Attach.Transform.Scale;
var time = SkeletonUtils.TotalSeconds(frameTime.Time, startTime);
root.UseTranslation().UseTrackBuilder("pose").WithPoint(time, pos - startPos);
root.UseRotation().UseTrackBuilder("pose").WithPoint(time, rot);
root.UseScale().UseTrackBuilder("pose").WithPoint(time, scale);
}
}

scene.AddNode(root);
scene.AddSkinnedMesh(GetDummyMesh(id), Matrix4x4.Identity, bones.Cast<NodeBuilder>().ToArray());
var sceneGraph = scene.ToGltf2();
var outputPath = Path.Combine(folder, $"motion_{id}.gltf");
sceneGraph.SaveGLTF(outputPath);
}

Process.Start("explorer.exe", folder);
logger.LogInformation("Export complete");
}
catch (Exception e)
{
logger.LogError(e, "Failed to export animation");
throw;
}
}

// https://github.com/0ceal0t/Dalamud-VFXEditor/blob/be00131b93b3c6dd4014a4f27c2661093daf3a85/VFXEditor/Utils/Gltf/GltfSkeleton.cs#L132
public static MeshBuilder<VertexPosition, VertexEmpty, VertexJoints4> GetDummyMesh(string name = "DUMMY_MESH")
{
var dummyMesh = new MeshBuilder<VertexPosition, VertexEmpty, VertexJoints4>(name);
var material = new MaterialBuilder("material");

var p1 = new VertexPosition
{
Position = new Vector3(0.000001f, 0, 0)
};
var p2 = new VertexPosition
{
Position = new Vector3(0, 0.000001f, 0)
};
var p3 = new VertexPosition
{
Position = new Vector3(0, 0, 0.000001f)
};

dummyMesh.UsePrimitive(material).AddTriangle(
(p1, new VertexEmpty(), new VertexJoints4(0)),
(p2, new VertexEmpty(), new VertexJoints4(0)),
(p3, new VertexEmpty(), new VertexJoints4(0))
);

return dummyMesh;
}
}
4 changes: 2 additions & 2 deletions Meddle/Meddle.Plugin/Services/ComposerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ public CharacterComposer CreateCharacterComposer(string? cacheDir = null, Action
Directory.CreateDirectory(cacheDir);

var dataProvider = CreateDataProvider(cacheDir, cancellationToken);
return new CharacterComposer(dataProvider, progress);
return new CharacterComposer(dataProvider, configuration, progress);
}

public CharacterComposer CreateCharacterComposer(DataProvider dataProvider)
{
return new CharacterComposer(dataProvider);
return new CharacterComposer(dataProvider, configuration);
}
}
Loading

0 comments on commit beae632

Please sign in to comment.