mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2026-08-15 02:57:49 +04:00
refactorings
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
namespace Common;
|
||||
|
||||
public record Sphere(float CenterX, float CenterY, float CenterZ, float Radius);
|
||||
@@ -6,6 +6,7 @@ namespace ParkanPlayground;
|
||||
/// <summary>
|
||||
/// MSH-компонент 0x01: таблица узлов модели.
|
||||
/// </summary>
|
||||
/// У ландшафта magic1 - grid_x_count
|
||||
public static class Msh0x01
|
||||
{
|
||||
public static Msh0x01Component ReadComponent(FileStream mshFs, NResArchive archive)
|
||||
@@ -50,7 +51,7 @@ public static class Msh0x01
|
||||
|
||||
elements.Add(new Node(
|
||||
rawBytes,
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(baseOffset)),
|
||||
(NodeFlags)BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(baseOffset)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(baseOffset + 2)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(baseOffset + 4)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(baseOffset + 6)),
|
||||
@@ -67,23 +68,28 @@ public static class Msh0x01
|
||||
|
||||
/// <summary>Узел 0x01.</summary>
|
||||
/// <param name="RawBytes">Сырые байты узла (length = attr3). Нужны для copy-through редких вариантов, например attr3 = 24.</param>
|
||||
/// <param name="Header0">[0x00..0x02] Заголовочное слово узла</param>
|
||||
/// <param name="ParentOrLink">[0x02..0x04] Индекс родителя или связанного узла</param>
|
||||
/// <param name="Flags">[0x00..0x02] Флаги узла</param>
|
||||
/// <param name="ParentOrLink_possibly_0x02_index">[0x02..0x04] Индекс родителя или связанного узла</param>
|
||||
/// <param name="AnimMapStart">[0x04..0x06] Начало блока в карте анимации Msh0x13 или 0xFFFF</param>
|
||||
/// <param name="FallbackKey">[0x06..0x08] Индекс fallback-ключа в пуле ключей Msh0x08</param>
|
||||
/// <param name="SlotIndex">[0x08..0x26] Индексы slot в Msh0x02 по формуле lod * 5 + group</param>
|
||||
/// <param name="Msh02SlotIndicesByLodAndGroupSlotIndex">[0x08..0x26] Индексы slot в Msh0x02 по формуле lod * 5 + group</param>
|
||||
public record Node(
|
||||
byte[] RawBytes,
|
||||
ushort Header0,
|
||||
ushort ParentOrLink,
|
||||
NodeFlags Flags,
|
||||
ushort ParentOrLink_possibly_0x02_index,
|
||||
ushort AnimMapStart,
|
||||
ushort FallbackKey,
|
||||
ushort[] SlotIndex)
|
||||
ushort[] Msh02SlotIndicesByLodAndGroupSlotIndex)
|
||||
{
|
||||
public ushort ResolveSlotIndex(int lod, int group = 0)
|
||||
{
|
||||
var index = lod * 5 + group;
|
||||
return index >= 0 && index < SlotIndex.Length ? SlotIndex[index] : ushort.MaxValue;
|
||||
return index >= 0 && index < Msh02SlotIndicesByLodAndGroupSlotIndex.Length ? Msh02SlotIndicesByLodAndGroupSlotIndex[index] : ushort.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum NodeFlags
|
||||
{
|
||||
MSH01_NODE_FLAG_NO_SHADOW = 0x40
|
||||
}
|
||||
+40
-35
@@ -100,44 +100,49 @@ public static class Msh0x02
|
||||
|
||||
var elements = new List<Slot>();
|
||||
var skippedHeader = data.AsSpan(0x8c);
|
||||
var slotCount = skippedHeader.Length / 68;
|
||||
var slotCount = skippedHeader.Length / 0x44;
|
||||
|
||||
for (var i = 0; i < slotCount; i++)
|
||||
{
|
||||
var baseOffset = 68 * i;
|
||||
var opaque = new uint[5];
|
||||
var baseOffset = 0x44 * i;
|
||||
|
||||
var opaque = new uint[3];
|
||||
for (var opaqueIndex = 0; opaqueIndex < opaque.Length; opaqueIndex++)
|
||||
{
|
||||
opaque[opaqueIndex] =
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(skippedHeader.Slice(baseOffset + 48 + opaqueIndex * 4));
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(
|
||||
skippedHeader.Slice(baseOffset + 0x38 + opaqueIndex * 4)
|
||||
);
|
||||
}
|
||||
|
||||
elements.Add(new Slot(
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 0)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 2)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 4)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 6)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 0x00)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 0x02)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 0x04)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(skippedHeader.Slice(baseOffset + 0x06)),
|
||||
new Vector3(
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 8)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 12)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 16))
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x08)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x0c)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x10))
|
||||
),
|
||||
new Vector3(
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 20)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 24)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 28))
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x14)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x18)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x1c))
|
||||
),
|
||||
new Vector3(
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 32)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 36)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 40))
|
||||
),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 44)),
|
||||
opaque));
|
||||
|
||||
new Sphere(
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x20)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x24)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x28)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x2c))),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x30)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(skippedHeader.Slice(baseOffset + 0x34)),
|
||||
opaque
|
||||
));
|
||||
}
|
||||
|
||||
return new Msh0x02Component(
|
||||
new Msh02Header(bb, center, centerW, bottom, top, xyRadius),
|
||||
new Msh02Header(bb, new Sphere(center.X, center.Y, center.Z, centerW), bottom, top, xyRadius),
|
||||
elements);
|
||||
}
|
||||
|
||||
@@ -148,38 +153,38 @@ public static class Msh0x02
|
||||
|
||||
/// <summary>Заголовок 0x02 (length = 0x8C).</summary>
|
||||
/// <param name="BoundingBox">[0x00..0x60] Bounding box из 8 точек.</param>
|
||||
/// <param name="Center">[0x60..0x6C] Центральная точка.</param>
|
||||
/// <param name="CenterW">[0x6C..0x70] W-компонента центра.</param>
|
||||
/// <param name="BoundingSphere">[0x60..0x70] Bounding sphere.</param>
|
||||
/// <param name="Bottom">[0x70..0x7C] Нижняя точка.</param>
|
||||
/// <param name="Top">[0x7C..0x88] Верхняя точка.</param>
|
||||
/// <param name="XYRadius">[0x88..0x8C] Радиус в плоскости XY.</param>
|
||||
public record class Msh02Header(
|
||||
BoundingBox BoundingBox,
|
||||
Vector3 Center,
|
||||
float CenterW,
|
||||
Sphere BoundingSphere,
|
||||
Vector3 Bottom,
|
||||
Vector3 Top,
|
||||
float XYRadius);
|
||||
|
||||
/// <summary>Slot 0x02 (length = 0x44).</summary>
|
||||
/// <summary>Geometry Slot 0x02 (length = 0x44).</summary>
|
||||
/// <param name="TriStart">[0x00..0x02] Первый triangle descriptor в Msh0x07. Для terrain-гипотезы может быть диапазоном Msh0x15.</param>
|
||||
/// <param name="TriCount">[0x02..0x04] Количество triangle descriptor в Msh0x07. Для terrain-гипотезы может быть count для Msh0x15.</param>
|
||||
/// <param name="BatchStart">[0x04..0x06] Первый batch в таблице 0x0D.</param>
|
||||
/// <param name="BatchCount">[0x06..0x08] Количество batch в таблице 0x0D</param>
|
||||
/// <param name="LocalMinimum">[0x08..0x14] Минимум локального AABB</param>
|
||||
/// <param name="LocalMaximum">[0x14..0x20] Максимум локального AABB</param>
|
||||
/// <param name="Center">[0x20..0x2C] Центр bounding sphere</param>
|
||||
/// <param name="SphereRadius">[0x2C..0x30] Радиус bounding sphere</param>
|
||||
/// <param name="Opaque">[0x30..0x44] Пять opaque dword</param>
|
||||
public record class Slot(
|
||||
/// <param name="BoundingSphere">[0x20..0x30] Bounding sphere</param>
|
||||
/// <param name="BaseXyArea">[0x30..0x34] Базовая XY-площадь / footprint area до масштабирования.</param>
|
||||
/// <param name="BaseVolume">[0x34..0x38] Базовый объём до масштабирования.</param>
|
||||
/// <param name="Opaque">[0x38..0x44] 3 opaque dword</param>
|
||||
public record Slot(
|
||||
ushort TriStart,
|
||||
ushort TriCount,
|
||||
ushort BatchStart,
|
||||
ushort BatchCount,
|
||||
Vector3 LocalMinimum,
|
||||
Vector3 LocalMaximum,
|
||||
Vector3 Center,
|
||||
float SphereRadius,
|
||||
Sphere BoundingSphere,
|
||||
float BaseXyArea,
|
||||
float BaseVolume,
|
||||
uint[] Opaque);
|
||||
|
||||
/// <summary>Bounding box заголовка: 8 точек по 3 float (length = 0x60).</summary>
|
||||
@@ -191,7 +196,7 @@ public static class Msh0x02
|
||||
/// <param name="TopFrontRight">[0x3C..0x48] Верхняя передняя правая точка.</param>
|
||||
/// <param name="TopBackRight">[0x48..0x54] Верхняя задняя правая точка.</param>
|
||||
/// <param name="TopBackLeft">[0x54..0x60] Верхняя задняя левая точка.</param>
|
||||
public record class BoundingBox(
|
||||
public record BoundingBox(
|
||||
Vector3 BottomFrontLeft,
|
||||
Vector3 BottomFrontRight,
|
||||
Vector3 BottomBackRight,
|
||||
|
||||
+38
-22
@@ -18,32 +18,36 @@ public static class Msh0x15
|
||||
throw new Exception("Archive doesn't contain file (15)");
|
||||
}
|
||||
|
||||
if (entry.ElementSize != 28)
|
||||
if (entry.ElementSize != 0x1C)
|
||||
{
|
||||
throw new Exception("Terrain triangle component (0x15) element size is not 28");
|
||||
}
|
||||
|
||||
if (entry.FileLength % entry.ElementSize != 0)
|
||||
if (entry.FileLength % entry.ElementSize != 0x0)
|
||||
{
|
||||
throw new Exception("Terrain triangle component (0x15) 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);
|
||||
mshFs.ReadExactly(data, 0x0, data.Length);
|
||||
|
||||
var elementBytes = data.Chunk(28);
|
||||
var elementBytes = data.Chunk(0x1C);
|
||||
|
||||
var elements = elementBytes.Select(x => new TerrainTriangle(
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(0)),
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(4)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(8)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(10)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(12)),
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(14)),
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(18)),
|
||||
BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(22)),
|
||||
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(26)))).ToList();
|
||||
Flags: (TerrainTriangleFlags)BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(0x0)),
|
||||
MaterialData: BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(0x4)),
|
||||
Vertex1Index: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x8)),
|
||||
Vertex2Index: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0xA)),
|
||||
Vertex3Index: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0xC)),
|
||||
Neighbor0: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x0E)),
|
||||
Neighbor1: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x10)),
|
||||
Neighbor2: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x12)),
|
||||
NormalX: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x14)),
|
||||
NormalY: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x16)),
|
||||
NormalZ: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x18)),
|
||||
PackedEdgeOrSelector: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x1A))))
|
||||
.ToList();
|
||||
|
||||
return elements;
|
||||
}
|
||||
@@ -54,20 +58,32 @@ public static class Msh0x15
|
||||
/// <param name="Vertex1Index">[0x08..0x0A] Индекс первой вершины в position stream Msh0x03</param>
|
||||
/// <param name="Vertex2Index">[0x0A..0x0C] Индекс второй вершины в position stream Msh0x03</param>
|
||||
/// <param name="Vertex3Index">[0x0C..0x0E] Индекс третьей вершины в position stream Msh0x03</param>
|
||||
/// <param name="Opaque0E">[0x0E..0x12] Opaque поле</param>
|
||||
/// <param name="Opaque12">[0x12..0x16] Opaque поле</param>
|
||||
/// <param name="Opaque16">[0x16..0x1A] Opaque поле</param>
|
||||
/// <param name="Opaque1A">[0x1A..0x1C] Opaque поле</param>
|
||||
/// <param name="Neighbor0">[0x0E..0x10] Сосед 0</param>
|
||||
/// <param name="Neighbor1">[0x10..0x12] Сосед 1</param>
|
||||
/// <param name="Neighbor2">[0x12..0x14] Сосед 2</param>
|
||||
/// <param name="NormalX">[0x14..0x16] Направление нормали</param>
|
||||
/// <param name="NormalY">[0x16..0x18] Направление нормали</param>
|
||||
/// <param name="NormalZ">[0x18..0x1A] Направление нормали</param>
|
||||
/// <param name="PackedEdgeOrSelector">[0x1A..0x1C] TODO</param>
|
||||
public readonly record struct TerrainTriangle(
|
||||
uint Flags,
|
||||
TerrainTriangleFlags Flags,
|
||||
|
||||
uint MaterialData,
|
||||
ushort Vertex1Index,
|
||||
ushort Vertex2Index,
|
||||
ushort Vertex3Index,
|
||||
|
||||
uint Opaque0E,
|
||||
uint Opaque12,
|
||||
uint Opaque16,
|
||||
ushort Opaque1A);
|
||||
ushort Neighbor0,
|
||||
ushort Neighbor1,
|
||||
ushort Neighbor2,
|
||||
ushort NormalX,
|
||||
ushort NormalY,
|
||||
ushort NormalZ,
|
||||
ushort PackedEdgeOrSelector);
|
||||
}
|
||||
|
||||
public enum TerrainTriangleFlags {
|
||||
MSH15_FLAG_REFLECTIVE_SURFACE = 0x20000,
|
||||
MSH15_FLAG_HAS_MICROTEXTURE = 0x400,
|
||||
MSH15_FLAG_DISABLE_BACKFACE_TEST = 0x8
|
||||
}
|
||||
Reference in New Issue
Block a user