Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions Meddle/Meddle.Plugin/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ public partial class Configuration : IPluginConfiguration
public bool DisableGposeUiHide { get; set; } = true;
public string ExportDirectory { get; set; } = Plugin.DefaultExportDirectory;
public string SecretConfig { get; set; } = string.Empty;
public bool DisplayDebugInfo { get; set; }
public bool OpenFolderOnExport { get; set; } = true;

/// <summary>
/// Used to hide names in the UI
Expand Down
2 changes: 1 addition & 1 deletion Meddle/Meddle.Plugin/Meddle.Plugin.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Dalamud.NET.Sdk/12.0.2">
<Project Sdk="Dalamud.NET.Sdk/13.0.0">
<PropertyGroup>
<Version>0.0.1</Version>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
23 changes: 19 additions & 4 deletions Meddle/Meddle.Plugin/Models/Composer/CharacterComposer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ public record SkinningContext(List<BoneNodeBuilder> Bones, BoneNodeBuilder? Root

private readonly ComposerCache composerCache;
private readonly Configuration.ExportConfiguration exportConfig;

public CharacterComposer(ComposerCache composerCache, Configuration.ExportConfiguration exportConfig)
private readonly CancellationToken cancellationToken;

public CharacterComposer(ComposerCache composerCache, Configuration.ExportConfiguration exportConfig, CancellationToken cancellationToken)
{
this.composerCache = composerCache;
this.exportConfig = exportConfig;
this.cancellationToken = cancellationToken;
}

public CharacterComposer(SqPack pack, Configuration.ExportConfiguration exportConfig, string outDir)
public CharacterComposer(SqPack pack, Configuration.ExportConfiguration exportConfig, string outDir, CancellationToken cancellationToken)
{
this.exportConfig = exportConfig;
this.cancellationToken = cancellationToken;
Directory.CreateDirectory(outDir);
var cacheDir = Path.Combine(outDir, "cache");
Directory.CreateDirectory(cacheDir);
Expand Down Expand Up @@ -392,6 +395,12 @@ private bool HandleRootAttach(

foreach (var t in characterInfo.Models)
{
if (cancellationToken.IsCancellationRequested)
{
Plugin.Logger?.LogInformation("Export cancelled, stopping model processing");
break;
}

try
{
HandleModel(characterInfo, t,
Expand All @@ -403,11 +412,17 @@ private bool HandleRootAttach(
Plugin.Logger?.LogError(e, "Failed to handle model\n{Message}\n{ModelInfo}", e.Message, JsonSerializer.Serialize(t, MaterialComposer.JsonOptions));
}

rootProgress.Progress++;
rootProgress.IncrementProgress();
}

foreach (var t in characterInfo.Attaches)
{
if (cancellationToken.IsCancellationRequested)
{
Plugin.Logger?.LogInformation("Export cancelled, stopping attach processing");
break;
}

ExportProgress? attachProgress = null;
try
{
Expand Down
6 changes: 3 additions & 3 deletions Meddle/Meddle.Plugin/Models/Composer/ComposerCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public MdlFile GetMdlFile(string path)
CacheFile(path);
}

if (mdlCache.Count > 100)
if (mdlCache.Count > 1000)
{
var toRemove = mdlCache.OrderBy(x => x.Value.LastAccess).First();
mdlCache.TryRemove(toRemove.Key, out _);
Expand Down Expand Up @@ -118,7 +118,7 @@ public MtrlFile GetMtrlFile(string path, out string? cachePath)
mtrlPathCache.TryAdd(path, cachePath);
}

if (mtrlCache.Count > 100)
if (mtrlCache.Count > 1000)
{
// evict least recently accessed
var toRemove = mtrlCache.OrderBy(x => x.Value.LastAccess).First();
Expand Down Expand Up @@ -290,7 +290,7 @@ public MaterialBuilder ComposeMaterial(string mtrlPath,
materialName = $"{Path.GetFileNameWithoutExtension(mtrlPath)}_{mtrlFile.GetShaderPackageName()}";
}

var materialBuilder = new RawMaterialBuilder(materialName);
var materialBuilder = new MaterialBuilder(materialName);
foreach (var texture in material.TextureUsageDict)
{
// ensure texture gets saved to cache dir.
Expand Down
76 changes: 59 additions & 17 deletions Meddle/Meddle.Plugin/Models/Composer/InstanceComposer.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Numerics;
using System.Collections.Concurrent;
using System.Numerics;
using System.Text.Json;
using System.Text.Json.Nodes;
using Meddle.Plugin.Models.Layout;
using Meddle.Plugin.Models.Structs;
using Meddle.Plugin.UI.Layout;
using Meddle.Plugin.Utils;
using Meddle.Utils;
using Meddle.Utils.Export;
Expand All @@ -22,13 +24,20 @@ public ExportProgress(int total, string? name)
Total = total;
Name = name;
}

public int Progress;

private int _progress;
public int Progress { get { return _progress; } }
public void IncrementProgress(int amount = 1)
{
Interlocked.Add(ref _progress, amount);
Parent?.IncrementProgress(amount);
}
public int Total;
public bool IsComplete;
public string? Name;
public ExportProgress? Parent;

public List<ExportProgress> Children = [];
public readonly ConcurrentBag<ExportProgress> Children = [];
}

public class InstanceComposer
Expand Down Expand Up @@ -98,10 +107,16 @@ private void ComposeInstanceGroup(IGrouping<ParsedInstanceType, ParsedInstance>
var orderedInstances = group.OrderBy(x => x.Transform.Translation.LengthSquared()).ToArray();
var lastSceneIdx = 0;

// var orderedInstanceChunks = orderedInstances
// .Select((x, i) => new { Index = i, Value = x })
// .GroupBy(x => x.Index / 100)
// .Select(g => g.Select(x => x.Value).ToArray())
// .ToArray();

for (var i = 0; i < orderedInstances.Length; i++)
{
if (cancellationToken.IsCancellationRequested) break;
progress.Progress++;
progress.IncrementProgress();
try
{
if (ComposeInstance(orderedInstances[i], scene, progress) != null)
Expand Down Expand Up @@ -142,14 +157,13 @@ private void ComposeInstanceGroup(IGrouping<ParsedInstanceType, ParsedInstance>
}
}
}



SaveRemainingScenes(group.Key, lastSceneIdx, orderedInstances.Length, scenes);
}

public void Compose(ParsedInstance[] instances, ExportProgress progress)
public void Compose(ParsedInstance[] instances, ProgressWrapper wrapper)
{
progress.Total = instances.Length;
progress.Progress = 0;
try
{
var instanceBlob = JsonSerializer.Serialize(instances, MaterialComposer.JsonOptions);
Expand All @@ -163,11 +177,37 @@ public void Compose(ParsedInstance[] instances, ExportProgress progress)
composerCache.SaveArrayTextures();

var instanceGroups = instances.GroupBy(x => x.Type);
// place characters and shared last to reduce shifting in ui
instanceGroups = instanceGroups
.OrderByDescending(x => x.Key == ParsedInstanceType.Character)
.ThenByDescending(x => x.Key == ParsedInstanceType.SharedGroup)
.ThenBy(x => x.Key)
.ToArray();

foreach (var group in instanceGroups)
{
ComposeInstanceGroup(group, progress);
}
// foreach (var group in instanceGroups)
// {
// ComposeInstanceGroup(group, progress);
// }
wrapper.Progress = new ExportProgress(instances.Length, "Composing Instances");
Parallel.ForEach(instanceGroups, group =>
{
if (cancellationToken.IsCancellationRequested) return;
Plugin.Logger?.LogInformation("Composing instances of type {InstanceType} ({InstanceCount})", group.Key, group.Count());
var groupProgress = new ExportProgress(group.Count(), $"Composing {group.Key}")
{
Parent = wrapper.Progress
};
wrapper.Progress.Children.Add(groupProgress);
try
{
ComposeInstanceGroup(group, groupProgress);
}
catch (Exception ex)
{
Plugin.Logger?.LogError(ex, "Failed to compose instance group {InstanceType}\n{Message}", group.Key, ex.Message);
}
groupProgress.IsComplete = true;
});

Plugin.Logger?.LogInformation("Finished composing instances");
}
Expand Down Expand Up @@ -265,7 +305,7 @@ private NodeBuilder ComposeCameraInstance(ParsedCameraInstance parsedCameraInsta
Plugin.Logger?.LogWarning("Character instance {InstanceId} has no character info", instance.Id);
return null;
}
var characterComposer = new CharacterComposer(composerCache, exportConfig);
var characterComposer = new CharacterComposer(composerCache, exportConfig, cancellationToken);
var root = new NodeBuilder($"{instance.Type}_{instance.Name}_{instance.Id}");

var characterProgress = new ExportProgress(instance.CharacterInfo.Models.Length, "Character Meshes");
Expand Down Expand Up @@ -318,6 +358,7 @@ private NodeBuilder ComposeCameraInstance(ParsedCameraInstance parsedCameraInsta
bool validChild = false;
foreach (var child in instance.Children)
{
if (cancellationToken.IsCancellationRequested) break;
var childNode = ComposeInstance(child, scene, sharedGroupProgress);
if (childNode != null)
{
Expand Down Expand Up @@ -422,6 +463,7 @@ public NodeBuilder ComposeTerrain(ParsedTerrainInstance terrainInstance, SceneBu

foreach (var (i, platePos, distance) in terrainPlates)
{
if (cancellationToken.IsCancellationRequested) break;
Plugin.Logger?.LogInformation("Parsing plate {i}", i);
var plateTransform = new Transform(new Vector3(platePos.X, 0, platePos.Y), Quaternion.Identity, Vector3.One);
if (exportConfig.LimitTerrainExportRange)
Expand All @@ -431,7 +473,7 @@ public NodeBuilder ComposeTerrain(ParsedTerrainInstance terrainInstance, SceneBu
{
Plugin.Logger?.LogDebug("Skipping plate {i} at distance {distance} from search origin {searchOrigin} (limit: {limit})",
i, distance, searchOrigin, exportConfig.TerrainExportDistance);
terrainProgress.Progress++;
terrainProgress.IncrementProgress();
continue;
}
}
Expand Down Expand Up @@ -466,7 +508,7 @@ public NodeBuilder ComposeTerrain(ParsedTerrainInstance terrainInstance, SceneBu

plateRoot.SetLocalTransform(plateTransform.AffineTransform, true);
root.AddNode(plateRoot);
terrainProgress.Progress++;
terrainProgress.IncrementProgress();
}

terrainProgress.IsComplete = true;
Expand Down Expand Up @@ -518,7 +560,7 @@ public NodeBuilder ComposeTerrain(ParsedTerrainInstance terrainInstance, SceneBu
{
if (instance.Light.Range <= 0)
{
Plugin.Logger?.LogWarning("Light {LightId} has a range of 0 or less ({Range})", instance.Id, instance.Light.Range);
// Plugin.Logger?.LogWarning("Light {LightId} has a range of 0 or less ({Range})", instance.Id, instance.Light.Range);
return null;
}

Expand Down
15 changes: 0 additions & 15 deletions Meddle/Meddle.Plugin/Models/Composer/RawMaterialBuilder.cs

This file was deleted.

2 changes: 1 addition & 1 deletion Meddle/Meddle.Plugin/Models/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.ComponentModel;
using Dalamud.Interface.Utility.Raii;
using ImGuiNET;
using Dalamud.Bindings.ImGui;

Check failure on line 3 in Meddle/Meddle.Plugin/Models/EnumExtensions.cs

View workflow job for this annotation

GitHub Actions / Build (9.0.x, latest)

The type or namespace name 'Bindings' does not exist in the namespace 'Dalamud' (are you missing an assembly reference?)

namespace Meddle.Plugin.Models;

Expand Down
2 changes: 1 addition & 1 deletion Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Numerics;
using System.Text.Json.Serialization;
using Dalamud.Bindings.ImGui;

Check failure on line 3 in Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs

View workflow job for this annotation

GitHub Actions / Build (9.0.x, latest)

The type or namespace name 'Bindings' does not exist in the namespace 'Dalamud' (are you missing an assembly reference?)
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.LayoutEngine;
using ImGuiNET;
using Lumina.Excel.Sheets;
using Meddle.Plugin.Models.Skeletons;
using Meddle.Plugin.Models.Structs;
Expand Down
9 changes: 7 additions & 2 deletions Meddle/Meddle.Plugin/Services/AnimationExportService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ namespace Meddle.Plugin.Services;
public class AnimationExportService : IDisposable, IService
{
private readonly ILogger<AnimationExportService> logger;
private readonly Configuration config;

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

public void Dispose()
Expand Down Expand Up @@ -68,7 +70,10 @@ public void ExportAnimation(
sceneGraph.SaveGLTF(outputPath);
}

Process.Start("explorer.exe", folder);
if (config.OpenFolderOnExport)
{
Process.Start("explorer.exe", folder);
}
logger.LogInformation("Export complete");
}
catch (Exception e)
Expand Down
2 changes: 1 addition & 1 deletion Meddle/Meddle.Plugin/Services/ComposerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ public CharacterComposer CreateCharacterComposer(string outDir,
Configuration.ExportConfiguration exportConfig,
CancellationToken cancellationToken = default)
{
return new CharacterComposer(pack, exportConfig, outDir);
return new CharacterComposer(pack, exportConfig, outDir, cancellationToken);
}
}
15 changes: 11 additions & 4 deletions Meddle/Meddle.Plugin/Services/LayoutService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,16 +253,23 @@ private unsafe ParsedInstanceSet[] ParseLayout(LayoutManager* activeLayout, Pars
if (typedInstance->DecalPtr == null || typedInstance->DecalPtr->DecalItem == null)
return null;

var transform = decalLayout->GetTransformImpl();
if (transform == null)
return null;

var decalData = typedInstance->DecalPtr->DecalItem;
var diffuseTex = decalData->TexDiffuse;
var diffusePath = diffuseTex != null ? diffuseTex->FileName.ParseString() : string.Empty;
var normalTex = decalData->TexNormal;
var normalPath = normalTex != null ? normalTex->FileName.ParseString() : string.Empty;
var specularTex = decalData->TexSpecular;
var specularPath = specularTex != null ? specularTex->FileName.ParseString() : string.Empty;

return new ParsedWorldDecalInstance((nint)decalLayout,
new Transform(*decalLayout->GetTransformImpl()),
diffuseTex->FileName.ParseString(),
normalTex->FileName.ParseString(),
specularTex->FileName.ParseString());
new Transform(*transform),
diffusePath,
normalPath,
specularPath);
}

private unsafe ParsedLightInstance? ParsedLightInstance(Pointer<ILayoutInstance> lightPtr)
Expand Down
2 changes: 1 addition & 1 deletion Meddle/Meddle.Plugin/Services/PbdHooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Meddle.Plugin.Services;

public class PbdHooks : IDisposable, IService
{
public const string HumanCreateDeformerSig = "40 53 48 83 EC 20 4C 8B C1 83 FA 0D";
public const string HumanCreateDeformerSig = "48 89 5C 24 ?? 57 48 83 EC 30 4C 8B C1 ";
private readonly Dictionary<nint, Dictionary<uint, DeformerCachedStruct>> deformerCache = new();
private readonly ILogger<PbdHooks> logger;
private readonly Hook<HumanCreateDeformerDelegate>? humanCreateDeformerHook;
Expand Down
4 changes: 2 additions & 2 deletions Meddle/Meddle.Plugin/Services/ResolverService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ private void ResolveInstance(ParsedInstance instance)
}

var material = materialPtr.Value;
var materialPath = material->MaterialResourceHandle->ResourceHandle.FileName.ParseString();
var materialPath = material->MaterialResourceHandle->FileName.ParseString();
var materialPathFromModel =
model->ModelResourceHandle->GetMaterialFileNameBySlot((uint)mtrlIdx);
var shaderName = material->MaterialResourceHandle->ShpkName;
Expand Down Expand Up @@ -354,7 +354,7 @@ private void ResolveInstance(ParsedInstance instance)
continue;
}

var materialPath = material->ResourceHandle.FileName.ParseString();
var materialPath = material->FileName.ParseString();
var pathFromModel = handle->GetMaterialFileNameBySlot((uint)i);
var shaderName = material->ShpkName;
IColorTableSet? colorTable = null;
Expand Down
Loading