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="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="SelectorPacked">[0x0E..0x10] Packed selectors. Старое наблюдение: 3 трактуется как 0xFFFF.</param>
/// <param name="SelectorPacked">[0x0E..0x10] Packed selectors. 3 трактуется как 0xFFFF.</param>
public readonly record struct TriangleDescriptor(
TriangleFlags Flags,
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="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="IndexCount">[0x08..0x0A] Количество индексов из component 0x06.</param>
/// <param name="IndexStart">[0x0A..0x0E] Первый индекс в component 0x06.</param>
/// <param name="VertexCount">[0x0E..0x10] Количество вершин для render primitive.</param>
/// <param name="BaseVertex">[0x10..0x14] Base vertex в vertex streams, включая position stream 0x03.</param>
/// <param name="IndexCount0x06">[0x08..0x0A] Количество индексов из component 0x06.</param>
/// <param name="IndexStart0x06">[0x0A..0x0E] Первый индекс в component 0x06.</param>
/// <param name="VertexCount0x03">[0x0E..0x10] Количество вершин для render primitive.</param>
/// <param name="BaseVertex0x03">[0x10..0x14] Base vertex в vertex streams, включая position stream 0x03.</param>
public readonly record struct Batch(
BatchFlags Flags,
ushort MaterialIndex,
ushort Opaque04,
ushort LocalBatchIndex,
ushort IndexCount,
uint IndexStart,
ushort VertexCount,
uint BaseVertex);
ushort IndexCount0x06,
uint IndexStart0x06,
ushort VertexCount0x03,
uint BaseVertex0x03);
}
[Flags]
-17
View File
@@ -58,23 +58,6 @@ public static class Msh0x14
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>
+2 -1
View File
@@ -82,7 +82,8 @@ public static class Msh0x15
ushort PackedEdgeOrSelector);
}
public enum TerrainTriangleFlags {
public enum TerrainTriangleFlags : uint
{
MSH15_FLAG_REFLECTIVE_SURFACE = 0x20000,
MSH15_FLAG_HAS_MICROTEXTURE = 0x400,
MSH15_FLAG_DISABLE_BACKFACE_TEST = 0x8
+9 -9
View File
@@ -151,22 +151,22 @@ public sealed class MshConverter
{
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++;
continue;
}
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 v2 = checked((int)batch.BaseVertex + indices[indexBase + 1]);
var v3 = checked((int)batch.BaseVertex + indices[indexBase + 2]);
var v1 = checked((int)batch.BaseVertex0x03 + indices[indexBase + 0]);
var v2 = checked((int)batch.BaseVertex0x03 + indices[indexBase + 1]);
var v3 = checked((int)batch.BaseVertex0x03 + indices[indexBase + 2]);
if (!IsValidTriangle(vertices.Count, v1, v2, v3))
{
@@ -178,9 +178,9 @@ public sealed class MshConverter
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");
}
}
}