mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Merge remote-tracking branch 'Exter-N/material-editor-adjustments'
This commit is contained in:
commit
052811049e
6 changed files with 87 additions and 46 deletions
|
|
@ -5,6 +5,7 @@ using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||||
using Penumbra.GameData.Files;
|
using Penumbra.GameData.Files;
|
||||||
|
using Penumbra.Interop.SafeHandles;
|
||||||
|
|
||||||
namespace Penumbra.Interop.MaterialPreview;
|
namespace Penumbra.Interop.MaterialPreview;
|
||||||
|
|
||||||
|
|
@ -16,8 +17,8 @@ public sealed unsafe class LiveColorSetPreviewer : LiveMaterialPreviewerBase
|
||||||
|
|
||||||
private readonly Framework _framework;
|
private readonly Framework _framework;
|
||||||
|
|
||||||
private readonly Texture** _colorSetTexture;
|
private readonly Texture** _colorSetTexture;
|
||||||
private readonly Texture* _originalColorSetTexture;
|
private readonly SafeTextureHandle _originalColorSetTexture;
|
||||||
|
|
||||||
private Half[] _colorSet;
|
private Half[] _colorSet;
|
||||||
private bool _updatePending;
|
private bool _updatePending;
|
||||||
|
|
@ -40,12 +41,10 @@ public sealed unsafe class LiveColorSetPreviewer : LiveMaterialPreviewerBase
|
||||||
|
|
||||||
_colorSetTexture = colorSetTextures + (MaterialInfo.ModelSlot * 4 + MaterialInfo.MaterialSlot);
|
_colorSetTexture = colorSetTextures + (MaterialInfo.ModelSlot * 4 + MaterialInfo.MaterialSlot);
|
||||||
|
|
||||||
_originalColorSetTexture = *_colorSetTexture;
|
_originalColorSetTexture = new SafeTextureHandle(*_colorSetTexture, true);
|
||||||
if (_originalColorSetTexture == null)
|
if (_originalColorSetTexture == null)
|
||||||
throw new InvalidOperationException("Material doesn't have a color set");
|
throw new InvalidOperationException("Material doesn't have a color set");
|
||||||
|
|
||||||
Structs.TextureUtility.IncRef(_originalColorSetTexture);
|
|
||||||
|
|
||||||
_colorSet = new Half[TextureLength];
|
_colorSet = new Half[TextureLength];
|
||||||
_updatePending = true;
|
_updatePending = true;
|
||||||
|
|
||||||
|
|
@ -59,15 +58,9 @@ public sealed unsafe class LiveColorSetPreviewer : LiveMaterialPreviewerBase
|
||||||
base.Clear(disposing, reset);
|
base.Clear(disposing, reset);
|
||||||
|
|
||||||
if (reset)
|
if (reset)
|
||||||
{
|
_originalColorSetTexture.Exchange(ref *(nint*)_colorSetTexture);
|
||||||
var oldTexture = (Texture*)Interlocked.Exchange(ref *(nint*)_colorSetTexture, (nint)_originalColorSetTexture);
|
|
||||||
if (oldTexture != null)
|
_originalColorSetTexture.Dispose();
|
||||||
Structs.TextureUtility.DecRef(oldTexture);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Structs.TextureUtility.DecRef(_originalColorSetTexture);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScheduleUpdate()
|
public void ScheduleUpdate()
|
||||||
|
|
@ -89,8 +82,8 @@ public sealed unsafe class LiveColorSetPreviewer : LiveMaterialPreviewerBase
|
||||||
textureSize[0] = TextureWidth;
|
textureSize[0] = TextureWidth;
|
||||||
textureSize[1] = TextureHeight;
|
textureSize[1] = TextureHeight;
|
||||||
|
|
||||||
var newTexture = Structs.TextureUtility.Create2D(Device.Instance(), textureSize, 1, 0x2460, 0x80000804, 7);
|
using var texture = new SafeTextureHandle(Structs.TextureUtility.Create2D(Device.Instance(), textureSize, 1, 0x2460, 0x80000804, 7), false);
|
||||||
if (newTexture == null)
|
if (texture.IsInvalid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool success;
|
bool success;
|
||||||
|
|
@ -98,20 +91,12 @@ public sealed unsafe class LiveColorSetPreviewer : LiveMaterialPreviewerBase
|
||||||
{
|
{
|
||||||
fixed (Half* colorSet = _colorSet)
|
fixed (Half* colorSet = _colorSet)
|
||||||
{
|
{
|
||||||
success = Structs.TextureUtility.InitializeContents(newTexture, colorSet);
|
success = Structs.TextureUtility.InitializeContents(texture.Texture, colorSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
texture.Exchange(ref *(nint*)_colorSetTexture);
|
||||||
var oldTexture = (Texture*)Interlocked.Exchange(ref *(nint*)_colorSetTexture, (nint)newTexture);
|
|
||||||
if (oldTexture != null)
|
|
||||||
Structs.TextureUtility.DecRef(oldTexture);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Structs.TextureUtility.DecRef(newTexture);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool IsStillValid()
|
protected override bool IsStillValid()
|
||||||
|
|
|
||||||
48
Penumbra/Interop/SafeHandles/SafeTextureHandle.cs
Normal file
48
Penumbra/Interop/SafeHandles/SafeTextureHandle.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Render;
|
||||||
|
using Penumbra.Interop.Structs;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop.SafeHandles;
|
||||||
|
|
||||||
|
public unsafe class SafeTextureHandle : SafeHandle
|
||||||
|
{
|
||||||
|
public Texture* Texture => (Texture*)handle;
|
||||||
|
|
||||||
|
public override bool IsInvalid => handle == 0;
|
||||||
|
|
||||||
|
public SafeTextureHandle(Texture* handle, bool incRef, bool ownsHandle = true) : base(0, ownsHandle)
|
||||||
|
{
|
||||||
|
if (incRef && !ownsHandle)
|
||||||
|
throw new ArgumentException("Non-owning SafeTextureHandle with IncRef is unsupported");
|
||||||
|
if (incRef && handle != null)
|
||||||
|
TextureUtility.IncRef(handle);
|
||||||
|
SetHandle((nint)handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Exchange(ref nint ppTexture)
|
||||||
|
{
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
handle = Interlocked.Exchange(ref ppTexture, handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SafeTextureHandle CreateInvalid()
|
||||||
|
=> new(null, incRef: false);
|
||||||
|
|
||||||
|
protected override bool ReleaseHandle()
|
||||||
|
{
|
||||||
|
nint handle;
|
||||||
|
lock (this)
|
||||||
|
{
|
||||||
|
handle = this.handle;
|
||||||
|
this.handle = 0;
|
||||||
|
}
|
||||||
|
if (handle != 0)
|
||||||
|
TextureUtility.DecRef((Texture*)handle);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,7 @@ public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
private interface IConstantEditor
|
private interface IConstantEditor
|
||||||
{
|
{
|
||||||
bool Draw(Span<float> values, bool disabled, float editorWidth);
|
bool Draw(Span<float> values, bool disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class FloatConstantEditor : IConstantEditor
|
private sealed class FloatConstantEditor : IConstantEditor
|
||||||
|
|
@ -42,16 +42,18 @@ public partial class ModEditWindow
|
||||||
_format = $"{_format} {unit.Replace("%", "%%")}";
|
_format = $"{_format} {unit.Replace("%", "%%")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Draw(Span<float> values, bool disabled, float editorWidth)
|
public bool Draw(Span<float> values, bool disabled)
|
||||||
{
|
{
|
||||||
var fieldWidth = (editorWidth - (values.Length - 1) * ImGui.GetStyle().ItemSpacing.X) / values.Length;
|
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
||||||
|
var fieldWidth = (ImGui.CalcItemWidth() - (values.Length - 1) * spacing) / values.Length;
|
||||||
|
|
||||||
var ret = false;
|
var ret = false;
|
||||||
|
|
||||||
|
// Not using DragScalarN because of _relativeSpeed and other points of lost flexibility.
|
||||||
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
|
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
|
||||||
{
|
{
|
||||||
if (valueIdx > 0)
|
if (valueIdx > 0)
|
||||||
ImGui.SameLine();
|
ImGui.SameLine(0.0f, spacing);
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
|
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
|
||||||
|
|
||||||
|
|
@ -101,16 +103,18 @@ public partial class ModEditWindow
|
||||||
_format = $"{_format} {unit.Replace("%", "%%")}";
|
_format = $"{_format} {unit.Replace("%", "%%")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Draw(Span<float> values, bool disabled, float editorWidth)
|
public bool Draw(Span<float> values, bool disabled)
|
||||||
{
|
{
|
||||||
var fieldWidth = (editorWidth - (values.Length - 1) * ImGui.GetStyle().ItemSpacing.X) / values.Length;
|
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
||||||
|
var fieldWidth = (ImGui.CalcItemWidth() - (values.Length - 1) * spacing) / values.Length;
|
||||||
|
|
||||||
var ret = false;
|
var ret = false;
|
||||||
|
|
||||||
|
// Not using DragScalarN because of _relativeSpeed and other points of lost flexibility.
|
||||||
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
|
for (var valueIdx = 0; valueIdx < values.Length; ++valueIdx)
|
||||||
{
|
{
|
||||||
if (valueIdx > 0)
|
if (valueIdx > 0)
|
||||||
ImGui.SameLine();
|
ImGui.SameLine(0.0f, spacing);
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
|
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
|
||||||
|
|
||||||
|
|
@ -148,13 +152,12 @@ public partial class ModEditWindow
|
||||||
_clamped = clamped;
|
_clamped = clamped;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Draw(Span<float> values, bool disabled, float editorWidth)
|
public bool Draw(Span<float> values, bool disabled)
|
||||||
{
|
{
|
||||||
switch (values.Length)
|
switch (values.Length)
|
||||||
{
|
{
|
||||||
case 3:
|
case 3:
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(editorWidth);
|
|
||||||
var value = new Vector3(values);
|
var value = new Vector3(values);
|
||||||
if (_squaredRgb)
|
if (_squaredRgb)
|
||||||
value = PseudoSqrtRgb(value);
|
value = PseudoSqrtRgb(value);
|
||||||
|
|
@ -170,7 +173,6 @@ public partial class ModEditWindow
|
||||||
}
|
}
|
||||||
case 4:
|
case 4:
|
||||||
{
|
{
|
||||||
ImGui.SetNextItemWidth(editorWidth);
|
|
||||||
var value = new Vector4(values);
|
var value = new Vector4(values);
|
||||||
if (_squaredRgb)
|
if (_squaredRgb)
|
||||||
value = PseudoSqrtRgb(value);
|
value = PseudoSqrtRgb(value);
|
||||||
|
|
@ -186,7 +188,7 @@ public partial class ModEditWindow
|
||||||
value.CopyTo(values);
|
value.CopyTo(values);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default: return FloatConstantEditor.Default.Draw(values, disabled, editorWidth);
|
default: return FloatConstantEditor.Default.Draw(values, disabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,9 +200,10 @@ public partial class ModEditWindow
|
||||||
public EnumConstantEditor(IReadOnlyList<(string Label, float Value, string Description)> values)
|
public EnumConstantEditor(IReadOnlyList<(string Label, float Value, string Description)> values)
|
||||||
=> _values = values;
|
=> _values = values;
|
||||||
|
|
||||||
public bool Draw(Span<float> values, bool disabled, float editorWidth)
|
public bool Draw(Span<float> values, bool disabled)
|
||||||
{
|
{
|
||||||
var fieldWidth = (editorWidth - (values.Length - 1) * ImGui.GetStyle().ItemSpacing.X) / values.Length;
|
var spacing = ImGui.GetStyle().ItemInnerSpacing.X;
|
||||||
|
var fieldWidth = (ImGui.CalcItemWidth() - (values.Length - 1) * spacing) / values.Length;
|
||||||
|
|
||||||
var ret = false;
|
var ret = false;
|
||||||
|
|
||||||
|
|
@ -208,7 +211,7 @@ public partial class ModEditWindow
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId(valueIdx);
|
using var id = ImRaii.PushId(valueIdx);
|
||||||
if (valueIdx > 0)
|
if (valueIdx > 0)
|
||||||
ImGui.SameLine();
|
ImGui.SameLine(0.0f, spacing);
|
||||||
|
|
||||||
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
|
ImGui.SetNextItemWidth(MathF.Round(fieldWidth * (valueIdx + 1)) - MathF.Round(fieldWidth * valueIdx));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ using Penumbra.GameData.Structs;
|
||||||
using Penumbra.Interop.MaterialPreview;
|
using Penumbra.Interop.MaterialPreview;
|
||||||
using Penumbra.String;
|
using Penumbra.String;
|
||||||
using Penumbra.String.Classes;
|
using Penumbra.String.Classes;
|
||||||
|
using Penumbra.UI.Classes;
|
||||||
using static Penumbra.GameData.Files.ShpkFile;
|
using static Penumbra.GameData.Files.ShpkFile;
|
||||||
|
|
||||||
namespace Penumbra.UI.AdvancedWindow;
|
namespace Penumbra.UI.AdvancedWindow;
|
||||||
|
|
@ -177,7 +178,7 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
if (mayVary && (data as JObject)?["Vary"] != null)
|
if (mayVary && (data as JObject)?["Vary"] != null)
|
||||||
{
|
{
|
||||||
var selector = BuildSelector(data["Vary"]!
|
var selector = BuildSelector(data!["Vary"]!
|
||||||
.Select(key => (uint)key)
|
.Select(key => (uint)key)
|
||||||
.Select(key => Mtrl.GetShaderKey(key)?.Value ?? AssociatedShpk!.GetMaterialKeyById(key)!.Value.DefaultValue));
|
.Select(key => Mtrl.GetShaderKey(key)?.Value ?? AssociatedShpk!.GetMaterialKeyById(key)!.Value.DefaultValue));
|
||||||
var index = (int)data["Selectors"]![selector.ToString()]!;
|
var index = (int)data["Selectors"]![selector.ToString()]!;
|
||||||
|
|
@ -668,12 +669,13 @@ public partial class ModEditWindow
|
||||||
|
|
||||||
private static void ApplyHighlight(ref MtrlFile.ColorSet.Row row, float time)
|
private static void ApplyHighlight(ref MtrlFile.ColorSet.Row row, float time)
|
||||||
{
|
{
|
||||||
var level = Math.Sin(time * 2.0 * Math.PI) * 0.25 + 0.5;
|
var level = (MathF.Sin(time * 2.0f * MathF.PI) + 2.0f) / 3.0f / 255.0f;
|
||||||
var levelSq = (float)(level * level);
|
var baseColor = ColorId.InGameHighlight.Value();
|
||||||
|
var color = level * new Vector3(baseColor & 0xFF, (baseColor >> 8) & 0xFF, (baseColor >> 16) & 0xFF);
|
||||||
|
|
||||||
row.Diffuse = Vector3.Zero;
|
row.Diffuse = Vector3.Zero;
|
||||||
row.Specular = Vector3.Zero;
|
row.Specular = Vector3.Zero;
|
||||||
row.Emissive = new Vector3(levelSq);
|
row.Emissive = color * color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,8 @@ public partial class ModEditWindow
|
||||||
if (buffer.Length > 0)
|
if (buffer.Length > 0)
|
||||||
{
|
{
|
||||||
using var id = ImRaii.PushId($"##{constant.Id:X8}:{slice.Start}");
|
using var id = ImRaii.PushId($"##{constant.Id:X8}:{slice.Start}");
|
||||||
if (editor.Draw(buffer[slice], disabled, 250.0f))
|
ImGui.SetNextItemWidth(250.0f);
|
||||||
|
if (editor.Draw(buffer[slice], disabled))
|
||||||
{
|
{
|
||||||
ret = true;
|
ret = true;
|
||||||
tab.SetMaterialParameter(constant.Id, slice.Start, buffer[slice]);
|
tab.SetMaterialParameter(constant.Id, slice.Start, buffer[slice]);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ public enum ColorId
|
||||||
RedundantAssignment,
|
RedundantAssignment,
|
||||||
NoModsAssignment,
|
NoModsAssignment,
|
||||||
NoAssignment,
|
NoAssignment,
|
||||||
SelectorPriority,
|
SelectorPriority,
|
||||||
|
InGameHighlight,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Colors
|
public static class Colors
|
||||||
|
|
@ -64,6 +65,7 @@ public static class Colors
|
||||||
ColorId.NoModsAssignment => ( 0x50000080, "'Use No Mods' Collection Assignment", "A collection assignment set to not use any mods at all."),
|
ColorId.NoModsAssignment => ( 0x50000080, "'Use No Mods' Collection Assignment", "A collection assignment set to not use any mods at all."),
|
||||||
ColorId.NoAssignment => ( 0x00000000, "Unassigned Collection Assignment", "A collection assignment that is not configured to any collection and thus just has no specific treatment."),
|
ColorId.NoAssignment => ( 0x00000000, "Unassigned Collection Assignment", "A collection assignment that is not configured to any collection and thus just has no specific treatment."),
|
||||||
ColorId.SelectorPriority => ( 0xFF808080, "Mod Selector Priority", "The priority displayed for non-zero priority mods in the mod selector."),
|
ColorId.SelectorPriority => ( 0xFF808080, "Mod Selector Priority", "The priority displayed for non-zero priority mods in the mod selector."),
|
||||||
|
ColorId.InGameHighlight => ( 0xFFEBCF89, "In-Game Highlight", "An in-game element that has been highlighted for ease of editing."),
|
||||||
_ => throw new ArgumentOutOfRangeException( nameof( color ), color, null ),
|
_ => throw new ArgumentOutOfRangeException( nameof( color ), color, null ),
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue