Skip to content

Commit 1f30f18

Browse files
Parse and use model-level render flag in export logic
1 parent b3718ee commit 1f30f18

8 files changed

Lines changed: 65 additions & 23 deletions

File tree

Meddle/Meddle.Plugin/Configuration.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ public class ExportConfiguration
5353

5454
[Obsolete("Use PoseMode instead", true)]
5555
public bool ExportPose { get; set; } = true;
56+
57+
[Obsolete("Use ApplyVisibilityFlags instead", true)]
5658
public bool RemoveAttributeDisabledSubmeshes { get; set; } = true;
59+
60+
public bool ApplyVisibilityFlags { get; set; } = true;
5761
public bool SkipHiddenBgParts { get; set; }
5862
public bool UseDeformer { get; set; } = true;
5963

@@ -78,7 +82,8 @@ public ExportConfiguration Clone()
7882
// ExportPose = ExportPose,
7983
// TextureMode = TextureMode,
8084
PoseMode = PoseMode,
81-
RemoveAttributeDisabledSubmeshes = RemoveAttributeDisabledSubmeshes,
85+
// RemoveAttributeDisabledSubmeshes = RemoveAttributeDisabledSubmeshes,
86+
ApplyVisibilityFlags = ApplyVisibilityFlags,
8287
SkipHiddenBgParts = SkipHiddenBgParts,
8388
// RootAttachHandling = RootAttachHandling
8489
UseDeformer = UseDeformer,
@@ -90,9 +95,10 @@ public ExportConfiguration Clone()
9095

9196
public void SetDefaultCloneOptions()
9297
{
93-
RemoveAttributeDisabledSubmeshes = true;
98+
// RemoveAttributeDisabledSubmeshes = true;
9499
SkipHiddenBgParts = true;
95100
UseDeformer = true;
101+
ApplyVisibilityFlags = true;
96102
}
97103

98104
public void Apply(ExportConfiguration other)
@@ -102,7 +108,8 @@ public void Apply(ExportConfiguration other)
102108
// ExportPose = other.ExportPose;
103109
// TextureMode = other.TextureMode;
104110
PoseMode = other.PoseMode;
105-
RemoveAttributeDisabledSubmeshes = other.RemoveAttributeDisabledSubmeshes;
111+
// RemoveAttributeDisabledSubmeshes = other.RemoveAttributeDisabledSubmeshes;
112+
ApplyVisibilityFlags = other.ApplyVisibilityFlags;
106113
SkipHiddenBgParts = other.SkipHiddenBgParts;
107114
// RootAttachHandling = other.RootAttachHandling;
108115
UseDeformer = other.UseDeformer;

Meddle/Meddle.Plugin/Models/Composer/CharacterComposer.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ private void HandleModel(ParsedCharacterInfo characterInfo, ParsedModelInfo m, S
4747
return;
4848
}
4949

50+
if (!m.Enabled && exportConfig.ApplyVisibilityFlags)
51+
{
52+
Plugin.Logger.LogDebug("Skipping disabled model {ModelPath}", m.Path.GamePath);
53+
return;
54+
}
55+
5056
var mdlFile = composerCache.GetMdlFile(m.Path.FullPath);
5157
Plugin.Logger.LogInformation("Loaded model {modelPath}", m.Path.FullPath);
5258
var materialBuilders = new MaterialBuilder[m.Materials.Length];
@@ -122,13 +128,13 @@ private void HandleModel(ParsedCharacterInfo characterInfo, ParsedModelInfo m, S
122128

123129
var enabledAttributes = Model.GetEnabledValues(model.EnabledAttributeMask, model.AttributeMasks).ToArray();
124130
var meshes = ModelBuilder.BuildMeshes(model, materialBuilders, skinningContext.Bones, deform, exportConfig.CreateMeshBuilderOptions());
125-
foreach (var mesh in meshes)
131+
foreach (var meshExport in meshes)
126132
{
127133
var extrasDict = new Dictionary<string, string>
128134
{
129135
{"modelGamePath", m.Path.GamePath},
130136
{"modelFullPath", m.Path.FullPath},
131-
{"meshShapes", mesh.Shapes != null ? string.Join(",", mesh.Shapes) : ""},
137+
{"meshShapes", meshExport.Shapes != null ? string.Join(",", meshExport.Shapes) : ""},
132138
{"modelEnabledAttributes", string.Join(",", enabledAttributes)},
133139
{"modelAttributes", string.Join(",", model.AttributeMasks.Select(x => x.name))},
134140
{"nodeType", "CharacterMesh"}
@@ -150,7 +156,7 @@ private void HandleModel(ParsedCharacterInfo characterInfo, ParsedModelInfo m, S
150156
}
151157

152158
// for shape keys to work, mesh.Mesh.Extras already has an object with a field 'targetNames' which names all the shape keys. need to preserve that.
153-
var extras = mesh.Mesh.Extras?.AsObject();
159+
var extras = meshExport.Mesh.Extras?.AsObject();
154160
if (extras != null)
155161
{
156162
foreach (var kvp in extrasDict)
@@ -161,38 +167,38 @@ private void HandleModel(ParsedCharacterInfo characterInfo, ParsedModelInfo m, S
161167
}
162168
}
163169

164-
mesh.Mesh.Extras = extras;
170+
meshExport.Mesh.Extras = extras;
165171
}
166172
else
167173
{
168-
mesh.Mesh.Extras = JsonNode.Parse(JsonSerializer.Serialize(extrasDict, MaterialComposer.JsonOptions));
174+
meshExport.Mesh.Extras = JsonNode.Parse(JsonSerializer.Serialize(extrasDict, MaterialComposer.JsonOptions));
169175
}
170176

171177
InstanceBuilder instance;
172178
if (skinningContext.Bones.Count > 0)
173179
{
174-
instance = scene.AddSkinnedMesh(mesh.Mesh, skinningContext.Transform, skinningContext.Bones.Cast<NodeBuilder>().ToArray());
180+
instance = scene.AddSkinnedMesh(meshExport.Mesh, skinningContext.Transform, skinningContext.Bones.Cast<NodeBuilder>().ToArray());
175181
}
176182
else
177183
{
178-
instance = scene.AddRigidMesh(mesh.Mesh, skinningContext.Transform);
184+
instance = scene.AddRigidMesh(meshExport.Mesh, skinningContext.Transform);
179185
}
180186

181-
if (model.Shapes.Count != 0 && mesh.Shapes != null)
187+
if (model.Shapes.Count != 0 && meshExport.Shapes != null)
182188
{
183189
// This will set the morphing value to 1 if the shape is enabled, 0 if not
184190
var enabledShapes = Model.GetEnabledValues(model.EnabledShapeMask, model.ShapeMasks)
185191
.ToArray();
186192
var shapes = model.Shapes
187-
.Where(x => mesh.Shapes.Contains(x.Name))
193+
.Where(x => meshExport.Shapes.Contains(x.Name))
188194
.Select(x => (x, enabledShapes.Contains(x.Name)));
189195
instance.Content.UseMorphing().SetValue(shapes.Select(x => x.Item2 ? 1f : 0).ToArray());
190196
}
191197

192-
if (mesh.Submesh != null)
198+
if (meshExport.Submesh != null && exportConfig.ApplyVisibilityFlags)
193199
{
194200
// Remove subMeshes that are not enabled
195-
if (!mesh.Submesh.Attributes.All(enabledAttributes.Contains) && exportConfig.RemoveAttributeDisabledSubmeshes)
201+
if (!meshExport.Submesh.Attributes.All(x => enabledAttributes.Contains(x)))
196202
{
197203
instance.Remove();
198204
}

Meddle/Meddle.Plugin/Models/Layout/ParsedInstance.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,10 @@ public string GetHash()
481481
}
482482
}
483483

484-
public class ParsedModelInfo(string path, string pathFromCharacter, DeformerCachedStruct? deformer, Model.ShapeAttributeGroup? shapeAttributeGroup, ParsedMaterialInfo?[] materials, Stain? stain0, Stain? stain1)
484+
public class ParsedModelInfo(string path, string pathFromCharacter, bool enabled, DeformerCachedStruct? deformer, Model.ShapeAttributeGroup? shapeAttributeGroup, ParsedMaterialInfo?[] materials, Stain? stain0, Stain? stain1)
485485
{
486486
public HandleString Path { get; } = new() { FullPath = path, GamePath = pathFromCharacter };
487+
public bool Enabled { get; } = enabled;
487488
public ParsedStain? Stain0 { get; } = stain0;
488489
public ParsedStain? Stain1 { get; } = stain1;
489490
public DeformerCachedStruct? Deformer { get; } = deformer;

Meddle/Meddle.Plugin/Models/Structs/ModelResourceHandle.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ public unsafe struct MeddleModelResourceHandle
1313
public ModelResourceHandleData GetData() => new(Base->ModelData);
1414
}
1515

16+
[StructLayout(LayoutKind.Explicit, Size = 0x158)]
17+
public unsafe struct MeddleModel
18+
{
19+
[FieldOffset(0x0)] public FFXIVClientStructs.FFXIV.Client.Graphics.Render.Model* Base;
20+
[FieldOffset(0x28)] public uint Flags; // bit 0: body visible, bit 1: attributes dirty, bit 2: shapes dirty
21+
22+
public bool BodyVisible => (Flags & 1) != 0;
23+
}
24+
1625

1726
public readonly struct ModelResourceHandleData
1827
{

Meddle/Meddle.Plugin/Services/ResolverService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ private void ResolveInstance(ParsedInstance instance)
189189
materials.Add(materialInfo);
190190
}
191191

192-
var modelInfo = new ParsedModelInfo(path, path, null, null, materials.ToArray(), null, null);
192+
var modelInfo = new ParsedModelInfo(path, path, true, null, null, materials.ToArray(), null, null);
193193
return modelInfo;
194194
}
195195

Meddle/Meddle.Plugin/UI/LiveCharacterTab.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using Meddle.Plugin.Models;
1515
using Meddle.Plugin.Models.Composer;
1616
using Meddle.Plugin.Models.Layout;
17+
using Meddle.Plugin.Models.Structs;
1718
using Meddle.Plugin.Services;
1819
using Meddle.Plugin.UI.Layout;
1920
using Meddle.Plugin.UI.Windows;
@@ -493,6 +494,11 @@ private void DrawModel(Pointer<CharacterBase> cPtr, Pointer<CSModel> mPtr)
493494
header += $"[{(HumanModelSlotIndex)model->SlotIndex}]";
494495
}
495496
header += $" {modelName}";
497+
bool modelEnabled = ((MeddleModel*)model)->BodyVisible;
498+
if (!modelEnabled)
499+
{
500+
header += " (Hidden)";
501+
}
496502
if (ImGui.CollapsingHeader(header))
497503
{
498504
UiUtil.Text($"Game File Name: {modelName}", modelName);
@@ -536,7 +542,8 @@ private void DrawModel(Pointer<CharacterBase> cPtr, Pointer<CSModel> mPtr)
536542
{
537543
ImGui.Text("No deformer info found");
538544
}
539-
545+
ImGui.Text($"Model Enabled: {modelEnabled}");
546+
540547
var modelShapeAttributes = StructExtensions.ParseModelShapeAttributes(model);
541548
DrawShapeAttributeTable(modelShapeAttributes);
542549

Meddle/Meddle.Plugin/Utils/ParseMaterialUtil.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ public static class ParseMaterialUtil
134134
var modelInfo = new ParsedModelInfo(
135135
model->ModelResourceHandle->FileName.ParseString(),
136136
characterBase->ResolveMdlPath(model->SlotIndex),
137+
((MeddleModel*)model)->BodyVisible,
137138
deform,
138139
StructExtensions.ParseModelShapeAttributes(model),
139140
materials.ToArray(),

Meddle/Meddle.Plugin/Utils/UIUtil.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,16 +172,27 @@ public static bool DrawExportConfig(Configuration.ExportConfiguration exportConf
172172

173173
if (flags.HasFlag(ExportConfigDrawFlags.ShowSubmeshOptions))
174174
{
175-
var removeAttributeDisabledSubmeshes = exportConfiguration.RemoveAttributeDisabledSubmeshes;
176-
if (ImGui.Checkbox("Remove unused character features", ref removeAttributeDisabledSubmeshes))
175+
// var removeAttributeDisabledSubmeshes = exportConfiguration.RemoveAttributeDisabledSubmeshes;
176+
// if (ImGui.Checkbox("Remove unused character features", ref removeAttributeDisabledSubmeshes))
177+
// {
178+
// exportConfiguration.RemoveAttributeDisabledSubmeshes = removeAttributeDisabledSubmeshes;
179+
// changed = true;
180+
// }
181+
//
182+
// ImGui.SameLine();
183+
// HintCircle("Certain character features can be toggled on and off in the character creator,\n" +
184+
// "this will make sure the export only includes the features that are currently enabled.");
185+
186+
var applyEqpFlags = exportConfiguration.ApplyVisibilityFlags;
187+
if (ImGui.Checkbox("Apply Visibility Flags", ref applyEqpFlags))
177188
{
178-
exportConfiguration.RemoveAttributeDisabledSubmeshes = removeAttributeDisabledSubmeshes;
189+
exportConfiguration.ApplyVisibilityFlags = applyEqpFlags;
179190
changed = true;
180191
}
181-
192+
182193
ImGui.SameLine();
183-
HintCircle("Certain character features can be toggled on and off in the character creator,\n" +
184-
"this will make sure the export only includes the features that are currently enabled.");
194+
HintCircle("If enabled, the export will apply visibility flags based on EQP manipulations and certain in-game toggles (such as additional character features)\n" +
195+
"This will make sure the export only includes the models/submeshes that are currently visible on the character.");
185196
}
186197

187198
if (flags.HasFlag(ExportConfigDrawFlags.ShowTerrainOptions))

0 commit comments

Comments
 (0)