mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2026-08-15 02:57:49 +04:00
fpopov thanks
This commit is contained in:
+107
-113
@@ -1,152 +1,148 @@
|
||||
namespace MaterialLib;
|
||||
|
||||
public class MaterialFile
|
||||
{
|
||||
// === Metadata (not from file content) ===
|
||||
public string FileName { get; set; }
|
||||
public int Version { get; set; } // From NRes ElementCount
|
||||
public int Magic1 { get; set; } // From NRes Magic1
|
||||
|
||||
// === Derived from Version/ElementCount ===
|
||||
public int MaterialRenderingType { get; set; } // (Version >> 2) & 0xF - 0=Standard, 1=Special, 2=Particle
|
||||
public bool SupportsBumpMapping { get; set; } // (Version & 2) != 0
|
||||
public int IsParticleEffect { get; set; } // Version & 40 - 0=Normal, 8=Particle/Effect
|
||||
|
||||
// === File Content (in read order) ===
|
||||
// Read order: StageCount (ushort), AnimCount (ushort), then conditionally blend modes and params
|
||||
|
||||
// Global Blend Modes (read if Magic1 >= 2)
|
||||
public BlendMode SourceBlendMode { get; set; } // Default: Unknown (0xFF)
|
||||
public BlendMode DestBlendMode { get; set; } // Default: Unknown (0xFF)
|
||||
|
||||
// Global Parameters (read if Magic1 > 2 and > 3 respectively)
|
||||
public float GlobalAlphaMultiplier { get; set; } // Default: 1.0 (always 1.0 in all 628 materials)
|
||||
public float GlobalEmissiveIntensity { get; set; } // Default: 0.0 (0=no glow, rare values: 1000, 10000)
|
||||
|
||||
public List<MaterialStage> Stages { get; set; } = new();
|
||||
public List<MaterialAnimation> Animations { get; set; } = new();
|
||||
}
|
||||
/// <summary>Файл материала MAT0.</summary>
|
||||
/// <param name="FileName">Имя файла из NRes metadata, не из payload материала.</param>
|
||||
/// <param name="Version">Значение attr1 / ElementCount из NRes metadata.</param>
|
||||
/// <param name="Magic1">Значение attr2 / Magic1 из NRes metadata.</param>
|
||||
/// <param name="MaterialRenderingType">Производное от Version: (Version >> 2) & 0xF. 0 = standard, 1 = special, 2 = particle.</param>
|
||||
/// <param name="SupportsBumpMapping">Производное от Version: (Version & 2) != 0.</param>
|
||||
/// <param name="IsParticleEffect">Производное от Version: Version & 40. 0 = normal, 8 = particle/effect.</param>
|
||||
/// <param name="SourceBlendMode">Глобальный source blend mode; читается, если Magic1 >= 2. Значение по умолчанию: Unknown (0xFF).</param>
|
||||
/// <param name="DestBlendMode">Глобальный destination blend mode; читается, если Magic1 >= 2. Значение по умолчанию: Unknown (0xFF).</param>
|
||||
/// <param name="GlobalAlphaMultiplier">Глобальный alpha multiplier; читается, если Magic1 > 2. Значение по умолчанию: 1.0.</param>
|
||||
/// <param name="GlobalEmissiveIntensity">Глобальная emissive intensity; читается, если Magic1 > 3. Значение по умолчанию: 0.0.</param>
|
||||
/// <param name="Stages">Стадии материала в порядке чтения из файла.</param>
|
||||
/// <param name="Animations">Анимации материала в порядке чтения из файла.</param>
|
||||
public record MaterialFile(
|
||||
string FileName,
|
||||
int Version,
|
||||
int Magic1,
|
||||
int MaterialRenderingType,
|
||||
bool SupportsBumpMapping,
|
||||
int IsParticleEffect,
|
||||
BlendMode SourceBlendMode,
|
||||
BlendMode DestBlendMode,
|
||||
float GlobalAlphaMultiplier,
|
||||
float GlobalEmissiveIntensity,
|
||||
List<MaterialStage> Stages,
|
||||
List<MaterialAnimation> Animations);
|
||||
|
||||
/// <summary>
|
||||
/// Blend modes for material rendering. These control how source and destination colors are combined.
|
||||
/// Formula: FinalColor = (SourceColor * SourceBlend) [operation] (DestColor * DestBlend)
|
||||
/// Maps to Direct3D D3DBLEND values.
|
||||
/// Режимы blend для материала. Управляют смешиванием source и destination цветов.
|
||||
/// Формула: FinalColor = SourceColor * SourceBlend [operation] DestColor * DestBlend.
|
||||
/// Соответствуют значениям Direct3D D3DBLEND.
|
||||
/// </summary>
|
||||
public enum BlendMode : byte
|
||||
{
|
||||
/// <summary>Blend factor is (0, 0, 0, 0) - results in black/transparent</summary>
|
||||
/// <summary>Blend factor = (0, 0, 0, 0).</summary>
|
||||
Zero = 1,
|
||||
|
||||
/// <summary>Blend factor is (1, 1, 1, 1) - uses full color value</summary>
|
||||
/// <summary>Blend factor = (1, 1, 1, 1).</summary>
|
||||
One = 2,
|
||||
|
||||
/// <summary>Blend factor is (Rs, Gs, Bs, As) - uses source color</summary>
|
||||
/// <summary>Blend factor = (Rs, Gs, Bs, As).</summary>
|
||||
SrcColor = 3,
|
||||
|
||||
/// <summary>Blend factor is (1-Rs, 1-Gs, 1-Bs, 1-As) - uses inverted source color</summary>
|
||||
/// <summary>Blend factor = (1-Rs, 1-Gs, 1-Bs, 1-As).</summary>
|
||||
InvSrcColor = 4,
|
||||
|
||||
/// <summary>Blend factor is (As, As, As, As) - uses source alpha for all channels (standard transparency)</summary>
|
||||
/// <summary>Blend factor = (As, As, As, As).</summary>
|
||||
SrcAlpha = 5,
|
||||
|
||||
/// <summary>Blend factor is (1-As, 1-As, 1-As, 1-As) - uses inverted source alpha</summary>
|
||||
/// <summary>Blend factor = (1-As, 1-As, 1-As, 1-As).</summary>
|
||||
InvSrcAlpha = 6,
|
||||
|
||||
/// <summary>Blend factor is (Ad, Ad, Ad, Ad) - uses destination alpha</summary>
|
||||
/// <summary>Blend factor = (Ad, Ad, Ad, Ad).</summary>
|
||||
DestAlpha = 7,
|
||||
|
||||
/// <summary>Blend factor is (1-Ad, 1-Ad, 1-Ad, 1-Ad) - uses inverted destination alpha</summary>
|
||||
/// <summary>Blend factor = (1-Ad, 1-Ad, 1-Ad, 1-Ad).</summary>
|
||||
InvDestAlpha = 8,
|
||||
|
||||
/// <summary>Blend factor is (Rd, Gd, Bd, Ad) - uses destination color</summary>
|
||||
/// <summary>Blend factor = (Rd, Gd, Bd, Ad).</summary>
|
||||
DestColor = 9,
|
||||
|
||||
/// <summary>Blend factor is (1-Rd, 1-Gd, 1-Bd, 1-Ad) - uses inverted destination color</summary>
|
||||
/// <summary>Blend factor = (1-Rd, 1-Gd, 1-Bd, 1-Ad).</summary>
|
||||
InvDestColor = 10,
|
||||
|
||||
/// <summary>Blend factor is (f, f, f, 1) where f = min(As, 1-Ad) - saturates source alpha</summary>
|
||||
/// <summary>Blend factor = (f, f, f, 1), где f = min(As, 1-Ad).</summary>
|
||||
SrcAlphaSat = 11,
|
||||
|
||||
/// <summary>Blend factor is (As, As, As, As) for both source and dest - obsolete in D3D9+</summary>
|
||||
/// <summary>Blend factor = (As, As, As, As) для source и destination; устарело в D3D9+.</summary>
|
||||
BothSrcAlpha = 12,
|
||||
|
||||
/// <summary>Blend factor is (1-As, 1-As, 1-As, 1-As) for both source and dest - obsolete in D3D9+</summary>
|
||||
/// <summary>Blend factor = (1-As, 1-As, 1-As, 1-As) для source и destination; устарело в D3D9+.</summary>
|
||||
BothInvSrcAlpha = 13,
|
||||
|
||||
/// <summary>Unknown or uninitialized blend mode (0xFF default value)</summary>
|
||||
/// <summary>Неизвестный или неинициализированный режим blend; значение по умолчанию 0xFF.</summary>
|
||||
Unknown = 0xFF
|
||||
}
|
||||
|
||||
public class MaterialStage
|
||||
{
|
||||
// === FILE READ ORDER (34 bytes per stage) ===
|
||||
// This matches the order bytes are read from the file (decompiled.c lines 159-217)
|
||||
// NOT the C struct memory layout (which is Diffuse, Ambient, Specular, Emissive)
|
||||
|
||||
// 1. Ambient Color (4 bytes, read first from file)
|
||||
public float AmbientR;
|
||||
public float AmbientG;
|
||||
public float AmbientB;
|
||||
public float AmbientA; // Scaled by 0.01 when read from file
|
||||
|
||||
// 2. Diffuse Color (4 bytes, read second from file)
|
||||
public float DiffuseR;
|
||||
public float DiffuseG;
|
||||
public float DiffuseB;
|
||||
public float DiffuseA;
|
||||
/// <summary>Стадия материала. Порядок полей соответствует порядку чтения из файла, а не C struct layout.</summary>
|
||||
/// <param name="AmbientR">Ambient R, читается первым цветовым блоком.</param>
|
||||
/// <param name="AmbientG">Ambient G, читается первым цветовым блоком.</param>
|
||||
/// <param name="AmbientB">Ambient B, читается первым цветовым блоком.</param>
|
||||
/// <param name="AmbientA">Ambient A, читается первым цветовым блоком и масштабируется на 0.01.</param>
|
||||
/// <param name="DiffuseR">Diffuse R, читается вторым цветовым блоком.</param>
|
||||
/// <param name="DiffuseG">Diffuse G, читается вторым цветовым блоком.</param>
|
||||
/// <param name="DiffuseB">Diffuse B, читается вторым цветовым блоком.</param>
|
||||
/// <param name="DiffuseA">Diffuse A, читается вторым цветовым блоком.</param>
|
||||
/// <param name="SpecularR">Specular R, читается третьим цветовым блоком.</param>
|
||||
/// <param name="SpecularG">Specular G, читается третьим цветовым блоком.</param>
|
||||
/// <param name="SpecularB">Specular B, читается третьим цветовым блоком.</param>
|
||||
/// <param name="SpecularA">Specular A, читается третьим цветовым блоком.</param>
|
||||
/// <param name="EmissiveR">Emissive R, читается четвертым цветовым блоком.</param>
|
||||
/// <param name="EmissiveG">Emissive G, читается четвертым цветовым блоком.</param>
|
||||
/// <param name="EmissiveB">Emissive B, читается четвертым цветовым блоком.</param>
|
||||
/// <param name="EmissiveA">Emissive A, читается четвертым цветовым блоком.</param>
|
||||
/// <param name="Power">Power, один байт -> float.</param>
|
||||
/// <param name="TextureStageIndex">Индекс texture stage. 255 = не задано/default, 0..47 = ссылка на texture stage.</param>
|
||||
/// <param name="TextureName">Имя текстуры, строка из 16 байт.</param>
|
||||
public record MaterialStage(
|
||||
float AmbientR,
|
||||
float AmbientG,
|
||||
float AmbientB,
|
||||
float AmbientA,
|
||||
float DiffuseR,
|
||||
float DiffuseG,
|
||||
float DiffuseB,
|
||||
float DiffuseA,
|
||||
float SpecularR,
|
||||
float SpecularG,
|
||||
float SpecularB,
|
||||
float SpecularA,
|
||||
float EmissiveR,
|
||||
float EmissiveG,
|
||||
float EmissiveB,
|
||||
float EmissiveA,
|
||||
float Power,
|
||||
int TextureStageIndex,
|
||||
string TextureName);
|
||||
|
||||
// 3. Specular Color (4 bytes, read third from file)
|
||||
public float SpecularR;
|
||||
public float SpecularG;
|
||||
public float SpecularB;
|
||||
public float SpecularA;
|
||||
|
||||
// 4. Emissive Color (4 bytes, read fourth from file)
|
||||
public float EmissiveR;
|
||||
public float EmissiveG;
|
||||
public float EmissiveB;
|
||||
public float EmissiveA;
|
||||
|
||||
// 5. Power (1 byte → float, read fifth from file)
|
||||
public float Power;
|
||||
|
||||
// 6. Texture Stage Index (1 byte, read sixth from file)
|
||||
// 255 = not set/default, 0-47 = reference to specific texture stage
|
||||
public int TextureStageIndex;
|
||||
|
||||
// 7. Texture Name (16 bytes, read seventh from file)
|
||||
public string TextureName { get; set; }
|
||||
}
|
||||
|
||||
public class MaterialAnimation
|
||||
{
|
||||
// === File Read Order ===
|
||||
|
||||
// Combined field (4 bytes): bits 3-31 = Target, bits 0-2 = LoopMode
|
||||
public AnimationTarget Target;
|
||||
public AnimationLoopMode LoopMode;
|
||||
|
||||
// Key count (2 bytes), then keys
|
||||
public List<AnimKey> Keys { get; set; } = new();
|
||||
|
||||
// Cached description for UI (computed once during parsing)
|
||||
public string TargetDescription { get; set; } = string.Empty;
|
||||
}
|
||||
/// <summary>Анимация материала.</summary>
|
||||
/// <param name="Target">Целевые компоненты анимации: биты 3..31 combined field.</param>
|
||||
/// <param name="LoopMode">Режим повтора: биты 0..2 combined field.</param>
|
||||
/// <param name="Keys">Ключи анимации.</param>
|
||||
/// <param name="TargetDescription">Кэшированное описание target для UI.</param>
|
||||
public record MaterialAnimation(
|
||||
AnimationTarget Target,
|
||||
AnimationLoopMode LoopMode,
|
||||
List<AnimKey> Keys,
|
||||
string TargetDescription);
|
||||
|
||||
|
||||
[Flags]
|
||||
public enum AnimationTarget : int
|
||||
{
|
||||
// NOTE: This is a BITSET (flags enum). Multiple flags can be combined.
|
||||
// When a flag is SET, that component is INTERPOLATED between stages.
|
||||
// When a flag is NOT SET, that component is COPIED from the source stage (no interpolation).
|
||||
// If ALL flags are 0, the ENTIRE stage is copied without any interpolation.
|
||||
// Это bitset: несколько флагов могут комбинироваться.
|
||||
// Установленный флаг означает интерполяцию компонента между стадиями.
|
||||
// Неустановленный флаг означает копирование компонента из исходной стадии.
|
||||
// Если все флаги равны 0, вся стадия копируется без интерполяции.
|
||||
|
||||
Ambient = 1, // 0x01 - Interpolates Ambient RGB (Interpolate.c lines 23-37)
|
||||
Diffuse = 2, // 0x02 - Interpolates Diffuse RGB (Interpolate.c lines 7-21)
|
||||
Specular = 4, // 0x04 - Interpolates Specular RGB (Interpolate.c lines 39-53)
|
||||
Emissive = 8, // 0x08 - Interpolates Emissive RGB (Interpolate.c lines 55-69)
|
||||
Power = 16 // 0x10 - Interpolates Ambient.A and sets Power (Interpolate.c lines 71-76)
|
||||
Ambient = 1, // 0x01: интерполирует Ambient RGB.
|
||||
Diffuse = 2, // 0x02: интерполирует Diffuse RGB.
|
||||
Specular = 4, // 0x04: интерполирует Specular RGB.
|
||||
Emissive = 8, // 0x08: интерполирует Emissive RGB.
|
||||
Power = 16 // 0x10: интерполирует Ambient.A и задает Power.
|
||||
}
|
||||
|
||||
|
||||
@@ -158,10 +154,8 @@ public enum AnimationLoopMode : int
|
||||
Random = 3
|
||||
}
|
||||
|
||||
public struct AnimKey
|
||||
{
|
||||
// === File Read Order (6 bytes per key) ===
|
||||
public ushort StageIndex; // Read first
|
||||
public ushort DurationMs; // Read second
|
||||
public ushort InterpolationCurve; // Read third - Always 0 (linear interpolation) in all 1848 keys
|
||||
}
|
||||
/// <summary>Ключ анимации материала (length = 6).</summary>
|
||||
/// <param name="StageIndex">[0x00..0x02] Индекс стадии материала.</param>
|
||||
/// <param name="DurationMs">[0x02..0x04] Длительность в миллисекундах.</param>
|
||||
/// <param name="InterpolationCurve">[0x04..0x06] Кривая интерполяции. В исследованных данных всегда 0.</param>
|
||||
public readonly record struct AnimKey(ushort StageIndex, ushort DurationMs, ushort InterpolationCurve);
|
||||
|
||||
Reference in New Issue
Block a user