mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Further work on texture importing.
This commit is contained in:
parent
80c717c9bc
commit
e994163637
2 changed files with 391 additions and 154 deletions
|
|
@ -5,7 +5,6 @@ using System.Runtime.InteropServices;
|
|||
using Dalamud.Logging;
|
||||
using Lumina.Data.Files;
|
||||
using Lumina.Extensions;
|
||||
using System.Drawing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
|
|
@ -37,10 +36,22 @@ public struct PixelFormat
|
|||
public FormatFlags Flags;
|
||||
public FourCCType FourCC;
|
||||
public int RgbBitCount;
|
||||
public int RBitMask;
|
||||
public int GBitMask;
|
||||
public int BBitMask;
|
||||
public int ABitMask;
|
||||
public uint RBitMask;
|
||||
public uint GBitMask;
|
||||
public uint BBitMask;
|
||||
public uint ABitMask;
|
||||
|
||||
public void Write( BinaryWriter bw )
|
||||
{
|
||||
bw.Write( Size );
|
||||
bw.Write( ( uint )Flags );
|
||||
bw.Write( ( uint )FourCC );
|
||||
bw.Write( RgbBitCount );
|
||||
bw.Write( RBitMask );
|
||||
bw.Write( GBitMask );
|
||||
bw.Write( BBitMask );
|
||||
bw.Write( ABitMask );
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Sequential )]
|
||||
|
|
@ -82,30 +93,70 @@ public struct DdsHeader
|
|||
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;
|
||||
public const int Size = 124;
|
||||
private 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;
|
||||
|
||||
public void Write( BinaryWriter bw )
|
||||
{
|
||||
bw.Write( ( byte )'D' );
|
||||
bw.Write( ( byte )'D' );
|
||||
bw.Write( ( byte )'S' );
|
||||
bw.Write( ( byte )' ' );
|
||||
bw.Write( Size );
|
||||
bw.Write( ( uint )Flags );
|
||||
bw.Write( Height );
|
||||
bw.Write( Width );
|
||||
bw.Write( PitchOrLinearSize );
|
||||
bw.Write( Depth );
|
||||
bw.Write( MipMapCount );
|
||||
bw.Write( Reserved1 );
|
||||
bw.Write( Reserved2 );
|
||||
bw.Write( Reserved3 );
|
||||
bw.Write( Reserved4 );
|
||||
bw.Write( Reserved5 );
|
||||
bw.Write( Reserved6 );
|
||||
bw.Write( Reserved7 );
|
||||
bw.Write( Reserved8 );
|
||||
bw.Write( Reserved9 );
|
||||
bw.Write( ReservedA );
|
||||
bw.Write( ReservedB );
|
||||
PixelFormat.Write( bw );
|
||||
bw.Write( ( uint )Caps1 );
|
||||
bw.Write( ( uint )Caps2 );
|
||||
bw.Write( Caps3 );
|
||||
bw.Write( Caps4 );
|
||||
bw.Write( ReservedC );
|
||||
}
|
||||
|
||||
public void Write( byte[] bytes, int offset )
|
||||
{
|
||||
using var m = new MemoryStream( bytes, offset, bytes.Length - offset );
|
||||
using var bw = new BinaryWriter( m );
|
||||
Write( bw );
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout( LayoutKind.Sequential )]
|
||||
|
|
@ -407,32 +458,88 @@ public class DdsFile
|
|||
|
||||
public class TextureImporter
|
||||
{
|
||||
public static bool ReadPng( string inputFile, out byte[] texData )
|
||||
private static void WriteHeader( byte[] target, int width, int height )
|
||||
{
|
||||
using var mem = new MemoryStream( target );
|
||||
using var bw = new BinaryWriter( mem );
|
||||
bw.Write( ( uint )TexFile.Attribute.TextureType2D );
|
||||
bw.Write( ( uint )TexFile.TextureFormat.A8R8G8B8 );
|
||||
bw.Write( ( ushort )width );
|
||||
bw.Write( ( ushort )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 );
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe bool RgbaBytesToDds( byte[] rgba, int width, int height, out byte[] ddsData )
|
||||
{
|
||||
var header = new DdsHeader()
|
||||
{
|
||||
Caps1 = DdsHeader.DdsCaps1.Complex | DdsHeader.DdsCaps1.Texture | DdsHeader.DdsCaps1.MipMap,
|
||||
Depth = 1,
|
||||
Flags = DdsHeader.DdsFlags.Required | DdsHeader.DdsFlags.Pitch | DdsHeader.DdsFlags.MipMapCount,
|
||||
Height = height,
|
||||
Width = width,
|
||||
PixelFormat = new PixelFormat()
|
||||
{
|
||||
Flags = PixelFormat.FormatFlags.AlphaPixels | PixelFormat.FormatFlags.RGB,
|
||||
FourCC = 0,
|
||||
BBitMask = 0x000000FF,
|
||||
GBitMask = 0x0000FF00,
|
||||
RBitMask = 0x00FF0000,
|
||||
ABitMask = 0xFF000000,
|
||||
Size = 32,
|
||||
RgbBitCount = 32,
|
||||
},
|
||||
};
|
||||
ddsData = new byte[DdsHeader.Size + rgba.Length];
|
||||
header.Write( ddsData, 0 );
|
||||
rgba.CopyTo( ddsData, DdsHeader.Size );
|
||||
for( var i = 0; i < rgba.Length; i += 4 )
|
||||
{
|
||||
( ddsData[ DdsHeader.Size + i ], ddsData[ DdsHeader.Size + i + 2 ] )
|
||||
= ( ddsData[ DdsHeader.Size + i + 2 ], ddsData[ DdsHeader.Size + i ] );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool RgbaBytesToTex( byte[] rgba, int width, int height, out byte[] texData )
|
||||
{
|
||||
texData = Array.Empty< byte >();
|
||||
if( rgba.Length != width * height * 4 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
texData = new byte[80 + width * height * 4];
|
||||
WriteHeader( texData, width, height );
|
||||
// RGBA to BGRA.
|
||||
for( var i = 0; i < rgba.Length; i += 4 )
|
||||
{
|
||||
texData[ 80 + i + 0 ] = rgba[ i + 2 ];
|
||||
texData[ 80 + i + 1 ] = rgba[ i + 1 ];
|
||||
texData[ 80 + i + 2 ] = rgba[ i + 0 ];
|
||||
texData[ 80 + i + 3 ] = rgba[ i + 3 ];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool PngToTex( 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
WriteHeader( buffer, image.Width, image.Height );
|
||||
|
||||
var span = new Span< byte >( buffer, 80, buffer.Length - 80 );
|
||||
image.CopyPixelDataTo( span );
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
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.Formats.Png;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace Penumbra.UI.Classes;
|
||||
|
|
@ -23,7 +24,6 @@ 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;
|
||||
|
|
@ -36,7 +36,22 @@ public partial class ModEditWindow
|
|||
private Matrix4x4 _multiplierLeft = Matrix4x4.Identity;
|
||||
private Matrix4x4 _multiplierRight = Matrix4x4.Identity;
|
||||
|
||||
private bool DrawMatrixInput( float width, ref Matrix4x4 matrix )
|
||||
private readonly FileDialogManager _dialogManager = new();
|
||||
|
||||
private static bool DragFloat( string label, float width, ref float value )
|
||||
{
|
||||
var tmp = value;
|
||||
ImGui.TableNextColumn();
|
||||
ImGui.SetNextItemWidth( width );
|
||||
if( ImGui.DragFloat( label, ref tmp, 0.001f, -1f, 1f ) )
|
||||
{
|
||||
value = tmp;
|
||||
}
|
||||
|
||||
return ImGui.IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
|
||||
private static bool DrawMatrixInput( float width, ref Matrix4x4 matrix )
|
||||
{
|
||||
using var table = ImRaii.Table( string.Empty, 5, ImGuiTableFlags.BordersInner | ImGuiTableFlags.SizingFixedFit );
|
||||
if( !table )
|
||||
|
|
@ -60,86 +75,66 @@ public partial class ModEditWindow
|
|||
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 );
|
||||
changes |= DragFloat( "##RR", inputWidth, ref matrix.M11 );
|
||||
changes |= DragFloat( "##RG", inputWidth, ref matrix.M12 );
|
||||
changes |= DragFloat( "##RB", inputWidth, ref matrix.M13 );
|
||||
changes |= DragFloat( "##RA", inputWidth, ref matrix.M14 );
|
||||
|
||||
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 );
|
||||
changes |= DragFloat( "##GR", inputWidth, ref matrix.M21 );
|
||||
changes |= DragFloat( "##GG", inputWidth, ref matrix.M22 );
|
||||
changes |= DragFloat( "##GB", inputWidth, ref matrix.M23 );
|
||||
changes |= DragFloat( "##GA", inputWidth, ref matrix.M24 );
|
||||
|
||||
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 );
|
||||
changes |= DragFloat( "##BR", inputWidth, ref matrix.M31 );
|
||||
changes |= DragFloat( "##BG", inputWidth, ref matrix.M32 );
|
||||
changes |= DragFloat( "##BB", inputWidth, ref matrix.M33 );
|
||||
changes |= DragFloat( "##BA", inputWidth, ref matrix.M34 );
|
||||
|
||||
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 );
|
||||
changes |= DragFloat( "##AR", inputWidth, ref matrix.M41 );
|
||||
changes |= DragFloat( "##AG", inputWidth, ref matrix.M42 );
|
||||
changes |= DragFloat( "##AB", inputWidth, ref matrix.M43 );
|
||||
changes |= DragFloat( "##AA", inputWidth, ref matrix.M44 );
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
private bool PathInputBox( string label, string hint, string tooltip, ref string path )
|
||||
private void PathInputBox( string label, string hint, string tooltip, int which )
|
||||
{
|
||||
var tmp = path;
|
||||
var tmp = which == 0 ? _pathLeft : _pathRight;
|
||||
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 )
|
||||
if( ImGui.IsItemDeactivatedAfterEdit() )
|
||||
{
|
||||
path = tmp;
|
||||
UpdateImage( tmp, which );
|
||||
}
|
||||
|
||||
return ret;
|
||||
ImGuiUtil.HoverTooltip( tooltip );
|
||||
ImGui.SameLine();
|
||||
if( ImGuiUtil.DrawDisabledButton( FontAwesomeIcon.Folder.ToIconString(), new Vector2( ImGui.GetFrameHeight() ), string.Empty, false,
|
||||
true ) )
|
||||
{
|
||||
var startPath = Penumbra.Config.DefaultModImportPath.Length > 0 ? Penumbra.Config.DefaultModImportPath : _mod?.ModPath.FullName;
|
||||
|
||||
void UpdatePath( bool success, List< string > paths )
|
||||
{
|
||||
if( success && paths.Count > 0 )
|
||||
{
|
||||
UpdateImage( paths[ 0 ], which );
|
||||
}
|
||||
}
|
||||
|
||||
_dialogManager.OpenFileDialog( "Open Image...", "Textures{.png,.dds,.tex}", UpdatePath, 1, startPath );
|
||||
}
|
||||
}
|
||||
|
||||
private static (byte[]?, int, int) GetDdsRgbaData( string path )
|
||||
|
|
@ -153,10 +148,18 @@ public partial class ModEditWindow
|
|||
}
|
||||
|
||||
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 );
|
||||
TexFile tex = new();
|
||||
tex.GetType().GetProperty( "Data",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy )
|
||||
?.SetValue( tex, bytes );
|
||||
tex.GetType().GetProperty( "FileStream",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy )
|
||||
?.SetValue( tex, new MemoryStream( tex.Data ) );
|
||||
tex.GetType().GetProperty( "Reader",
|
||||
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy )
|
||||
?.SetValue( tex, new BinaryReader( tex.FileStream ) );
|
||||
tex.LoadFile();
|
||||
return ( tex.GetRgbaImageData(), tex.Header.Width, tex.Header.Height );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
|
|
@ -186,7 +189,7 @@ public partial class ModEditWindow
|
|||
try
|
||||
{
|
||||
using var stream = File.OpenRead( path );
|
||||
var png = Image.Load< Rgba32 >( stream );
|
||||
using var png = Image.Load< Rgba32 >( stream );
|
||||
var bytes = new byte[png.Height * png.Width * 4];
|
||||
png.CopyPixelDataTo( bytes );
|
||||
return ( bytes, png.Width, png.Height );
|
||||
|
|
@ -198,8 +201,23 @@ public partial class ModEditWindow
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateImage( string path, ref byte[]? data, ref TextureWrap? wrap )
|
||||
private void UpdateImage( string newPath, int which )
|
||||
{
|
||||
if( which is < 0 or > 1 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref var path = ref which == 0 ? ref _pathLeft : ref _pathRight;
|
||||
if( path == newPath )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
path = newPath;
|
||||
ref var data = ref which == 0 ? ref _imageLeft : ref _imageRight;
|
||||
ref var wrap = ref which == 0 ? ref _wrapLeft : ref _wrapRight;
|
||||
|
||||
data = null;
|
||||
wrap?.Dispose();
|
||||
wrap = null;
|
||||
|
|
@ -232,21 +250,35 @@ public partial class ModEditWindow
|
|||
UpdateCenter();
|
||||
}
|
||||
|
||||
private static Vector4 CappedVector( IReadOnlyList< byte >? bytes, int offset, Matrix4x4 transform )
|
||||
{
|
||||
if( bytes == null )
|
||||
{
|
||||
return Vector4.Zero;
|
||||
}
|
||||
|
||||
var rgba = new Rgba32( bytes[ offset ], bytes[ offset + 1 ], bytes[ offset + 2 ], bytes[ offset + 3 ] );
|
||||
var transformed = Vector4.Transform( rgba.ToVector4(), transform );
|
||||
transformed.X = Math.Clamp( transformed.X, 0, 1 );
|
||||
transformed.Y = Math.Clamp( transformed.Y, 0, 1 );
|
||||
transformed.Z = Math.Clamp( transformed.Z, 0, 1 );
|
||||
transformed.W = Math.Clamp( transformed.W, 0, 1 );
|
||||
return transformed;
|
||||
}
|
||||
|
||||
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 );
|
||||
var left = CappedVector( _imageLeft, offset, _multiplierLeft );
|
||||
var right = CappedVector( _imageRight, offset, _multiplierRight );
|
||||
var alpha = right.W + left.W * ( 1 - right.W );
|
||||
if( alpha == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sum = ( right * right.W + left * left.W * ( 1 - right.W ) ) / alpha;
|
||||
var rgba = new Rgba32( sum with { W = alpha } );
|
||||
_imageCenter![ offset ] = rgba.R;
|
||||
_imageCenter![ offset + 1 ] = rgba.G;
|
||||
_imageCenter![ offset + 2 ] = rgba.B;
|
||||
|
|
@ -255,7 +287,25 @@ public partial class ModEditWindow
|
|||
|
||||
private void UpdateCenter()
|
||||
{
|
||||
_wrapCenter?.Dispose();
|
||||
if( _imageLeft != null && _imageRight == null && _multiplierLeft.IsIdentity )
|
||||
{
|
||||
_imageCenter = _imageLeft;
|
||||
_wrapCenter = _wrapLeft;
|
||||
return;
|
||||
}
|
||||
|
||||
if( _imageLeft == null && _imageRight != null && _multiplierRight.IsIdentity )
|
||||
{
|
||||
_imageCenter = _imageRight;
|
||||
_wrapCenter = _wrapRight;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !ReferenceEquals( _imageCenter, _imageLeft ) && !ReferenceEquals( _imageCenter, _imageRight ) )
|
||||
{
|
||||
_wrapCenter?.Dispose();
|
||||
}
|
||||
|
||||
if( _imageLeft != null || _imageRight != null )
|
||||
{
|
||||
var (width, height) = _imageLeft != null ? ( _wrapLeft!.Width, _wrapLeft.Height ) : ( _wrapRight!.Width, _wrapRight.Height );
|
||||
|
|
@ -280,21 +330,73 @@ public partial class ModEditWindow
|
|||
_wrapCenter = null;
|
||||
}
|
||||
|
||||
private static void ScaledImage( TextureWrap? wrap, Vector2 size )
|
||||
private static void ScaledImage( string path, TextureWrap? wrap, Vector2 size )
|
||||
{
|
||||
if( wrap != null )
|
||||
{
|
||||
size = size with { Y = wrap.Height * size.X / wrap.Width };
|
||||
ImGui.Image( wrap.ImGuiHandle, size );
|
||||
}
|
||||
else if( path.Length > 0 )
|
||||
{
|
||||
ImGui.TextUnformatted( "Could not load file." );
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui.Dummy( size );
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAs( bool success, string path, int type )
|
||||
{
|
||||
if( !success || _imageCenter == null || _wrapCenter == null )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
switch( type )
|
||||
{
|
||||
case 0:
|
||||
var img = Image.LoadPixelData< Rgba32 >( _imageCenter, _wrapCenter.Width, _wrapCenter.Height );
|
||||
img.Save( path, new PngEncoder() { CompressionLevel = PngCompressionLevel.NoCompression } );
|
||||
break;
|
||||
case 1:
|
||||
if( TextureImporter.RgbaBytesToTex( _imageCenter, _wrapCenter.Width, _wrapCenter.Height, out var tex ) )
|
||||
{
|
||||
File.WriteAllBytes( path, tex );
|
||||
}
|
||||
|
||||
break;
|
||||
case 2:
|
||||
if( TextureImporter.RgbaBytesToDds( _imageCenter, _wrapCenter.Width, _wrapCenter.Height, out var dds ) )
|
||||
{
|
||||
File.WriteAllBytes( path, dds );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
PluginLog.Error( $"Could not save image to {path}:\n{e}" );
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAsPng( bool success, string path )
|
||||
=> SaveAs( success, path, 0 );
|
||||
|
||||
private void SaveAsTex( bool success, string path )
|
||||
=> SaveAs( success, path, 1 );
|
||||
|
||||
private void SaveAsDds( bool success, string path )
|
||||
=> SaveAs( success, path, 2 );
|
||||
|
||||
private void DrawTextureTab()
|
||||
{
|
||||
_dialogManager.Draw();
|
||||
|
||||
using var tab = ImRaii.TabItem( "Texture Import/Export" );
|
||||
if( !tab )
|
||||
{
|
||||
|
|
@ -305,44 +407,72 @@ public partial class ModEditWindow
|
|||
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 ) )
|
||||
if( child )
|
||||
{
|
||||
UpdateImage( _pathLeft, ref _imageLeft, ref _wrapLeft );
|
||||
PathInputBox( "##ImageLeft", "Import Image...", string.Empty, 0 );
|
||||
|
||||
ImGui.NewLine();
|
||||
if( DrawMatrixInput( leftRightWidth.X, ref _multiplierLeft ) )
|
||||
{
|
||||
UpdateCenter();
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
ScaledImage( _pathLeft, _wrapLeft, imageSize );
|
||||
}
|
||||
|
||||
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 );
|
||||
if( child )
|
||||
{
|
||||
if( _wrapCenter == null && _wrapLeft != null && _wrapRight != null )
|
||||
{
|
||||
ImGui.TextUnformatted( "Images have incompatible resolutions." );
|
||||
}
|
||||
else if( _wrapCenter != null )
|
||||
{
|
||||
if( ImGui.Button( "Save as TEX", -Vector2.UnitX ) )
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension( _pathLeft.Length > 0 ? _pathLeft : _pathRight );
|
||||
_dialogManager.SaveFileDialog( "Save Texture as TEX...", ".tex", fileName, ".tex", SaveAsTex, _mod!.ModPath.FullName );
|
||||
}
|
||||
|
||||
if( ImGui.Button( "Save as PNG", -Vector2.UnitX ) )
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension( _pathRight.Length > 0 ? _pathRight : _pathLeft );
|
||||
_dialogManager.SaveFileDialog( "Save Texture as PNG...", ".png", fileName, ".png", SaveAsPng, _mod!.ModPath.FullName );
|
||||
}
|
||||
|
||||
if( ImGui.Button( "Save as DDS", -Vector2.UnitX ) )
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension( _pathRight.Length > 0 ? _pathRight : _pathLeft );
|
||||
_dialogManager.SaveFileDialog( "Save Texture as DDS...", ".dds", fileName, ".dds", SaveAsDds, _mod!.ModPath.FullName );
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
ScaledImage( string.Empty, _wrapCenter, imageSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
using( var child = ImRaii.Child( "ImageRight", leftRightWidth, true ) )
|
||||
{
|
||||
if( PathInputBox( "##ImageRight", "Import Image...", string.Empty, ref _pathRight ) )
|
||||
if( child )
|
||||
{
|
||||
UpdateImage( _pathRight, ref _imageRight, ref _wrapRight );
|
||||
}
|
||||
PathInputBox( "##ImageRight", "Import Image...", string.Empty, 1 );
|
||||
|
||||
ImGui.NewLine();
|
||||
if( DrawMatrixInput( leftRightWidth.X, ref _multiplierRight ) )
|
||||
{
|
||||
UpdateCenter();
|
||||
}
|
||||
ImGui.NewLine();
|
||||
if( DrawMatrixInput( leftRightWidth.X, ref _multiplierRight ) )
|
||||
{
|
||||
UpdateCenter();
|
||||
}
|
||||
|
||||
ImGui.NewLine();
|
||||
ScaledImage( _wrapRight, imageSize );
|
||||
ImGui.NewLine();
|
||||
ScaledImage( _pathRight, _wrapRight, imageSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue