mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Start texture import stuff.
This commit is contained in:
parent
1d3a31db6f
commit
10f06e2715
5 changed files with 797 additions and 1 deletions
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit a9a5b2a4bbf061d9cfed5234ca731bd2d94bcb96
|
||||
Subproject commit 0bd85ed72057b1941579d20a6f622cc2cd9c58ac
|
||||
446
Penumbra/Import/Textures/TextureImporter.cs
Normal file
446
Penumbra/Import/Textures/TextureImporter.cs
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Logging;
|
||||
using Lumina.Data.Files;
|
||||
using Lumina.Extensions;
|
||||
using System.Drawing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace Penumbra.Import.Textures;
|
||||
|
||||
[StructLayout( LayoutKind.Sequential )]
|
||||
public struct PixelFormat
|
||||
{
|
||||
[Flags]
|
||||
public enum FormatFlags : uint
|
||||
{
|
||||
AlphaPixels = 0x000001,
|
||||
Alpha = 0x000002,
|
||||
FourCC = 0x000004,
|
||||
RGB = 0x000040,
|
||||
YUV = 0x000200,
|
||||
Luminance = 0x020000,
|
||||
}
|
||||
|
||||
public enum FourCCType : uint
|
||||
{
|
||||
DXT1 = 'D' | ( 'X' << 8 ) | ( 'T' << 16 ) | ( '1' << 24 ),
|
||||
DXT3 = 'D' | ( 'X' << 8 ) | ( 'T' << 16 ) | ( '3' << 24 ),
|
||||
DXT5 = 'D' | ( 'X' << 8 ) | ( 'T' << 16 ) | ( '5' << 24 ),
|
||||
DX10 = 'D' | ( 'X' << 8 ) | ( '1' << 16 ) | ( '0' << 24 ),
|
||||
}
|
||||
|
||||
public int Size;
|
||||
public FormatFlags Flags;
|
||||
public FourCCType FourCC;
|
||||
public int RgbBitCount;
|
||||
public int RBitMask;
|
||||
public int GBitMask;
|
||||
public int BBitMask;
|
||||
public int ABitMask;
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Sequential )]
|
||||
public struct DdsHeader
|
||||
{
|
||||
[Flags]
|
||||
public enum DdsFlags : uint
|
||||
{
|
||||
Caps = 0x00000001,
|
||||
Height = 0x00000002,
|
||||
Width = 0x00000004,
|
||||
Pitch = 0x00000008,
|
||||
PixelFormat = 0x00001000,
|
||||
MipMapCount = 0x00020000,
|
||||
LinearSize = 0x00080000,
|
||||
Depth = 0x00800000,
|
||||
|
||||
Required = Caps | Height | Width | PixelFormat,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DdsCaps1 : uint
|
||||
{
|
||||
Complex = 0x08,
|
||||
MipMap = 0x400000,
|
||||
Texture = 0x1000,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum DdsCaps2 : uint
|
||||
{
|
||||
CubeMap = 0x200,
|
||||
CubeMapPositiveEX = 0x400,
|
||||
CubeMapNegativeEX = 0x800,
|
||||
CubeMapPositiveEY = 0x1000,
|
||||
CubeMapNegativeEY = 0x2000,
|
||||
CubeMapPositiveEZ = 0x4000,
|
||||
CubeMapNegativeEZ = 0x8000,
|
||||
Volume = 0x200000,
|
||||
}
|
||||
|
||||
public int Size;
|
||||
public DdsFlags Flags;
|
||||
public int Height;
|
||||
public int Width;
|
||||
public int PitchOrLinearSize;
|
||||
public int Depth;
|
||||
public int MipMapCount;
|
||||
public int Reserved1;
|
||||
public int Reserved2;
|
||||
public int Reserved3;
|
||||
public int Reserved4;
|
||||
public int Reserved5;
|
||||
public int Reserved6;
|
||||
public int Reserved7;
|
||||
public int Reserved8;
|
||||
public int Reserved9;
|
||||
public int ReservedA;
|
||||
public int ReservedB;
|
||||
public PixelFormat PixelFormat;
|
||||
public DdsCaps1 Caps1;
|
||||
public DdsCaps2 Caps2;
|
||||
public uint Caps3;
|
||||
public uint Caps4;
|
||||
public int ReservedC;
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Sequential )]
|
||||
public struct DXT10Header
|
||||
{
|
||||
public enum DXGIFormat : uint
|
||||
{
|
||||
Unknown = 0,
|
||||
R32G32B32A32Typeless = 1,
|
||||
R32G32B32A32Float = 2,
|
||||
R32G32B32A32UInt = 3,
|
||||
R32G32B32A32SInt = 4,
|
||||
R32G32B32Typeless = 5,
|
||||
R32G32B32Float = 6,
|
||||
R32G32B32UInt = 7,
|
||||
R32G32B32SInt = 8,
|
||||
R16G16B16A16Typeless = 9,
|
||||
R16G16B16A16Float = 10,
|
||||
R16G16B16A16UNorm = 11,
|
||||
R16G16B16A16UInt = 12,
|
||||
R16G16B16A16SNorm = 13,
|
||||
R16G16B16A16SInt = 14,
|
||||
R32G32Typeless = 15,
|
||||
R32G32Float = 16,
|
||||
R32G32UInt = 17,
|
||||
R32G32SInt = 18,
|
||||
R32G8X24Typeless = 19,
|
||||
D32FloatS8X24UInt = 20,
|
||||
R32FloatX8X24Typeless = 21,
|
||||
X32TypelessG8X24UInt = 22,
|
||||
R10G10B10A2Typeless = 23,
|
||||
R10G10B10A2UNorm = 24,
|
||||
R10G10B10A2UInt = 25,
|
||||
R11G11B10Float = 26,
|
||||
R8G8B8A8Typeless = 27,
|
||||
R8G8B8A8UNorm = 28,
|
||||
R8G8B8A8UNormSRGB = 29,
|
||||
R8G8B8A8UInt = 30,
|
||||
R8G8B8A8SNorm = 31,
|
||||
R8G8B8A8SInt = 32,
|
||||
R16G16Typeless = 33,
|
||||
R16G16Float = 34,
|
||||
R16G16UNorm = 35,
|
||||
R16G16UInt = 36,
|
||||
R16G16SNorm = 37,
|
||||
R16G16SInt = 38,
|
||||
R32Typeless = 39,
|
||||
D32Float = 40,
|
||||
R32Float = 41,
|
||||
R32UInt = 42,
|
||||
R32SInt = 43,
|
||||
R24G8Typeless = 44,
|
||||
D24UNormS8UInt = 45,
|
||||
R24UNormX8Typeless = 46,
|
||||
X24TypelessG8UInt = 47,
|
||||
R8G8Typeless = 48,
|
||||
R8G8UNorm = 49,
|
||||
R8G8UInt = 50,
|
||||
R8G8SNorm = 51,
|
||||
R8G8SInt = 52,
|
||||
R16Typeless = 53,
|
||||
R16Float = 54,
|
||||
D16UNorm = 55,
|
||||
R16UNorm = 56,
|
||||
R16UInt = 57,
|
||||
R16SNorm = 58,
|
||||
R16SInt = 59,
|
||||
R8Typeless = 60,
|
||||
R8UNorm = 61,
|
||||
R8UInt = 62,
|
||||
R8SNorm = 63,
|
||||
R8SInt = 64,
|
||||
A8UNorm = 65,
|
||||
R1UNorm = 66,
|
||||
R9G9B9E5SharedEXP = 67,
|
||||
R8G8B8G8UNorm = 68,
|
||||
G8R8G8B8UNorm = 69,
|
||||
BC1Typeless = 70,
|
||||
BC1UNorm = 71,
|
||||
BC1UNormSRGB = 72,
|
||||
BC2Typeless = 73,
|
||||
BC2UNorm = 74,
|
||||
BC2UNormSRGB = 75,
|
||||
BC3Typeless = 76,
|
||||
BC3UNorm = 77,
|
||||
BC3UNormSRGB = 78,
|
||||
BC4Typeless = 79,
|
||||
BC4UNorm = 80,
|
||||
BC4SNorm = 81,
|
||||
BC5Typeless = 82,
|
||||
BC5UNorm = 83,
|
||||
BC5SNorm = 84,
|
||||
B5G6R5UNorm = 85,
|
||||
B5G5R5A1UNorm = 86,
|
||||
B8G8R8A8UNorm = 87,
|
||||
B8G8R8X8UNorm = 88,
|
||||
R10G10B10XRBiasA2UNorm = 89,
|
||||
B8G8R8A8Typeless = 90,
|
||||
B8G8R8A8UNormSRGB = 91,
|
||||
B8G8R8X8Typeless = 92,
|
||||
B8G8R8X8UNormSRGB = 93,
|
||||
BC6HTypeless = 94,
|
||||
BC6HUF16 = 95,
|
||||
BC6HSF16 = 96,
|
||||
BC7Typeless = 97,
|
||||
BC7UNorm = 98,
|
||||
BC7UNormSRGB = 99,
|
||||
AYUV = 100,
|
||||
Y410 = 101,
|
||||
Y416 = 102,
|
||||
NV12 = 103,
|
||||
P010 = 104,
|
||||
P016 = 105,
|
||||
F420Opaque = 106,
|
||||
YUY2 = 107,
|
||||
Y210 = 108,
|
||||
Y216 = 109,
|
||||
NV11 = 110,
|
||||
AI44 = 111,
|
||||
IA44 = 112,
|
||||
P8 = 113,
|
||||
A8P8 = 114,
|
||||
B4G4R4A4UNorm = 115,
|
||||
P208 = 130,
|
||||
V208 = 131,
|
||||
V408 = 132,
|
||||
SamplerFeedbackMinMipOpaque,
|
||||
SamplerFeedbackMipRegionUsedOpaque,
|
||||
ForceUInt = 0xffffffff,
|
||||
}
|
||||
|
||||
public enum D3DResourceDimension : int
|
||||
{
|
||||
Unknown = 0,
|
||||
Buffer = 1,
|
||||
Texture1D = 2,
|
||||
Texture2D = 3,
|
||||
Texture3D = 4,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum D3DResourceMiscFlags : uint
|
||||
{
|
||||
GenerateMips = 0x000001,
|
||||
Shared = 0x000002,
|
||||
TextureCube = 0x000004,
|
||||
DrawIndirectArgs = 0x000010,
|
||||
BufferAllowRawViews = 0x000020,
|
||||
BufferStructured = 0x000040,
|
||||
ResourceClamp = 0x000080,
|
||||
SharedKeyedMutex = 0x000100,
|
||||
GDICompatible = 0x000200,
|
||||
SharedNTHandle = 0x000800,
|
||||
RestrictedContent = 0x001000,
|
||||
RestrictSharedResource = 0x002000,
|
||||
RestrictSharedResourceDriver = 0x004000,
|
||||
Guarded = 0x008000,
|
||||
TilePool = 0x020000,
|
||||
Tiled = 0x040000,
|
||||
HWProtected = 0x080000,
|
||||
SharedDisplayable,
|
||||
SharedExclusiveWriter,
|
||||
};
|
||||
|
||||
public enum D3DAlphaMode : int
|
||||
{
|
||||
Unknown = 0,
|
||||
Straight = 1,
|
||||
Premultiplied = 2,
|
||||
Opaque = 3,
|
||||
Custom = 4,
|
||||
};
|
||||
|
||||
public DXGIFormat Format;
|
||||
public D3DResourceDimension ResourceDimension;
|
||||
public D3DResourceMiscFlags MiscFlags;
|
||||
public uint ArraySize;
|
||||
public D3DAlphaMode AlphaMode;
|
||||
}
|
||||
|
||||
public class DdsFile
|
||||
{
|
||||
public const int DdsIdentifier = 0x20534444;
|
||||
|
||||
public DdsHeader Header;
|
||||
public DXT10Header? DXT10Header;
|
||||
public byte[] MainSurfaceData;
|
||||
public byte[] RemainingSurfaceData;
|
||||
|
||||
private DdsFile( DdsHeader header, byte[] mainSurfaceData, byte[] remainingSurfaceData, DXT10Header? dXT10Header = null )
|
||||
{
|
||||
Header = header;
|
||||
DXT10Header = dXT10Header;
|
||||
MainSurfaceData = mainSurfaceData;
|
||||
RemainingSurfaceData = remainingSurfaceData;
|
||||
}
|
||||
|
||||
public static bool Load( Stream data, [NotNullWhen( true )] out DdsFile? file )
|
||||
{
|
||||
file = null;
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader( data );
|
||||
if( br.ReadUInt32() != DdsIdentifier )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var header = br.ReadStructure< DdsHeader >();
|
||||
var dxt10 = header.PixelFormat.FourCC == PixelFormat.FourCCType.DX10 ? ( DXT10Header? )br.ReadStructure< DXT10Header >() : null;
|
||||
|
||||
file = new DdsFile( header, br.ReadBytes( ( int )( br.BaseStream.Length - br.BaseStream.Position ) ), Array.Empty< byte >(),
|
||||
dxt10 );
|
||||
return true;
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not load DDS file:\n{e}" );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ConvertToTex( out byte[] texBytes )
|
||||
{
|
||||
using var mem = new MemoryStream( MainSurfaceData.Length * 2 );
|
||||
using( var bw = new BinaryWriter( mem ) )
|
||||
{
|
||||
var format = WriteTexHeader( bw );
|
||||
bw.Write( ConvertBytes( MainSurfaceData, format ) );
|
||||
}
|
||||
|
||||
texBytes = mem.ToArray();
|
||||
return true;
|
||||
}
|
||||
|
||||
private TexFile.TextureFormat WriteTexHeader( BinaryWriter bw )
|
||||
{
|
||||
var (format, mipLength) = ConvertFormat( Header.PixelFormat, Header.Height, Header.Width, DXT10Header );
|
||||
|
||||
bw.Write( ( uint )TexFile.Attribute.TextureType2D );
|
||||
bw.Write( ( uint )format );
|
||||
bw.Write( ( ushort )Header.Width );
|
||||
bw.Write( ( ushort )Header.Height );
|
||||
bw.Write( ( ushort )Header.Depth );
|
||||
bw.Write( ( ushort )Header.MipMapCount );
|
||||
bw.Write( 0 );
|
||||
bw.Write( 1 );
|
||||
bw.Write( 2 );
|
||||
|
||||
var offset = 80;
|
||||
for( var i = 0; i < Header.MipMapCount; ++i )
|
||||
{
|
||||
bw.Write( offset );
|
||||
offset += mipLength;
|
||||
mipLength = Math.Max( 16, mipLength >> 2 );
|
||||
}
|
||||
|
||||
for( var i = Header.MipMapCount; i < 13; ++i )
|
||||
{
|
||||
bw.Write( 0 );
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
private static byte[] ConvertBytes( byte[] ddsData, TexFile.TextureFormat format )
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
_ => ddsData,
|
||||
};
|
||||
}
|
||||
|
||||
private static (TexFile.TextureFormat, int) ConvertFormat( PixelFormat format, int height, int width, DXT10Header? dxt10 )
|
||||
=> format.FourCC switch
|
||||
{
|
||||
PixelFormat.FourCCType.DXT1 => ( TexFile.TextureFormat.DXT1, height * width / 2 ),
|
||||
PixelFormat.FourCCType.DXT3 => ( TexFile.TextureFormat.DXT3, height * width * 4 ),
|
||||
PixelFormat.FourCCType.DXT5 => ( TexFile.TextureFormat.DXT5, height * width ),
|
||||
PixelFormat.FourCCType.DX10 => dxt10!.Value.Format switch
|
||||
{
|
||||
Textures.DXT10Header.DXGIFormat.A8UNorm => ( TexFile.TextureFormat.A8, height * width ),
|
||||
Textures.DXT10Header.DXGIFormat.R8G8B8A8UInt => ( TexFile.TextureFormat.A8R8G8B8, height * width * 4 ),
|
||||
Textures.DXT10Header.DXGIFormat.R8G8UNorm => ( TexFile.TextureFormat.L8, height * width ),
|
||||
Textures.DXT10Header.DXGIFormat.B8G8R8X8UNorm => ( TexFile.TextureFormat.R8G8B8X8, height * width * 4 ),
|
||||
Textures.DXT10Header.DXGIFormat.B4G4R4A4UNorm => ( TexFile.TextureFormat.R4G4B4A4, height * width * 2 ),
|
||||
Textures.DXT10Header.DXGIFormat.B5G5R5A1UNorm => ( TexFile.TextureFormat.R5G5B5A1, height * width * 2 ),
|
||||
Textures.DXT10Header.DXGIFormat.R32Float => ( TexFile.TextureFormat.R32F, height * width * 4 ),
|
||||
Textures.DXT10Header.DXGIFormat.R32G32B32A32Float => ( TexFile.TextureFormat.R32G32B32A32F, height * width * 16 ),
|
||||
Textures.DXT10Header.DXGIFormat.R16G16Float => ( TexFile.TextureFormat.R16G16F, height * width * 4 ),
|
||||
Textures.DXT10Header.DXGIFormat.R16G16B16A16Float => ( TexFile.TextureFormat.R16G16B16A16F, height * width * 8 ),
|
||||
Textures.DXT10Header.DXGIFormat.D16UNorm => ( TexFile.TextureFormat.D16, height * width * 2 ),
|
||||
Textures.DXT10Header.DXGIFormat.D24UNormS8UInt => ( TexFile.TextureFormat.D24S8, height * width * 4 ),
|
||||
_ => ( TexFile.TextureFormat.A8R8G8B8, height * width * 4 ),
|
||||
},
|
||||
_ => ( TexFile.TextureFormat.A8R8G8B8, height * width * 4 ),
|
||||
};
|
||||
}
|
||||
|
||||
public class TextureImporter
|
||||
{
|
||||
public static bool ReadPng( string inputFile, out byte[] texData )
|
||||
{
|
||||
using var file = File.OpenRead( inputFile );
|
||||
var image = Image.Load< Bgra32 >( file );
|
||||
|
||||
var buffer = new byte[80 + image.Height * image.Width * 4];
|
||||
using( var mem = new MemoryStream( buffer ) )
|
||||
{
|
||||
using( var bw = new BinaryWriter( mem ) )
|
||||
{
|
||||
bw.Write( ( uint )TexFile.Attribute.TextureType2D );
|
||||
bw.Write( ( uint )TexFile.TextureFormat.A8R8G8B8 );
|
||||
bw.Write( ( ushort )image.Width );
|
||||
bw.Write( ( ushort )image.Height );
|
||||
bw.Write( ( ushort )1 );
|
||||
bw.Write( ( ushort )1 );
|
||||
bw.Write( 0 );
|
||||
bw.Write( 1 );
|
||||
bw.Write( 2 );
|
||||
bw.Write( 80 );
|
||||
for( var i = 1; i < 13; ++i )
|
||||
{
|
||||
bw.Write( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var span = new Span< byte >( buffer, 80, buffer.Length - 80 );
|
||||
image.CopyPixelDataTo( span );
|
||||
|
||||
texData = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Import( string inputFile )
|
||||
{ }
|
||||
}
|
||||
|
|
@ -66,6 +66,7 @@
|
|||
<PackageReference Include="EmbedIO" Version="3.4.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.3.1" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
348
Penumbra/UI/Classes/ModEditWindow.Textures.cs
Normal file
348
Penumbra/UI/Classes/ModEditWindow.Textures.cs
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Logging;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
using Lumina.Data;
|
||||
using Lumina.Data.Files;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.ByteString;
|
||||
using Penumbra.Import.Textures;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
||||
public partial class ModEditWindow
|
||||
{
|
||||
private string _pathLeft = string.Empty;
|
||||
private string _pathRight = string.Empty;
|
||||
private string _pathSave = string.Empty;
|
||||
|
||||
private byte[]? _imageLeft;
|
||||
private byte[]? _imageRight;
|
||||
private byte[]? _imageCenter;
|
||||
|
||||
private TextureWrap? _wrapLeft;
|
||||
private TextureWrap? _wrapRight;
|
||||
private TextureWrap? _wrapCenter;
|
||||
|
||||
private Matrix4x4 _multiplierLeft = Matrix4x4.Identity;
|
||||
private Matrix4x4 _multiplierRight = Matrix4x4.Identity;
|
||||
|
||||
private bool DrawMatrixInput( float width, ref Matrix4x4 matrix )
|
||||
{
|
||||
using var table = ImRaii.Table( string.Empty, 5, ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingFixedFit );
|
||||
if( !table )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var changes = false;
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center( "R" );
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center( "G" );
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center( "B" );
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.Center( "A" );
|
||||
|
||||
var inputWidth = width / 6;
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text( "R " );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##RR", ref matrix.M11, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##RG", ref matrix.M12, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##RB", ref matrix.M13, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##RA", ref matrix.M14, 0.001f, -1f, 1f );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text( "G " );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##GR", ref matrix.M21, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##GG", ref matrix.M22, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##GB", ref matrix.M23, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##GA", ref matrix.M24, 0.001f, -1f, 1f );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text( "B " );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##BR", ref matrix.M31, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##BG", ref matrix.M32, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##BB", ref matrix.M33, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##BA", ref matrix.M34, 0.001f, -1f, 1f );
|
||||
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.Text( "A " );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##AR", ref matrix.M41, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##AG", ref matrix.M42, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##AB", ref matrix.M43, 0.001f, -1f, 1f );
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( inputWidth );
|
||||
changes |= ImGui.DragFloat( "##AA", ref matrix.M44, 0.001f, -1f, 1f );
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private bool PathInputBox( string label, string hint, string tooltip, ref string path )
|
||||
{
|
||||
var tmp = path;
|
||||
using var spacing = ImRaii.PushStyle( ImGuiStyleVar.ItemSpacing, new Vector2( 3 * ImGuiHelpers.GlobalScale, 0 ) );
|
||||
ImGui.SetNextItemWidth( -ImGui.GetFrameHeight() - 3 * ImGuiHelpers.GlobalScale );
|
||||
ImGui.InputTextWithHint( label, hint, ref tmp, Utf8GamePath.MaxGamePathLength );
|
||||
var ret = ImGui.IsItemDeactivatedAfterEdit() && tmp != path;
|
||||
ImGuiUtil.HoverTooltip( tooltip );
|
||||
ImGui.SameLine();
|
||||
ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Folder.ToIconString(), new Vector2( ImGui.GetFrameHeight() ), string.Empty, false, true );
|
||||
if( ret )
|
||||
{
|
||||
path = tmp;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static (byte[]?, int, int) GetDdsRgbaData( string path )
|
||||
{
|
||||
try
|
||||
{
|
||||
using var stream = File.OpenRead( path );
|
||||
if( !DdsFile.Load( stream, out var f ) )
|
||||
{
|
||||
return ( null, 0, 0 );
|
||||
}
|
||||
|
||||
f.ConvertToTex( out var bytes );
|
||||
using var ms = new MemoryStream( bytes );
|
||||
using var sq = new SqPackStream( ms );
|
||||
var x = sq.ReadFile< TexFile >( 0 );
|
||||
return ( x.GetRgbaImageData(), x.Header.Width, x.Header.Height );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not parse DDS {path} to RGBA:\n{e}" );
|
||||
return ( null, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private static ( byte[]?, int, int) GetTexRgbaData( string path, bool fromDisk )
|
||||
{
|
||||
try
|
||||
{
|
||||
var tex = fromDisk ? Dalamud.GameData.GameData.GetFileFromDisk< TexFile >( path ) : Dalamud.GameData.GetFile< TexFile >( path );
|
||||
return tex == null
|
||||
? ( null, 0, 0 )
|
||||
: ( tex.GetRgbaImageData(), tex.Header.Width, tex.Header.Height );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not parse TEX {path} to RGBA:\n{e}" );
|
||||
return ( null, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private static (byte[]?, int, int) GetPngRgbaData( string path )
|
||||
{
|
||||
try
|
||||
{
|
||||
using var stream = File.OpenRead( path );
|
||||
var png = Image.Load< Rgba32 >( stream );
|
||||
var bytes = new byte[png.Height * png.Width * 4];
|
||||
png.CopyPixelDataTo( bytes );
|
||||
return ( bytes, png.Width, png.Height );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not parse PNG {path} to RGBA:\n{e}" );
|
||||
return ( null, 0, 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateImage( string path, ref byte[]? data, ref TextureWrap? wrap )
|
||||
{
|
||||
data = null;
|
||||
wrap?.Dispose();
|
||||
wrap = null;
|
||||
var width = 0;
|
||||
var height = 0;
|
||||
|
||||
if( Path.IsPathRooted( path ) )
|
||||
{
|
||||
if( File.Exists( path ) )
|
||||
{
|
||||
( data, width, height ) = Path.GetExtension( path ) switch
|
||||
{
|
||||
".dds" => GetDdsRgbaData( path ),
|
||||
".png" => GetPngRgbaData( path ),
|
||||
".tex" => GetTexRgbaData( path, true ),
|
||||
_ => ( null, 0, 0 ),
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
( data, width, height ) = GetTexRgbaData( path, false );
|
||||
}
|
||||
|
||||
if( data != null )
|
||||
{
|
||||
wrap = Dalamud.PluginInterface.UiBuilder.LoadImageRaw( data, width, height, 4 );
|
||||
}
|
||||
|
||||
UpdateCenter();
|
||||
}
|
||||
|
||||
private void AddPixels( int width, int x, int y )
|
||||
{
|
||||
var offset = ( y * width + x ) * 4;
|
||||
var rgbaLeft = _imageLeft != null
|
||||
? new Rgba32( _imageLeft[ offset ], _imageLeft[ offset + 1 ], _imageLeft[ offset + 2 ], _imageLeft[ offset + 3 ] )
|
||||
: new Rgba32();
|
||||
var rgbaRight = _imageRight != null
|
||||
? new Rgba32( _imageRight[ offset ], _imageRight[ offset + 1 ], _imageRight[ offset + 2 ], _imageRight[ offset + 3 ] )
|
||||
: new Rgba32();
|
||||
var transformLeft = Vector4.Transform( rgbaLeft.ToVector4(), _multiplierLeft );
|
||||
var transformRight = Vector4.Transform( rgbaRight.ToVector4(), _multiplierRight );
|
||||
var alpha = transformLeft.Z + transformRight.Z * ( 1 - transformLeft.Z );
|
||||
var rgba = alpha == 0
|
||||
? new Rgba32()
|
||||
: new Rgba32( ( transformLeft * transformLeft.Z + transformRight * transformRight.Z * ( 1 - transformLeft.Z ) ) / alpha );
|
||||
_imageCenter![ offset ] = rgba.R;
|
||||
_imageCenter![ offset + 1 ] = rgba.G;
|
||||
_imageCenter![ offset + 2 ] = rgba.B;
|
||||
_imageCenter![ offset + 3 ] = rgba.A;
|
||||
}
|
||||
|
||||
private void UpdateCenter()
|
||||
{
|
||||
_wrapCenter?.Dispose();
|
||||
if( _imageLeft != null || _imageRight != null )
|
||||
{
|
||||
var (width, height) = _imageLeft != null ? ( _wrapLeft!.Width, _wrapLeft.Height ) : ( _wrapRight!.Width, _wrapRight.Height );
|
||||
if( _imageRight == null || _wrapRight!.Width == width && _wrapRight!.Height == height )
|
||||
{
|
||||
_imageCenter = new byte[4 * width * height];
|
||||
|
||||
for( var y = 0; y < height; ++y )
|
||||
{
|
||||
for( var x = 0; x < width; ++x )
|
||||
{
|
||||
AddPixels( width, x, y );
|
||||
}
|
||||
}
|
||||
|
||||
_wrapCenter = Dalamud.PluginInterface.UiBuilder.LoadImageRaw( _imageCenter, width, height, 4 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_imageCenter = null;
|
||||
_wrapCenter = null;
|
||||
}
|
||||
|
||||
private static void ScaledImage( TextureWrap? wrap, Vector2 size )
|
||||
{
|
||||
if( wrap != null )
|
||||
{
|
||||
size = size with { Y = wrap.Height * size.X / wrap.Width };
|
||||
ImGui.Image( wrap.ImGuiHandle, size );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.Dummy( size );
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTextureTab()
|
||||
{
|
||||
using var tab = ImRaii.TabItem( "Texture Import/Export" );
|
||||
if( !tab )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var leftRightWidth = new Vector2( ( ImGui.GetWindowContentRegionWidth() - ImGui.GetStyle().FramePadding.X * 4 ) / 3, -1 );
|
||||
var imageSize = new Vector2( leftRightWidth.X - ImGui.GetStyle().FramePadding.X * 2 );
|
||||
using( var child = ImRaii.Child( "ImageLeft", leftRightWidth, true ) )
|
||||
{
|
||||
if( PathInputBox( "##ImageLeft", "Import Image...", string.Empty, ref _pathLeft ) )
|
||||
{
|
||||
UpdateImage( _pathLeft, ref _imageLeft, ref _wrapLeft );
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
if( DrawMatrixInput( leftRightWidth.X, ref _multiplierLeft ) )
|
||||
{
|
||||
UpdateCenter();
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
ScaledImage( _wrapLeft, imageSize );
|
||||
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
using( var child = ImRaii.Child( "ImageMix", leftRightWidth, true ) )
|
||||
{
|
||||
ScaledImage( _wrapCenter, imageSize );
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
using( var child = ImRaii.Child( "ImageRight", leftRightWidth, true ) )
|
||||
{
|
||||
if( PathInputBox( "##ImageRight", "Import Image...", string.Empty, ref _pathRight ) )
|
||||
{
|
||||
UpdateImage( _pathRight, ref _imageRight, ref _wrapRight );
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
if( DrawMatrixInput( leftRightWidth.X, ref _multiplierRight ) )
|
||||
{
|
||||
UpdateCenter();
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
ScaledImage( _wrapRight, imageSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,7 @@ public partial class ModEditWindow : Window, IDisposable
|
|||
DrawMissingFilesTab();
|
||||
DrawDuplicatesTab();
|
||||
DrawMaterialChangeTab();
|
||||
DrawTextureTab();
|
||||
}
|
||||
|
||||
// A row of three buttonSizes and a help marker that can be used for material suffix changing.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue