mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2026-08-15 02:57:49 +04:00
msh 0x14 and different flags
This commit is contained in:
@@ -119,7 +119,7 @@ public static class NResParser
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!formattable)
|
if (formattable)
|
||||||
{
|
{
|
||||||
return Encoding.ASCII.GetString(bytes);
|
return Encoding.ASCII.GetString(bytes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,10 @@ public static class Msh0x01
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum NodeFlags
|
public enum NodeFlags : ushort
|
||||||
{
|
{
|
||||||
|
// very uncertain
|
||||||
|
MSH01_NODE_FLAG_UNKNOWN_SKIP_RECURSE_0x04 = 0x4,
|
||||||
|
MSH01_NODE_FLAG_STOP_CHILD_TRAVERSAL = 0x10,
|
||||||
MSH01_NODE_FLAG_NO_SHADOW = 0x40
|
MSH01_NODE_FLAG_NO_SHADOW = 0x40
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ public static class Msh0x0D
|
|||||||
var elementBytes = data.Chunk(ElementSize);
|
var elementBytes = data.Chunk(ElementSize);
|
||||||
|
|
||||||
var elements = elementBytes.Select(x => new Batch(
|
var elements = elementBytes.Select(x => new Batch(
|
||||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0)),
|
(BatchFlags)BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0)),
|
||||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(2)),
|
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(2)),
|
||||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(4)),
|
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(4)),
|
||||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(6)),
|
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(6)),
|
||||||
@@ -59,7 +59,7 @@ public static class Msh0x0D
|
|||||||
/// <param name="Opaque14">[0x0E..0x10] Opaque поле. Старое локальное имя: CountOf03; не подтверждено.</param>
|
/// <param name="Opaque14">[0x0E..0x10] Opaque поле. Старое локальное имя: CountOf03; не подтверждено.</param>
|
||||||
/// <param name="BaseVertex">[0x10..0x14] Базовая вершина в position stream 0x03. У FParkan: baseVertex.</param>
|
/// <param name="BaseVertex">[0x10..0x14] Базовая вершина в position stream 0x03. У FParkan: baseVertex.</param>
|
||||||
public readonly record struct Batch(
|
public readonly record struct Batch(
|
||||||
ushort BatchFlags,
|
BatchFlags BatchFlags,
|
||||||
ushort MaterialIndex,
|
ushort MaterialIndex,
|
||||||
ushort Opaque4,
|
ushort Opaque4,
|
||||||
ushort Opaque6,
|
ushort Opaque6,
|
||||||
@@ -68,3 +68,16 @@ public static class Msh0x0D
|
|||||||
ushort Opaque14,
|
ushort Opaque14,
|
||||||
uint BaseVertex);
|
uint BaseVertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum BatchFlags : ushort
|
||||||
|
{
|
||||||
|
MSH0D_BATCH_SPECIAL_SUBMIT = 1,
|
||||||
|
MSH0D_BATCH_INTERSECT_TWO_SIDED = 2,
|
||||||
|
MSH0D_BATCH_SPECIAL_PASS_OR_CONDITIONAL = 5,
|
||||||
|
MSH0D_BATCH_USE_MATERIAL_PHASE = 8,
|
||||||
|
MSH0D_BATCH_SUPPRESS_BATCH = 32,
|
||||||
|
MSH0D_BATCH_FORCE_PASS2 = 256,
|
||||||
|
MSH0D_BATCH_EMULATE_POINT_LIGHTS = 2048,
|
||||||
|
MSH0D_BATCH_HAS_LIGHTMAP_OR_TEXCOORD1 = 8192,
|
||||||
|
MSH0D_BATCH_FACING_TEST_EARLY_OUT = 16384,
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Numerics;
|
||||||
|
using NResLib;
|
||||||
|
|
||||||
|
namespace ParkanPlayground;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MSH-компонент 0x14: таблица локальных directional/probe light entries.
|
||||||
|
/// Используется CAniMesh_IJointMesh::SampleMsh14Lights / AccumulateMsh14LightContributions.
|
||||||
|
/// </summary>
|
||||||
|
public static class Msh0x14
|
||||||
|
{
|
||||||
|
public const int ElementSize = 48;
|
||||||
|
|
||||||
|
public static List<LightProbe> ReadComponent(
|
||||||
|
FileStream mshFs, NResArchive archive)
|
||||||
|
{
|
||||||
|
var entry = archive.Files.FirstOrDefault(x => x.FileType == "14 00 00 00");
|
||||||
|
|
||||||
|
if (entry is null)
|
||||||
|
{
|
||||||
|
throw new Exception("Archive doesn't contain file (14)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.ElementSize != ElementSize)
|
||||||
|
{
|
||||||
|
throw new Exception("Light probe component (0x14) element size is not 48");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry.FileLength % entry.ElementSize != 0)
|
||||||
|
{
|
||||||
|
throw new Exception("Light probe component (0x14) payload size is not divisible by element size");
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = new byte[entry.FileLength];
|
||||||
|
mshFs.Seek(entry.OffsetInFile, SeekOrigin.Begin);
|
||||||
|
mshFs.ReadExactly(data, 0, data.Length);
|
||||||
|
|
||||||
|
var elementBytes = data.Chunk(ElementSize);
|
||||||
|
|
||||||
|
var elements = elementBytes.Select(x => new LightProbe(
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x00)),
|
||||||
|
new Vector3(
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x04)),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x08)),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x0C))),
|
||||||
|
new Vector3(
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x10)),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x14)),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x18))),
|
||||||
|
new Vector4(
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x1C)),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x20)),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x24)),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x28))),
|
||||||
|
BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x2C)))).ToList();
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component 0x14 is optional in the game reader.
|
||||||
|
/// This helper mirrors that behavior and returns an empty list when absent.
|
||||||
|
/// </summary>
|
||||||
|
public static List<LightProbe> ReadComponentOrEmpty(
|
||||||
|
FileStream mshFs, NResArchive archive)
|
||||||
|
{
|
||||||
|
var entry = archive.Files.FirstOrDefault(x => x.FileType == "14 00 00 00");
|
||||||
|
|
||||||
|
if (entry is null)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadComponent(mshFs, archive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Light/probe entry 0x14.</summary>
|
||||||
|
/// <param name="PieceIndex">[0x00..0x04] Индекс MSH_piece, чью world matrix используют для transform position/direction.</param>
|
||||||
|
/// <param name="LocalPosition">[0x04..0x10] Локальная позиция источника/probe относительно PieceIndex.</param>
|
||||||
|
/// <param name="LocalDirection">[0x10..0x1C] Локальное направление, transform direction через matrix piece.</param>
|
||||||
|
/// <param name="Color">[0x1C..0x2C] RGBA/intensity color multiplier. В коде умножается на вычисленный strength.</param>
|
||||||
|
/// <param name="Intensity">[0x2C..0x30] Scalar intensity. В коде участвует как * Intensity * 3.0.</param>
|
||||||
|
public readonly record struct LightProbe(
|
||||||
|
int PieceIndex,
|
||||||
|
Vector3 LocalPosition,
|
||||||
|
Vector3 LocalDirection,
|
||||||
|
Vector4 Color,
|
||||||
|
int Intensity);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user