mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Finish work on dye previews.
This commit is contained in:
parent
4df8f720f5
commit
17a8e06c1d
7 changed files with 722 additions and 547 deletions
|
|
@ -7,6 +7,7 @@ using System.Numerics;
|
|||
using System.Text;
|
||||
using Lumina.Data.Parsing;
|
||||
using Lumina.Extensions;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Penumbra.GameData.Files;
|
||||
|
||||
|
|
@ -28,114 +29,112 @@ public partial class MtrlFile : IWritable
|
|||
|
||||
public Vector3 Diffuse
|
||||
{
|
||||
get => new(ToFloat( 0 ), ToFloat( 1 ), ToFloat( 2 ));
|
||||
get => new(ToFloat(0), ToFloat(1), ToFloat(2));
|
||||
set
|
||||
{
|
||||
_data[ 0 ] = FromFloat( value.X );
|
||||
_data[ 1 ] = FromFloat( value.Y );
|
||||
_data[ 2 ] = FromFloat( value.Z );
|
||||
_data[0] = FromFloat(value.X);
|
||||
_data[1] = FromFloat(value.Y);
|
||||
_data[2] = FromFloat(value.Z);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 Specular
|
||||
{
|
||||
get => new(ToFloat( 4 ), ToFloat( 5 ), ToFloat( 6 ));
|
||||
get => new(ToFloat(4), ToFloat(5), ToFloat(6));
|
||||
set
|
||||
{
|
||||
_data[ 4 ] = FromFloat( value.X );
|
||||
_data[ 5 ] = FromFloat( value.Y );
|
||||
_data[ 6 ] = FromFloat( value.Z );
|
||||
_data[4] = FromFloat(value.X);
|
||||
_data[5] = FromFloat(value.Y);
|
||||
_data[6] = FromFloat(value.Z);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 Emissive
|
||||
{
|
||||
get => new(ToFloat( 8 ), ToFloat( 9 ), ToFloat( 10 ));
|
||||
get => new(ToFloat(8), ToFloat(9), ToFloat(10));
|
||||
set
|
||||
{
|
||||
_data[ 8 ] = FromFloat( value.X );
|
||||
_data[ 9 ] = FromFloat( value.Y );
|
||||
_data[ 10 ] = FromFloat( value.Z );
|
||||
_data[8] = FromFloat(value.X);
|
||||
_data[9] = FromFloat(value.Y);
|
||||
_data[10] = FromFloat(value.Z);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 MaterialRepeat
|
||||
{
|
||||
get => new(ToFloat( 12 ), ToFloat( 15 ));
|
||||
get => new(ToFloat(12), ToFloat(15));
|
||||
set
|
||||
{
|
||||
_data[ 12 ] = FromFloat( value.X );
|
||||
_data[ 15 ] = FromFloat( value.Y );
|
||||
_data[12] = FromFloat(value.X);
|
||||
_data[15] = FromFloat(value.Y);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 MaterialSkew
|
||||
{
|
||||
get => new(ToFloat( 13 ), ToFloat( 14 ));
|
||||
get => new(ToFloat(13), ToFloat(14));
|
||||
set
|
||||
{
|
||||
_data[ 13 ] = FromFloat( value.X );
|
||||
_data[ 14 ] = FromFloat( value.Y );
|
||||
_data[13] = FromFloat(value.X);
|
||||
_data[14] = FromFloat(value.Y);
|
||||
}
|
||||
}
|
||||
|
||||
public float SpecularStrength
|
||||
{
|
||||
get => ToFloat( 3 );
|
||||
set => _data[ 3 ] = FromFloat( value );
|
||||
get => ToFloat(3);
|
||||
set => _data[3] = FromFloat(value);
|
||||
}
|
||||
|
||||
public float GlossStrength
|
||||
{
|
||||
get => ToFloat( 7 );
|
||||
set => _data[ 7 ] = FromFloat( value );
|
||||
get => ToFloat(7);
|
||||
set => _data[7] = FromFloat(value);
|
||||
}
|
||||
|
||||
public ushort TileSet
|
||||
{
|
||||
get => (ushort) (ToFloat(11) * 64f);
|
||||
set => _data[ 11 ] = FromFloat(value / 64f);
|
||||
get => (ushort)(ToFloat(11) * 64f);
|
||||
set => _data[11] = FromFloat(value / 64f);
|
||||
}
|
||||
|
||||
private float ToFloat( int idx )
|
||||
=> ( float )BitConverter.UInt16BitsToHalf( _data[ idx ] );
|
||||
private float ToFloat(int idx)
|
||||
=> (float)BitConverter.UInt16BitsToHalf(_data[idx]);
|
||||
|
||||
private static ushort FromFloat( float x )
|
||||
=> BitConverter.HalfToUInt16Bits( ( Half )x );
|
||||
private static ushort FromFloat(float x)
|
||||
=> BitConverter.HalfToUInt16Bits((Half)x);
|
||||
}
|
||||
|
||||
public struct RowArray : IEnumerable< Row >
|
||||
public struct RowArray : IEnumerable<Row>
|
||||
{
|
||||
public const int NumRows = 16;
|
||||
private fixed byte _rowData[NumRows * Row.Size];
|
||||
|
||||
public ref Row this[ int i ]
|
||||
public ref Row this[int i]
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed( byte* ptr = _rowData )
|
||||
fixed (byte* ptr = _rowData)
|
||||
{
|
||||
return ref ( ( Row* )ptr )[ i ];
|
||||
return ref ((Row*)ptr)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator< Row > GetEnumerator()
|
||||
public IEnumerator<Row> GetEnumerator()
|
||||
{
|
||||
for( var i = 0; i < NumRows; ++i )
|
||||
{
|
||||
yield return this[ i ];
|
||||
}
|
||||
for (var i = 0; i < NumRows; ++i)
|
||||
yield return this[i];
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public ReadOnlySpan< byte > AsBytes()
|
||||
public ReadOnlySpan<byte> AsBytes()
|
||||
{
|
||||
fixed( byte* ptr = _rowData )
|
||||
fixed (byte* ptr = _rowData)
|
||||
{
|
||||
return new ReadOnlySpan< byte >( ptr, NumRows * Row.Size );
|
||||
return new ReadOnlySpan<byte>(ptr, NumRows * Row.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -154,73 +153,71 @@ public partial class MtrlFile : IWritable
|
|||
|
||||
public ushort Template
|
||||
{
|
||||
get => ( ushort )( _data >> 5 );
|
||||
set => _data = ( ushort )( ( _data & 0x1F ) | ( value << 5 ) );
|
||||
get => (ushort)(_data >> 5);
|
||||
set => _data = (ushort)((_data & 0x1F) | (value << 5));
|
||||
}
|
||||
|
||||
public bool Diffuse
|
||||
{
|
||||
get => ( _data & 0x01 ) != 0;
|
||||
set => _data = ( ushort )( value ? _data | 0x01 : _data & 0xFFFE );
|
||||
get => (_data & 0x01) != 0;
|
||||
set => _data = (ushort)(value ? _data | 0x01 : _data & 0xFFFE);
|
||||
}
|
||||
|
||||
public bool Specular
|
||||
{
|
||||
get => ( _data & 0x02 ) != 0;
|
||||
set => _data = ( ushort )( value ? _data | 0x02 : _data & 0xFFFD );
|
||||
get => (_data & 0x02) != 0;
|
||||
set => _data = (ushort)(value ? _data | 0x02 : _data & 0xFFFD);
|
||||
}
|
||||
|
||||
public bool Emissive
|
||||
{
|
||||
get => ( _data & 0x04 ) != 0;
|
||||
set => _data = ( ushort )( value ? _data | 0x04 : _data & 0xFFFB );
|
||||
get => (_data & 0x04) != 0;
|
||||
set => _data = (ushort)(value ? _data | 0x04 : _data & 0xFFFB);
|
||||
}
|
||||
|
||||
public bool Gloss
|
||||
{
|
||||
get => ( _data & 0x08 ) != 0;
|
||||
set => _data = ( ushort )( value ? _data | 0x08 : _data & 0xFFF7 );
|
||||
get => (_data & 0x08) != 0;
|
||||
set => _data = (ushort)(value ? _data | 0x08 : _data & 0xFFF7);
|
||||
}
|
||||
|
||||
public bool SpecularStrength
|
||||
{
|
||||
get => ( _data & 0x10 ) != 0;
|
||||
set => _data = ( ushort )( value ? _data | 0x10 : _data & 0xFFEF );
|
||||
get => (_data & 0x10) != 0;
|
||||
set => _data = (ushort)(value ? _data | 0x10 : _data & 0xFFEF);
|
||||
}
|
||||
}
|
||||
|
||||
public struct RowArray : IEnumerable< Row >
|
||||
public struct RowArray : IEnumerable<Row>
|
||||
{
|
||||
public const int NumRows = 16;
|
||||
private fixed ushort _rowData[NumRows];
|
||||
|
||||
public ref Row this[ int i ]
|
||||
public ref Row this[int i]
|
||||
{
|
||||
get
|
||||
{
|
||||
fixed( ushort* ptr = _rowData )
|
||||
fixed (ushort* ptr = _rowData)
|
||||
{
|
||||
return ref ( ( Row* )ptr )[ i ];
|
||||
return ref ((Row*)ptr)[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator< Row > GetEnumerator()
|
||||
public IEnumerator<Row> GetEnumerator()
|
||||
{
|
||||
for( var i = 0; i < NumRows; ++i )
|
||||
{
|
||||
yield return this[ i ];
|
||||
}
|
||||
for (var i = 0; i < NumRows; ++i)
|
||||
yield return this[i];
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public ReadOnlySpan< byte > AsBytes()
|
||||
public ReadOnlySpan<byte> AsBytes()
|
||||
{
|
||||
fixed( ushort* ptr = _rowData )
|
||||
fixed (ushort* ptr = _rowData)
|
||||
{
|
||||
return new ReadOnlySpan< byte >( ptr, NumRows * sizeof( ushort ) );
|
||||
return new ReadOnlySpan<byte>(ptr, NumRows * sizeof(ushort));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -262,10 +259,53 @@ public partial class MtrlFile : IWritable
|
|||
public ShaderPackageData ShaderPackage;
|
||||
public byte[] AdditionalData;
|
||||
|
||||
public MtrlFile( byte[] data )
|
||||
public bool ApplyDyeTemplate(StmFile stm, int colorSetIdx, int rowIdx, StainId stainId)
|
||||
{
|
||||
using var stream = new MemoryStream( data );
|
||||
using var r = new BinaryReader( stream );
|
||||
if (colorSetIdx < 0 || colorSetIdx >= ColorDyeSets.Length || rowIdx is < 0 or >= ColorSet.RowArray.NumRows)
|
||||
return false;
|
||||
|
||||
var dyeSet = ColorDyeSets[colorSetIdx].Rows[rowIdx];
|
||||
if (!stm.TryGetValue(dyeSet.Template, stainId, out var dyes))
|
||||
return false;
|
||||
|
||||
var ret = false;
|
||||
if (dyeSet.Diffuse && ColorSets[colorSetIdx].Rows[rowIdx].Diffuse != dyes.Diffuse)
|
||||
{
|
||||
ColorSets[colorSetIdx].Rows[rowIdx].Diffuse = dyes.Diffuse;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (dyeSet.Specular && ColorSets[colorSetIdx].Rows[rowIdx].Specular != dyes.Specular)
|
||||
{
|
||||
ColorSets[colorSetIdx].Rows[rowIdx].Specular = dyes.Specular;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (dyeSet.SpecularStrength && ColorSets[colorSetIdx].Rows[rowIdx].SpecularStrength != dyes.SpecularPower)
|
||||
{
|
||||
ColorSets[colorSetIdx].Rows[rowIdx].SpecularStrength = dyes.SpecularPower;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (dyeSet.Emissive && ColorSets[colorSetIdx].Rows[rowIdx].Emissive != dyes.Emissive)
|
||||
{
|
||||
ColorSets[colorSetIdx].Rows[rowIdx].Emissive = dyes.Emissive;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
if (dyeSet.Gloss && ColorSets[colorSetIdx].Rows[rowIdx].GlossStrength != dyes.Gloss)
|
||||
{
|
||||
ColorSets[colorSetIdx].Rows[rowIdx].GlossStrength = dyes.Gloss;
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public MtrlFile(byte[] data)
|
||||
{
|
||||
using var stream = new MemoryStream(data);
|
||||
using var r = new BinaryReader(stream);
|
||||
|
||||
Version = r.ReadUInt32();
|
||||
r.ReadUInt16(); // file size
|
||||
|
|
@ -277,39 +317,37 @@ public partial class MtrlFile : IWritable
|
|||
var colorSetCount = r.ReadByte();
|
||||
var additionalDataSize = r.ReadByte();
|
||||
|
||||
Textures = ReadTextureOffsets( r, textureCount, out var textureOffsets );
|
||||
UvSets = ReadUvSetOffsets( r, uvSetCount, out var uvOffsets );
|
||||
ColorSets = ReadColorSetOffsets( r, colorSetCount, out var colorOffsets );
|
||||
Textures = ReadTextureOffsets(r, textureCount, out var textureOffsets);
|
||||
UvSets = ReadUvSetOffsets(r, uvSetCount, out var uvOffsets);
|
||||
ColorSets = ReadColorSetOffsets(r, colorSetCount, out var colorOffsets);
|
||||
|
||||
var strings = r.ReadBytes( stringTableSize );
|
||||
for( var i = 0; i < textureCount; ++i )
|
||||
{
|
||||
Textures[ i ].Path = UseOffset( strings, textureOffsets[ i ] );
|
||||
}
|
||||
var strings = r.ReadBytes(stringTableSize);
|
||||
for (var i = 0; i < textureCount; ++i)
|
||||
Textures[i].Path = UseOffset(strings, textureOffsets[i]);
|
||||
|
||||
for( var i = 0; i < uvSetCount; ++i )
|
||||
{
|
||||
UvSets[ i ].Name = UseOffset( strings, uvOffsets[ i ] );
|
||||
}
|
||||
for (var i = 0; i < uvSetCount; ++i)
|
||||
UvSets[i].Name = UseOffset(strings, uvOffsets[i]);
|
||||
|
||||
for( var i = 0; i < colorSetCount; ++i )
|
||||
{
|
||||
ColorSets[ i ].Name = UseOffset( strings, colorOffsets[ i ] );
|
||||
}
|
||||
for (var i = 0; i < colorSetCount; ++i)
|
||||
ColorSets[i].Name = UseOffset(strings, colorOffsets[i]);
|
||||
|
||||
ColorDyeSets = ColorSets.Length * ColorSet.RowArray.NumRows * ColorSet.Row.Size < dataSetSize
|
||||
? ColorSets.Select( c => new ColorDyeSet { Index = c.Index, Name = c.Name } ).ToArray()
|
||||
: Array.Empty< ColorDyeSet >();
|
||||
|
||||
ShaderPackage.Name = UseOffset( strings, shaderPackageNameOffset );
|
||||
|
||||
AdditionalData = r.ReadBytes( additionalDataSize );
|
||||
for( var i = 0; i < ColorSets.Length; ++i )
|
||||
{
|
||||
if( stream.Position + ColorSet.RowArray.NumRows * ColorSet.Row.Size <= stream.Length )
|
||||
? ColorSets.Select(c => new ColorDyeSet
|
||||
{
|
||||
ColorSets[ i ].Rows = r.ReadStructure< ColorSet.RowArray >();
|
||||
ColorSets[ i ].HasRows = true;
|
||||
Index = c.Index,
|
||||
Name = c.Name,
|
||||
}).ToArray()
|
||||
: Array.Empty<ColorDyeSet>();
|
||||
|
||||
ShaderPackage.Name = UseOffset(strings, shaderPackageNameOffset);
|
||||
|
||||
AdditionalData = r.ReadBytes(additionalDataSize);
|
||||
for (var i = 0; i < ColorSets.Length; ++i)
|
||||
{
|
||||
if (stream.Position + ColorSet.RowArray.NumRows * ColorSet.Row.Size <= stream.Length)
|
||||
{
|
||||
ColorSets[i].Rows = r.ReadStructure<ColorSet.RowArray>();
|
||||
ColorSets[i].HasRows = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -317,10 +355,8 @@ public partial class MtrlFile : IWritable
|
|||
}
|
||||
}
|
||||
|
||||
for( var i = 0; i < ColorDyeSets.Length; ++i )
|
||||
{
|
||||
ColorDyeSets[ i ].Rows = r.ReadStructure< ColorDyeSet.RowArray >();
|
||||
}
|
||||
for (var i = 0; i < ColorDyeSets.Length; ++i)
|
||||
ColorDyeSets[i].Rows = r.ReadStructure<ColorDyeSet.RowArray>();
|
||||
|
||||
var shaderValueListSize = r.ReadUInt16();
|
||||
var shaderKeyCount = r.ReadUInt16();
|
||||
|
|
@ -328,55 +364,55 @@ public partial class MtrlFile : IWritable
|
|||
var samplerCount = r.ReadUInt16();
|
||||
ShaderPackage.Flags = r.ReadUInt32();
|
||||
|
||||
ShaderPackage.ShaderKeys = r.ReadStructuresAsArray< ShaderKey >( shaderKeyCount );
|
||||
ShaderPackage.Constants = r.ReadStructuresAsArray< Constant >( constantCount );
|
||||
ShaderPackage.Samplers = r.ReadStructuresAsArray< Sampler >( samplerCount );
|
||||
ShaderPackage.ShaderValues = r.ReadStructuresAsArray< float >( shaderValueListSize / 4 );
|
||||
ShaderPackage.ShaderKeys = r.ReadStructuresAsArray<ShaderKey>(shaderKeyCount);
|
||||
ShaderPackage.Constants = r.ReadStructuresAsArray<Constant>(constantCount);
|
||||
ShaderPackage.Samplers = r.ReadStructuresAsArray<Sampler>(samplerCount);
|
||||
ShaderPackage.ShaderValues = r.ReadStructuresAsArray<float>(shaderValueListSize / 4);
|
||||
}
|
||||
|
||||
private static Texture[] ReadTextureOffsets( BinaryReader r, int count, out ushort[] offsets )
|
||||
private static Texture[] ReadTextureOffsets(BinaryReader r, int count, out ushort[] offsets)
|
||||
{
|
||||
var ret = new Texture[count];
|
||||
offsets = new ushort[count];
|
||||
for( var i = 0; i < count; ++i )
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
offsets[ i ] = r.ReadUInt16();
|
||||
ret[ i ].Flags = r.ReadUInt16();
|
||||
offsets[i] = r.ReadUInt16();
|
||||
ret[i].Flags = r.ReadUInt16();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static UvSet[] ReadUvSetOffsets( BinaryReader r, int count, out ushort[] offsets )
|
||||
private static UvSet[] ReadUvSetOffsets(BinaryReader r, int count, out ushort[] offsets)
|
||||
{
|
||||
var ret = new UvSet[count];
|
||||
offsets = new ushort[count];
|
||||
for( var i = 0; i < count; ++i )
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
offsets[ i ] = r.ReadUInt16();
|
||||
ret[ i ].Index = r.ReadUInt16();
|
||||
offsets[i] = r.ReadUInt16();
|
||||
ret[i].Index = r.ReadUInt16();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static ColorSet[] ReadColorSetOffsets( BinaryReader r, int count, out ushort[] offsets )
|
||||
private static ColorSet[] ReadColorSetOffsets(BinaryReader r, int count, out ushort[] offsets)
|
||||
{
|
||||
var ret = new ColorSet[count];
|
||||
offsets = new ushort[count];
|
||||
for( var i = 0; i < count; ++i )
|
||||
for (var i = 0; i < count; ++i)
|
||||
{
|
||||
offsets[ i ] = r.ReadUInt16();
|
||||
ret[ i ].Index = r.ReadUInt16();
|
||||
offsets[i] = r.ReadUInt16();
|
||||
ret[i].Index = r.ReadUInt16();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static string UseOffset( ReadOnlySpan< byte > strings, ushort offset )
|
||||
private static string UseOffset(ReadOnlySpan<byte> strings, ushort offset)
|
||||
{
|
||||
strings = strings[ offset.. ];
|
||||
var end = strings.IndexOf( ( byte )'\0' );
|
||||
return Encoding.UTF8.GetString( strings[ ..end ] );
|
||||
strings = strings[offset..];
|
||||
var end = strings.IndexOf((byte)'\0');
|
||||
return Encoding.UTF8.GetString(strings[..end]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue