mirror of
https://github.com/4sval/FModel.git
synced 2026-04-20 00:27:43 -05:00
proper texture swizzling
This commit is contained in:
parent
ec2e25153e
commit
412c5dd786
|
|
@ -32,6 +32,7 @@ public class Options
|
|||
public readonly Dictionary<string, Texture> Icons;
|
||||
|
||||
private readonly ETexturePlatform _platform;
|
||||
private readonly string _game;
|
||||
|
||||
public Options()
|
||||
{
|
||||
|
|
@ -60,6 +61,7 @@ public class Options
|
|||
};
|
||||
|
||||
_platform = UserSettings.Default.OverridedPlatform;
|
||||
_game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.GameName.ToUpper();
|
||||
|
||||
SelectModel(Guid.Empty);
|
||||
}
|
||||
|
|
@ -181,9 +183,9 @@ public class Options
|
|||
if (!Textures.TryGetValue(guid, out texture) && o.GetMipByMaxSize(UserSettings.Default.PreviewMaxTextureSize) is { } mip)
|
||||
{
|
||||
TextureDecoder.DecodeTexture(mip, o.Format, o.isNormalMap, _platform, out var data, out _);
|
||||
if (fix) TextureHelper.FixChannels(o, mip, ref data);
|
||||
|
||||
texture = new Texture(data, mip.SizeX, mip.SizeY, o);
|
||||
if (fix) TextureHelper.FixChannels(_game, texture);
|
||||
Textures[guid] = texture;
|
||||
}
|
||||
return texture != null;
|
||||
|
|
|
|||
|
|
@ -337,74 +337,7 @@ public class Material : IDisposable
|
|||
return ImGui.IsItemHovered() && ImGui.IsMouseDoubleClicked(ImGuiMouseButton.Left);
|
||||
}
|
||||
|
||||
private Vector3 _scrolling = new (0.0f, 0.0f, 1.0f);
|
||||
public void ImGuiTextureInspector(Texture fallback)
|
||||
{
|
||||
var texture = GetSelectedTexture() ?? fallback;
|
||||
if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp))
|
||||
{
|
||||
SnimGui.NoFramePaddingOnY(() =>
|
||||
{
|
||||
SnimGui.Layout("Type");ImGui.Text($" : ({texture.Format}) {texture.Name}");
|
||||
SnimGui.TooltipCopy("(?) Click to Copy Path", texture.Path);
|
||||
SnimGui.Layout("Guid");ImGui.Text($" : {texture.Guid.ToString(EGuidFormats.UniqueObjectGuid)}");
|
||||
SnimGui.Layout("Import");ImGui.Text($" : {texture.ImportedWidth}x{texture.ImportedHeight}");
|
||||
SnimGui.Layout("Export");ImGui.Text($" : {texture.Width}x{texture.Height}");
|
||||
ImGui.EndTable();
|
||||
});
|
||||
}
|
||||
|
||||
var io = ImGui.GetIO();
|
||||
var canvasP0 = ImGui.GetCursorScreenPos();
|
||||
var canvasSize = ImGui.GetContentRegionAvail();
|
||||
if (canvasSize.X < 50.0f) canvasSize.X = 50.0f;
|
||||
if (canvasSize.Y < 50.0f) canvasSize.Y = 50.0f;
|
||||
var canvasP1 = canvasP0 + canvasSize;
|
||||
var origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
|
||||
var absoluteMiddle = canvasSize / 2.0f;
|
||||
|
||||
ImGui.InvisibleButton("texture_inspector_canvas", canvasSize, ImGuiButtonFlags.MouseButtonLeft);
|
||||
if (ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
|
||||
{
|
||||
_scrolling.X += io.MouseDelta.X;
|
||||
_scrolling.Y += io.MouseDelta.Y;
|
||||
}
|
||||
else if (ImGui.IsItemHovered() && io.MouseWheel != 0.0f)
|
||||
{
|
||||
var zoomFactor = 1.0f + io.MouseWheel * 0.1f;
|
||||
var mousePosCanvas = io.MousePos - origin;
|
||||
|
||||
_scrolling.X -= (mousePosCanvas.X - absoluteMiddle.X) * (zoomFactor - 1);
|
||||
_scrolling.Y -= (mousePosCanvas.Y - absoluteMiddle.Y) * (zoomFactor - 1);
|
||||
_scrolling.Z *= zoomFactor;
|
||||
origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
|
||||
}
|
||||
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
drawList.AddRectFilled(canvasP0, canvasP1, 0xFF242424);
|
||||
drawList.PushClipRect(canvasP0, canvasP1, true);
|
||||
{
|
||||
var sensitivity = _scrolling.Z * 25.0f;
|
||||
for (float x = _scrolling.X % sensitivity; x < canvasSize.X; x += sensitivity)
|
||||
drawList.AddLine(canvasP0 with { X = canvasP0.X + x }, canvasP1 with { X = canvasP0.X + x }, 0x28C8C8C8);
|
||||
for (float y = _scrolling.Y % sensitivity; y < canvasSize.Y; y += sensitivity)
|
||||
drawList.AddLine(canvasP0 with { Y = canvasP0.Y + y }, canvasP1 with { Y = canvasP0.Y + y }, 0x28C8C8C8);
|
||||
}
|
||||
drawList.PopClipRect();
|
||||
|
||||
drawList.PushClipRect(canvasP0, canvasP1, true);
|
||||
{
|
||||
var relativeMiddle = origin + absoluteMiddle;
|
||||
var ratio = Math.Min(canvasSize.X / texture.Width, canvasSize.Y / texture.Height) * 0.95f * _scrolling.Z;
|
||||
var size = new Vector2(texture.Width, texture.Height) * ratio / 2f;
|
||||
|
||||
drawList.AddImage(texture.GetPointer(), relativeMiddle - size, relativeMiddle + size);
|
||||
drawList.AddRect(relativeMiddle - size, relativeMiddle + size, 0xFFFFFFFF);
|
||||
}
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
|
||||
private Texture GetSelectedTexture()
|
||||
public Texture GetSelectedTexture()
|
||||
{
|
||||
return SelectedTexture switch
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Windows;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using ImGuiNET;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
|
@ -25,6 +27,17 @@ public class Texture : IDisposable
|
|||
public int Width;
|
||||
public int Height;
|
||||
|
||||
private const int DisabledChannel = (int)BlendingFactor.Zero;
|
||||
private readonly bool[] _values = { true, true, true, true };
|
||||
private readonly string[] _labels = { "R", "G", "B", "A" };
|
||||
public int[] SwizzleMask =
|
||||
{
|
||||
(int) PixelFormat.Red,
|
||||
(int) PixelFormat.Green,
|
||||
(int) PixelFormat.Blue,
|
||||
(int) PixelFormat.Alpha
|
||||
};
|
||||
|
||||
public Texture(TextureType type)
|
||||
{
|
||||
_handle = GL.GenTexture();
|
||||
|
|
@ -58,7 +71,7 @@ public class Texture : IDisposable
|
|||
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, IntPtr.Zero);
|
||||
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
|
||||
|
||||
|
|
@ -80,7 +93,7 @@ public class Texture : IDisposable
|
|||
|
||||
GL.TexImage2D(_target, 0, texture2D.SRGB ? PixelInternalFormat.Srgb : PixelInternalFormat.Rgb, Width, Height, 0, PixelFormat.Rgba, PixelType.UnsignedByte, data);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureBaseLevel, 0);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8);
|
||||
|
||||
|
|
@ -97,7 +110,7 @@ public class Texture : IDisposable
|
|||
|
||||
GL.TexImage2D(_target, 0, PixelInternalFormat.Rgba, Width, Height, 0, PixelFormat.Rgba, PixelType.Float, ref color);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureBaseLevel, 0);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMaxLevel, 8);
|
||||
|
||||
|
|
@ -114,7 +127,7 @@ public class Texture : IDisposable
|
|||
}
|
||||
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.LinearMipmapLinear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
|
||||
|
|
@ -129,7 +142,7 @@ public class Texture : IDisposable
|
|||
ProcessPixels(texture, _target);
|
||||
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMinFilter, (int) TextureMinFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMinFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureMagFilter, (int) TextureMagFilter.Linear);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapR, (int) TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapS, (int) TextureWrapMode.ClampToEdge);
|
||||
GL.TexParameter(_target, TextureParameterName.TextureWrapT, (int) TextureWrapMode.ClampToEdge);
|
||||
|
|
@ -167,6 +180,12 @@ public class Texture : IDisposable
|
|||
GL.BindTexture(_target, _handle);
|
||||
}
|
||||
|
||||
public void Swizzle()
|
||||
{
|
||||
Bind();
|
||||
GL.TexParameter(_target, TextureParameterName.TextureSwizzleRgba, SwizzleMask);
|
||||
}
|
||||
|
||||
public IntPtr GetPointer() => (IntPtr) _handle;
|
||||
|
||||
public void WindowResized(int width, int height)
|
||||
|
|
@ -194,6 +213,84 @@ public class Texture : IDisposable
|
|||
{
|
||||
GL.DeleteTexture(_handle);
|
||||
}
|
||||
|
||||
private Vector3 _scrolling = new (0.0f, 0.0f, 1.0f);
|
||||
public void ImGuiTextureInspector()
|
||||
{
|
||||
if (ImGui.BeginTable("texture_inspector", 2, ImGuiTableFlags.SizingStretchProp))
|
||||
{
|
||||
SnimGui.NoFramePaddingOnY(() =>
|
||||
{
|
||||
SnimGui.Layout("Type");ImGui.Text($" : ({Format}) {Name}");
|
||||
SnimGui.TooltipCopy("(?) Click to Copy Path", Path);
|
||||
SnimGui.Layout("Guid");ImGui.Text($" : {Guid.ToString(EGuidFormats.UniqueObjectGuid)}");
|
||||
SnimGui.Layout("Import");ImGui.Text($" : {ImportedWidth}x{ImportedHeight}");
|
||||
SnimGui.Layout("Export");ImGui.Text($" : {Width}x{Height}");
|
||||
|
||||
SnimGui.Layout("Swizzle");
|
||||
for (int c = 0; c < SwizzleMask.Length; c++)
|
||||
{
|
||||
if (ImGui.Checkbox(_labels[c], ref _values[c]))
|
||||
{
|
||||
Bind();
|
||||
GL.TexParameter(_target, TextureParameterName.TextureSwizzleR + c, _values[c] ? SwizzleMask[c] : DisabledChannel);
|
||||
}
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
||||
ImGui.EndTable();
|
||||
});
|
||||
}
|
||||
|
||||
var io = ImGui.GetIO();
|
||||
var canvasP0 = ImGui.GetCursorScreenPos();
|
||||
var canvasSize = ImGui.GetContentRegionAvail();
|
||||
if (canvasSize.X < 50.0f) canvasSize.X = 50.0f;
|
||||
if (canvasSize.Y < 50.0f) canvasSize.Y = 50.0f;
|
||||
var canvasP1 = canvasP0 + canvasSize;
|
||||
var origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
|
||||
var absoluteMiddle = canvasSize / 2.0f;
|
||||
|
||||
ImGui.InvisibleButton("texture_inspector_canvas", canvasSize, ImGuiButtonFlags.MouseButtonLeft);
|
||||
if (ImGui.IsItemActive() && ImGui.IsMouseDragging(ImGuiMouseButton.Left))
|
||||
{
|
||||
_scrolling.X += io.MouseDelta.X;
|
||||
_scrolling.Y += io.MouseDelta.Y;
|
||||
}
|
||||
else if (ImGui.IsItemHovered() && io.MouseWheel != 0.0f)
|
||||
{
|
||||
var zoomFactor = 1.0f + io.MouseWheel * 0.1f;
|
||||
var mousePosCanvas = io.MousePos - origin;
|
||||
|
||||
_scrolling.X -= (mousePosCanvas.X - absoluteMiddle.X) * (zoomFactor - 1);
|
||||
_scrolling.Y -= (mousePosCanvas.Y - absoluteMiddle.Y) * (zoomFactor - 1);
|
||||
_scrolling.Z *= zoomFactor;
|
||||
origin = new Vector2(canvasP0.X + _scrolling.X, canvasP0.Y + _scrolling.Y);
|
||||
}
|
||||
|
||||
var drawList = ImGui.GetWindowDrawList();
|
||||
drawList.AddRectFilled(canvasP0, canvasP1, 0xFF242424);
|
||||
drawList.PushClipRect(canvasP0, canvasP1, true);
|
||||
{
|
||||
var sensitivity = _scrolling.Z * 25.0f;
|
||||
for (float x = _scrolling.X % sensitivity; x < canvasSize.X; x += sensitivity)
|
||||
drawList.AddLine(canvasP0 with { X = canvasP0.X + x }, canvasP1 with { X = canvasP0.X + x }, 0x28C8C8C8);
|
||||
for (float y = _scrolling.Y % sensitivity; y < canvasSize.Y; y += sensitivity)
|
||||
drawList.AddLine(canvasP0 with { Y = canvasP0.Y + y }, canvasP1 with { Y = canvasP0.Y + y }, 0x28C8C8C8);
|
||||
}
|
||||
drawList.PopClipRect();
|
||||
|
||||
drawList.PushClipRect(canvasP0, canvasP1, true);
|
||||
{
|
||||
var relativeMiddle = origin + absoluteMiddle;
|
||||
var ratio = Math.Min(canvasSize.X / Width, canvasSize.Y / Height) * 0.95f * _scrolling.Z;
|
||||
var size = new Vector2(Width, Height) * ratio / 2f;
|
||||
|
||||
drawList.AddImage(GetPointer(), relativeMiddle - size, relativeMiddle + size);
|
||||
drawList.AddRect(relativeMiddle - size, relativeMiddle + size, 0xFFFFFFFF);
|
||||
}
|
||||
drawList.PopClipRect();
|
||||
}
|
||||
}
|
||||
|
||||
public enum TextureType
|
||||
|
|
|
|||
|
|
@ -1,84 +1,65 @@
|
|||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
|
||||
namespace FModel.Views.Snooper.Shading;
|
||||
|
||||
public static class TextureHelper
|
||||
{
|
||||
private static readonly string _game = Services.ApplicationService.ApplicationView.CUE4Parse.Provider.GameName;
|
||||
|
||||
/// <summary>
|
||||
/// Red : Specular (not used anymore)
|
||||
/// Green : Metallic
|
||||
/// Blue : Roughness
|
||||
/// </summary>
|
||||
public static void FixChannels(UTexture2D o, FTexture2DMipMap mip, ref byte[] data)
|
||||
public static void FixChannels(string game, Texture texture)
|
||||
{
|
||||
// only if it makes a big difference pls
|
||||
switch (_game)
|
||||
switch (game)
|
||||
{
|
||||
// R: Whatever (AO / S / E / ...)
|
||||
// G: Roughness
|
||||
// B: Metallic
|
||||
case "hk_project":
|
||||
case "cosmicshake":
|
||||
case "phoenix":
|
||||
case "HK_PROJECT":
|
||||
case "COSMICSHAKE":
|
||||
case "PHOENIX":
|
||||
{
|
||||
unsafe
|
||||
texture.SwizzleMask = new []
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
{
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
(d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // RBG
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
(int) PixelFormat.Red,
|
||||
(int) PixelFormat.Blue,
|
||||
(int) PixelFormat.Green,
|
||||
(int) PixelFormat.Alpha
|
||||
};
|
||||
break;
|
||||
}
|
||||
// R: Metallic
|
||||
// G: Roughness
|
||||
// B: Whatever (AO / S / E / ...)
|
||||
case "shootergame":
|
||||
case "divineknockout":
|
||||
case "moonman":
|
||||
case "SHOOTERGAME":
|
||||
case "DIVINEKNOCKOUT":
|
||||
case "MOONMAN":
|
||||
{
|
||||
unsafe
|
||||
texture.SwizzleMask = new []
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
{
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
(d[offset], d[offset + 1]) = (d[offset + 1], d[offset]); // GRB
|
||||
(d[offset], d[offset + 2]) = (d[offset + 2], d[offset]); // RBG
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
(int) PixelFormat.Blue,
|
||||
(int) PixelFormat.Red,
|
||||
(int) PixelFormat.Green,
|
||||
(int) PixelFormat.Alpha
|
||||
};
|
||||
break;
|
||||
}
|
||||
// R: Roughness
|
||||
// G: Metallic
|
||||
// B: Whatever (AO / S / E / ...)
|
||||
case "ccff7r":
|
||||
case "CCFF7R":
|
||||
{
|
||||
unsafe
|
||||
texture.SwizzleMask = new []
|
||||
{
|
||||
var offset = 0;
|
||||
fixed (byte* d = data)
|
||||
{
|
||||
for (var i = 0; i < mip.SizeX * mip.SizeY; i++)
|
||||
{
|
||||
(d[offset + 1], d[offset + 2]) = (d[offset + 2], d[offset + 1]); // RBG
|
||||
(d[offset], d[offset + 1]) = (d[offset + 1], d[offset]); // BRG
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
(int) PixelFormat.Blue,
|
||||
(int) PixelFormat.Green,
|
||||
(int) PixelFormat.Red,
|
||||
(int) PixelFormat.Alpha
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
texture.Swizzle();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -701,7 +701,7 @@ Snooper aims to give an accurate preview of models, materials, skeletal animatio
|
|||
s.Renderer.Options.TryGetModel(out var model) &&
|
||||
s.Renderer.Options.TryGetSection(model, out var section))
|
||||
{
|
||||
model.Materials[section.MaterialIndex].ImGuiTextureInspector(s.Renderer.Options.Icons["noimage"]);
|
||||
(model.Materials[section.MaterialIndex].GetSelectedTexture() ?? s.Renderer.Options.Icons["noimage"]).ImGuiTextureInspector();
|
||||
}
|
||||
ImGui.End(); // if window is collapsed
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user