2025-08-26 04:29:30 +03:00
|
|
|
using System.Buffers.Binary;
|
|
|
|
|
using NResLib;
|
|
|
|
|
|
2026-05-17 15:40:16 +03:00
|
|
|
namespace MshLib;
|
2025-08-26 04:29:30 +03:00
|
|
|
|
2026-05-09 20:36:43 +03:00
|
|
|
/// <summary>
|
2026-05-12 01:19:19 +03:00
|
|
|
/// MSH-компонент 0x06: индексный буфер.
|
|
|
|
|
/// Используется batch-ами 0x0D через Batch.IndexStart / Batch.IndexCount.
|
|
|
|
|
/// Индексы являются ushort и обычно читаются тройками как triangle indices.
|
2026-05-09 20:36:43 +03:00
|
|
|
/// </summary>
|
|
|
|
|
public static class Msh0x06
|
2025-08-26 04:29:30 +03:00
|
|
|
{
|
2026-05-12 01:19:19 +03:00
|
|
|
public const int ElementSize = 2;
|
|
|
|
|
|
|
|
|
|
public static List<ushort> ReadComponent(FileStream mshFs, NResArchive archive)
|
2025-08-26 04:29:30 +03:00
|
|
|
{
|
|
|
|
|
var entry = archive.Files.FirstOrDefault(x => x.FileType == "06 00 00 00");
|
|
|
|
|
|
|
|
|
|
if (entry is null)
|
|
|
|
|
{
|
2026-05-12 01:19:19 +03:00
|
|
|
throw new Exception("Archive doesn't contain index buffer component (0x06)");
|
2025-08-26 04:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 01:19:19 +03:00
|
|
|
if (entry.ElementSize != ElementSize)
|
2026-05-09 20:36:43 +03:00
|
|
|
{
|
|
|
|
|
throw new Exception("Index buffer component (0x06) element size is not 2");
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 01:19:19 +03:00
|
|
|
if (entry.FileLength % ElementSize != 0)
|
2026-05-09 20:36:43 +03:00
|
|
|
{
|
2026-05-12 01:19:19 +03:00
|
|
|
throw new Exception("Index buffer component (0x06) payload size is not divisible by 2");
|
2026-05-09 20:36:43 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var data = new byte[entry.FileLength];
|
2026-05-12 01:19:19 +03:00
|
|
|
|
2025-08-26 04:29:30 +03:00
|
|
|
mshFs.Seek(entry.OffsetInFile, SeekOrigin.Begin);
|
|
|
|
|
mshFs.ReadExactly(data, 0, data.Length);
|
|
|
|
|
|
2026-05-12 01:19:19 +03:00
|
|
|
var span = data.AsSpan();
|
|
|
|
|
var indices = new List<ushort>(entry.FileLength / ElementSize);
|
|
|
|
|
|
|
|
|
|
for (var offset = 0; offset < span.Length; offset += ElementSize)
|
|
|
|
|
{
|
|
|
|
|
indices.Add(BinaryPrimitives.ReadUInt16LittleEndian(span.Slice(offset, ElementSize)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return indices;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Возвращает triangle triplets из диапазона индексов.
|
|
|
|
|
/// Удобно для обхода batch.IndexStart / batch.IndexCount из 0x0D.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IEnumerable<TriangleIndices> EnumerateTriangles(
|
|
|
|
|
IReadOnlyList<ushort> indices,
|
|
|
|
|
int indexStart,
|
2026-05-17 02:31:11 +03:00
|
|
|
int indexCount
|
|
|
|
|
)
|
2026-05-12 01:19:19 +03:00
|
|
|
{
|
|
|
|
|
if (indexStart < 0)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentOutOfRangeException(nameof(indexStart));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (indexCount < 0)
|
2025-08-26 04:29:30 +03:00
|
|
|
{
|
2026-05-12 01:19:19 +03:00
|
|
|
throw new ArgumentOutOfRangeException(nameof(indexCount));
|
2025-08-26 04:29:30 +03:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 01:19:19 +03:00
|
|
|
if (indexStart + indexCount > indices.Count)
|
|
|
|
|
{
|
|
|
|
|
throw new ArgumentException("Index range is outside the 0x06 index buffer");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i + 2 < indexCount; i += 3)
|
|
|
|
|
{
|
|
|
|
|
yield return new TriangleIndices(
|
|
|
|
|
indices[indexStart + i + 0],
|
|
|
|
|
indices[indexStart + i + 1],
|
|
|
|
|
indices[indexStart + i + 2]);
|
|
|
|
|
}
|
2025-08-26 04:29:30 +03:00
|
|
|
}
|
2026-05-12 01:19:19 +03:00
|
|
|
|
|
|
|
|
/// <summary>Тройка индексов triangle из MSH 0x06.</summary>
|
2026-05-17 02:31:11 +03:00
|
|
|
/// <param name="Vertex0">Первый vertex index внутри batch/base vertex range.</param>
|
|
|
|
|
/// <param name="Vertex1">Второй vertex index внутри batch/base vertex range.</param>
|
|
|
|
|
/// <param name="Vertex2">Третий vertex index внутри batch/base vertex range.</param>
|
2026-05-12 01:19:19 +03:00
|
|
|
public readonly record struct TriangleIndices(
|
2026-05-17 02:31:11 +03:00
|
|
|
ushort Vertex0,
|
|
|
|
|
ushort Vertex1,
|
|
|
|
|
ushort Vertex2
|
|
|
|
|
);
|
|
|
|
|
}
|