msh reverse

This commit is contained in:
bird_egop
2026-05-13 02:53:34 +03:00
parent 60e4fcf004
commit 9e651a60ce
5 changed files with 20 additions and 36 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ public static class Msh0x07
/// <param name="PackedNormalX">[0x08..0x0A] Packed normal X, int16, scale = 1 / 32767.</param> /// <param name="PackedNormalX">[0x08..0x0A] Packed normal X, int16, scale = 1 / 32767.</param>
/// <param name="PackedNormalY">[0x0A..0x0C] Packed normal Y, int16, scale = 1 / 32767.</param> /// <param name="PackedNormalY">[0x0A..0x0C] Packed normal Y, int16, scale = 1 / 32767.</param>
/// <param name="PackedNormalZ">[0x0C..0x0E] Packed normal Z, int16, scale = 1 / 32767.</param> /// <param name="PackedNormalZ">[0x0C..0x0E] Packed normal Z, int16, scale = 1 / 32767.</param>
/// <param name="SelectorPacked">[0x0E..0x10] Packed selectors. Старое наблюдение: 3 трактуется как 0xFFFF.</param> /// <param name="SelectorPacked">[0x0E..0x10] Packed selectors. 3 трактуется как 0xFFFF.</param>
public readonly record struct TriangleDescriptor( public readonly record struct TriangleDescriptor(
TriangleFlags Flags, TriangleFlags Flags,
ushort Link0, ushort Link0,
+8 -8
View File
@@ -54,19 +54,19 @@ public static class Msh0x0D
/// <param name="MaterialIndex">[0x02..0x04] Индекс material slot. Может быть overridden CAniMesh::forced_material_index.</param> /// <param name="MaterialIndex">[0x02..0x04] Индекс material slot. Может быть overridden CAniMesh::forced_material_index.</param>
/// <param name="Opaque04">[0x04..0x06] Opaque. В GetBatchRenderData попадает в packed field вместе с material/lightmap данными.</param> /// <param name="Opaque04">[0x04..0x06] Opaque. В GetBatchRenderData попадает в packed field вместе с material/lightmap данными.</param>
/// <param name="LocalBatchIndex">[0x06..0x08] Локальный batch index. В GetBatchRenderData складывается с MSH_piece.local_index_base / batch base.</param> /// <param name="LocalBatchIndex">[0x06..0x08] Локальный batch index. В GetBatchRenderData складывается с MSH_piece.local_index_base / batch base.</param>
/// <param name="IndexCount">[0x08..0x0A] Количество индексов из component 0x06.</param> /// <param name="IndexCount0x06">[0x08..0x0A] Количество индексов из component 0x06.</param>
/// <param name="IndexStart">[0x0A..0x0E] Первый индекс в component 0x06.</param> /// <param name="IndexStart0x06">[0x0A..0x0E] Первый индекс в component 0x06.</param>
/// <param name="VertexCount">[0x0E..0x10] Количество вершин для render primitive.</param> /// <param name="VertexCount0x03">[0x0E..0x10] Количество вершин для render primitive.</param>
/// <param name="BaseVertex">[0x10..0x14] Base vertex в vertex streams, включая position stream 0x03.</param> /// <param name="BaseVertex0x03">[0x10..0x14] Base vertex в vertex streams, включая position stream 0x03.</param>
public readonly record struct Batch( public readonly record struct Batch(
BatchFlags Flags, BatchFlags Flags,
ushort MaterialIndex, ushort MaterialIndex,
ushort Opaque04, ushort Opaque04,
ushort LocalBatchIndex, ushort LocalBatchIndex,
ushort IndexCount, ushort IndexCount0x06,
uint IndexStart, uint IndexStart0x06,
ushort VertexCount, ushort VertexCount0x03,
uint BaseVertex); uint BaseVertex0x03);
} }
[Flags] [Flags]
-17
View File
@@ -58,23 +58,6 @@ public static class Msh0x14
return elements; 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> /// <summary>Light/probe entry 0x14.</summary>
/// <param name="PieceIndex">[0x00..0x04] Индекс MSH_piece, чью world matrix используют для transform position/direction.</param> /// <param name="PieceIndex">[0x00..0x04] Индекс MSH_piece, чью world matrix используют для transform position/direction.</param>
/// <param name="LocalPosition">[0x04..0x10] Локальная позиция источника/probe относительно PieceIndex.</param> /// <param name="LocalPosition">[0x04..0x10] Локальная позиция источника/probe относительно PieceIndex.</param>
+2 -1
View File
@@ -82,7 +82,8 @@ public static class Msh0x15
ushort PackedEdgeOrSelector); ushort PackedEdgeOrSelector);
} }
public enum TerrainTriangleFlags { public enum TerrainTriangleFlags : uint
{
MSH15_FLAG_REFLECTIVE_SURFACE = 0x20000, MSH15_FLAG_REFLECTIVE_SURFACE = 0x20000,
MSH15_FLAG_HAS_MICROTEXTURE = 0x400, MSH15_FLAG_HAS_MICROTEXTURE = 0x400,
MSH15_FLAG_DISABLE_BACKFACE_TEST = 0x8 MSH15_FLAG_DISABLE_BACKFACE_TEST = 0x8
+9 -9
View File
@@ -151,22 +151,22 @@ public sealed class MshConverter
{ {
var batch = batches[batchIndex]; var batch = batches[batchIndex];
if (batch.IndexStart + batch.IndexCount > indices.Count) if (batch.IndexStart0x06 + batch.IndexCount0x06 > indices.Count)
{ {
Warn($"Piece {pieceIndex}, batch {batchIndex}: index range {batch.IndexStart}:{batch.IndexCount} out of range"); Warn($"Piece {pieceIndex}, batch {batchIndex}: index range {batch.IndexStart0x06}:{batch.IndexCount0x06} out of range");
skippedBatches++; skippedBatches++;
continue; continue;
} }
writer.WriteLine($"# batch {batchIndex}, material {batch.MaterialIndex}, flags {batch.Flags}"); writer.WriteLine($"# batch {batchIndex}, material {batch.MaterialIndex}, flags {batch.Flags}");
for (var i = 0; i + 2 < batch.IndexCount; i += 3) for (var i = 0; i + 2 < batch.IndexCount0x06; i += 3)
{ {
var indexBase = (int)batch.IndexStart + i; var indexBase = (int)batch.IndexStart0x06 + i;
var v1 = checked((int)batch.BaseVertex + indices[indexBase + 0]); var v1 = checked((int)batch.BaseVertex0x03 + indices[indexBase + 0]);
var v2 = checked((int)batch.BaseVertex + indices[indexBase + 1]); var v2 = checked((int)batch.BaseVertex0x03 + indices[indexBase + 1]);
var v3 = checked((int)batch.BaseVertex + indices[indexBase + 2]); var v3 = checked((int)batch.BaseVertex0x03 + indices[indexBase + 2]);
if (!IsValidTriangle(vertices.Count, v1, v2, v3)) if (!IsValidTriangle(vertices.Count, v1, v2, v3))
{ {
@@ -178,9 +178,9 @@ public sealed class MshConverter
exportedFaces++; exportedFaces++;
} }
if (batch.IndexCount % 3 != 0) if (batch.IndexCount0x06 % 3 != 0)
{ {
Warn($"Piece {pieceIndex}, batch {batchIndex}: index count {batch.IndexCount} is not divisible by 3"); Warn($"Piece {pieceIndex}, batch {batchIndex}: index count {batch.IndexCount0x06} is not divisible by 3");
} }
} }
} }