mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2026-08-15 02:57:49 +04:00
docs improvements
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Common;
|
||||||
|
|
||||||
|
public record Quaternion(float X, float Y, float Z, float W);
|
||||||
|
|
||||||
|
public record UShortQuaternion(ushort X, ushort Y, ushort Z, ushort W)
|
||||||
|
{
|
||||||
|
public Quaternion ToRegular()
|
||||||
|
{
|
||||||
|
return new Quaternion(
|
||||||
|
X / 32767f,
|
||||||
|
Y / 32767f,
|
||||||
|
Z / 32767f,
|
||||||
|
W / 32767f
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -82,8 +82,8 @@ public static class Msh0x02
|
|||||||
return new Msh02Header(
|
return new Msh02Header(
|
||||||
BoundingBox: bbox,
|
BoundingBox: bbox,
|
||||||
BoundingSphere: ReadSphere(header, 0x60),
|
BoundingSphere: ReadSphere(header, 0x60),
|
||||||
Bottom: ReadVector3(header, 0x70),
|
MeshStart: ReadVector3(header, 0x70),
|
||||||
Top: ReadVector3(header, 0x7C),
|
MeshEnd: ReadVector3(header, 0x7C),
|
||||||
XyRadius: BinaryPrimitives.ReadSingleLittleEndian(header.Slice(0x88, 4)));
|
XyRadius: BinaryPrimitives.ReadSingleLittleEndian(header.Slice(0x88, 4)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,14 +120,14 @@ public static class Msh0x02
|
|||||||
/// <summary>Заголовок MSH 0x02, length = 0x8C.</summary>
|
/// <summary>Заголовок MSH 0x02, length = 0x8C.</summary>
|
||||||
/// <param name="BoundingBox">[0x00..0x60] Bounding box из 8 точек.</param>
|
/// <param name="BoundingBox">[0x00..0x60] Bounding box из 8 точек.</param>
|
||||||
/// <param name="BoundingSphere">[0x60..0x70] Bounding sphere: xyz = center, w = radius.</param>
|
/// <param name="BoundingSphere">[0x60..0x70] Bounding sphere: xyz = center, w = radius.</param>
|
||||||
/// <param name="Bottom">[0x70..0x7C] Нижняя/опорная точка меша.</param>
|
/// <param name="MeshStart">[0x70..0x7C] Нижняя/опорная точка меша. Минимальная точка меша ??</param>
|
||||||
/// <param name="Top">[0x7C..0x88] Верхняя точка меша.</param>
|
/// <param name="MeshEnd">[0x7C..0x88] Верхняя точка меша. Максимальная точка меша ??</param>
|
||||||
/// <param name="XyRadius">[0x88..0x8C] Радиус/extent в плоскости XY.</param>
|
/// <param name="XyRadius">[0x88..0x8C] Радиус/extent в плоскости XY.</param>
|
||||||
public sealed record Msh02Header(
|
public sealed record Msh02Header(
|
||||||
BoundingBox BoundingBox,
|
BoundingBox BoundingBox,
|
||||||
Sphere BoundingSphere,
|
Sphere BoundingSphere,
|
||||||
Vector3 Bottom,
|
Vector3 MeshStart,
|
||||||
Vector3 Top,
|
Vector3 MeshEnd,
|
||||||
float XyRadius);
|
float XyRadius);
|
||||||
|
|
||||||
/// <summary>Geometry slot MSH 0x02, length = 0x44.</summary>
|
/// <summary>Geometry slot MSH 0x02, length = 0x44.</summary>
|
||||||
|
|||||||
@@ -47,5 +47,4 @@ public static class Msh0x05
|
|||||||
/// <summary>Упакованные UV0: две int16-компоненты (length = 4).</summary>
|
/// <summary>Упакованные UV0: две int16-компоненты (length = 4).</summary>
|
||||||
/// <param name="U">[0x00..0x02] U-компонента, uv = U / 1024.0.</param>
|
/// <param name="U">[0x00..0x02] U-компонента, uv = U / 1024.0.</param>
|
||||||
/// <param name="V">[0x02..0x04] V-компонента, uv = V / 1024.0.</param>
|
/// <param name="V">[0x02..0x04] V-компонента, uv = V / 1024.0.</param>
|
||||||
public readonly record struct Msh05Uv(short U, short V)
|
public readonly record struct Msh05Uv(short U, short V);
|
||||||
;
|
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ public static class Msh0x06
|
|||||||
public static IEnumerable<TriangleIndices> EnumerateTriangles(
|
public static IEnumerable<TriangleIndices> EnumerateTriangles(
|
||||||
IReadOnlyList<ushort> indices,
|
IReadOnlyList<ushort> indices,
|
||||||
int indexStart,
|
int indexStart,
|
||||||
int indexCount)
|
int indexCount
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (indexStart < 0)
|
if (indexStart < 0)
|
||||||
{
|
{
|
||||||
@@ -81,11 +82,12 @@ public static class Msh0x06
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Тройка индексов triangle из MSH 0x06.</summary>
|
/// <summary>Тройка индексов triangle из MSH 0x06.</summary>
|
||||||
/// <param name="A">Первый vertex index внутри batch/base vertex range.</param>
|
/// <param name="Vertex0">Первый vertex index внутри batch/base vertex range.</param>
|
||||||
/// <param name="B">Второй vertex index внутри batch/base vertex range.</param>
|
/// <param name="Vertex1">Второй vertex index внутри batch/base vertex range.</param>
|
||||||
/// <param name="C">Третий vertex index внутри batch/base vertex range.</param>
|
/// <param name="Vertex2">Третий vertex index внутри batch/base vertex range.</param>
|
||||||
public readonly record struct TriangleIndices(
|
public readonly record struct TriangleIndices(
|
||||||
ushort A,
|
ushort Vertex0,
|
||||||
ushort B,
|
ushort Vertex1,
|
||||||
ushort C);
|
ushort Vertex2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
+13
-13
@@ -47,13 +47,13 @@ public static class Msh0x07
|
|||||||
|
|
||||||
descriptors.Add(new TriangleDescriptor(
|
descriptors.Add(new TriangleDescriptor(
|
||||||
Flags: (TriangleFlags)BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x00, 2)),
|
Flags: (TriangleFlags)BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x00, 2)),
|
||||||
Link0: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x02, 2)),
|
LinkedTriangleIndex0_0x07: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x02, 2)),
|
||||||
Link1: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x04, 2)),
|
LinkedTriangleIndex1_0x07: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x04, 2)),
|
||||||
Link2: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x06, 2)),
|
LinkedTriangleIndex2_0x07: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x06, 2)),
|
||||||
PackedNormalX: BinaryPrimitives.ReadInt16LittleEndian(element.Slice(0x08, 2)),
|
PackedNormalX: BinaryPrimitives.ReadInt16LittleEndian(element.Slice(0x08, 2)),
|
||||||
PackedNormalY: BinaryPrimitives.ReadInt16LittleEndian(element.Slice(0x0A, 2)),
|
PackedNormalY: BinaryPrimitives.ReadInt16LittleEndian(element.Slice(0x0A, 2)),
|
||||||
PackedNormalZ: BinaryPrimitives.ReadInt16LittleEndian(element.Slice(0x0C, 2)),
|
PackedNormalZ: BinaryPrimitives.ReadInt16LittleEndian(element.Slice(0x0C, 2)),
|
||||||
SelectorPacked: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x0E, 2))));
|
PackedSelector: BinaryPrimitives.ReadUInt16LittleEndian(element.Slice(0x0E, 2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
return descriptors;
|
return descriptors;
|
||||||
@@ -61,22 +61,22 @@ public static class Msh0x07
|
|||||||
|
|
||||||
/// <summary>Описатель triangle MSH 0x07, length = 0x10.</summary>
|
/// <summary>Описатель triangle MSH 0x07, length = 0x10.</summary>
|
||||||
/// <param name="Flags">[0x00..0x02] Triangle flags. Используются GeometryWalkFilter require/exclude.</param>
|
/// <param name="Flags">[0x00..0x02] Triangle flags. Используются GeometryWalkFilter require/exclude.</param>
|
||||||
/// <param name="Link0">[0x02..0x04] Указывает на треугольники в 0x07 (текущем) компоненте.</param>
|
/// <param name="LinkedTriangleIndex0_0x07">[0x02..0x04] Указывает на треугольники в 0x07 (текущем) компоненте.</param>
|
||||||
/// <param name="Link1">[0x04..0x06] Указывает на треугольники в 0x07 (текущем) компоненте.</param>
|
/// <param name="LinkedTriangleIndex1_0x07">[0x04..0x06] Указывает на треугольники в 0x07 (текущем) компоненте.</param>
|
||||||
/// <param name="Link2">[0x06..0x08] Указывает на треугольники в 0x07 (текущем) компоненте.</param>
|
/// <param name="LinkedTriangleIndex2_0x07">[0x06..0x08] Указывает на треугольники в 0x07 (текущем) компоненте.</param>
|
||||||
/// <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="PackedSelector">[0x0E..0x10] Packed selectors. 3 трактуется как 0xFFFF.</param>
|
||||||
public readonly record struct TriangleDescriptor(
|
public readonly record struct TriangleDescriptor(
|
||||||
TriangleFlags Flags,
|
TriangleFlags Flags,
|
||||||
ushort Link0,
|
ushort LinkedTriangleIndex0_0x07,
|
||||||
ushort Link1,
|
ushort LinkedTriangleIndex1_0x07,
|
||||||
ushort Link2,
|
ushort LinkedTriangleIndex2_0x07,
|
||||||
short PackedNormalX,
|
short PackedNormalX,
|
||||||
short PackedNormalY,
|
short PackedNormalY,
|
||||||
short PackedNormalZ,
|
short PackedNormalZ,
|
||||||
ushort SelectorPacked)
|
ushort PackedSelector)
|
||||||
{
|
{
|
||||||
public Vector3 Normal => new(
|
public Vector3 Normal => new(
|
||||||
PackedNormalX * PackedNormalScale,
|
PackedNormalX * PackedNormalScale,
|
||||||
@@ -90,7 +90,7 @@ public static class Msh0x07
|
|||||||
throw new ArgumentOutOfRangeException(nameof(index));
|
throw new ArgumentOutOfRangeException(nameof(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
var selector = (SelectorPacked >> (index * 2)) & 0b11;
|
var selector = (PackedSelector >> (index * 2)) & 0b11;
|
||||||
|
|
||||||
return selector == 3
|
return selector == 3
|
||||||
? ushort.MaxValue
|
? ushort.MaxValue
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using System.Buffers.Binary;
|
||||||
|
using Common;
|
||||||
|
using NResLib;
|
||||||
|
|
||||||
|
namespace ParkanPlayground;
|
||||||
|
|
||||||
|
public static class Msh0x08
|
||||||
|
{
|
||||||
|
public static List<AnimationDescriptor> ReadComponent(FileStream mshFs, NResArchive archive)
|
||||||
|
{
|
||||||
|
var entry = archive.Files.FirstOrDefault(x => x.FileType == "08 00 00 00");
|
||||||
|
|
||||||
|
if (entry is null)
|
||||||
|
{
|
||||||
|
throw new Exception("Archive doesn't contain animation descriptor component (0x08)");
|
||||||
|
}
|
||||||
|
|
||||||
|
mshFs.Seek(entry.OffsetInFile, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var descriptors = new List<AnimationDescriptor>();
|
||||||
|
|
||||||
|
for (var i = 0; i < entry.ElementCount; i++)
|
||||||
|
{
|
||||||
|
descriptors.Add(new AnimationDescriptor(
|
||||||
|
new Vector3(mshFs.ReadFloatLittleEndian(),
|
||||||
|
mshFs.ReadFloatLittleEndian(),
|
||||||
|
mshFs.ReadFloatLittleEndian()),
|
||||||
|
mshFs.ReadFloatLittleEndian(),
|
||||||
|
new UShortQuaternion(
|
||||||
|
mshFs.ReadUInt16LittleEndian(),
|
||||||
|
mshFs.ReadUInt16LittleEndian(),
|
||||||
|
mshFs.ReadUInt16LittleEndian(),
|
||||||
|
mshFs.ReadUInt16LittleEndian()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public record AnimationDescriptor(
|
||||||
|
Vector3 Position,
|
||||||
|
float Time,
|
||||||
|
UShortQuaternion Rotation
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -112,75 +112,81 @@ public sealed class MshConverter
|
|||||||
var skippedBatches = 0;
|
var skippedBatches = 0;
|
||||||
var skippedFaces = 0;
|
var skippedFaces = 0;
|
||||||
|
|
||||||
for (var pieceIndex = 0; pieceIndex < nodes.Nodes.Count; pieceIndex++)
|
for (var pieceIndex = 1; pieceIndex < nodes.Nodes.Count; pieceIndex += 100000)
|
||||||
{
|
{
|
||||||
var node = nodes.Nodes[pieceIndex];
|
var node = nodes.Nodes[pieceIndex];
|
||||||
var slotIndex = node.ResolveSlotIndex(lod, group);
|
|
||||||
|
|
||||||
if (slotIndex == ushort.MaxValue)
|
for (var s = 0; s < node.Msh02SlotIndicesByStateAndLOD.Length; s++)
|
||||||
{
|
{
|
||||||
skippedSlots++;
|
var slotIndex = node.Msh02SlotIndicesByStateAndLOD[s];
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slotIndex >= geometry.Slots.Count)
|
if (slotIndex == ushort.MaxValue)
|
||||||
{
|
|
||||||
Warn($"Piece {pieceIndex}: geometry slot {slotIndex} out of range");
|
|
||||||
skippedSlots++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var slot = geometry.Slots[slotIndex];
|
|
||||||
|
|
||||||
if (!slot.HasBatches)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot.BatchEndExclusive0x0D > batches.Count)
|
|
||||||
{
|
|
||||||
Warn($"Piece {pieceIndex}: batch range {slot.BatchStart0x0D}:{slot.BatchCount0x0D} out of range");
|
|
||||||
skippedBatches++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteLine();
|
|
||||||
writer.WriteLine($"g piece_{pieceIndex}_slot_{slotIndex}");
|
|
||||||
|
|
||||||
for (var batchIndex = slot.BatchStart0x0D; batchIndex < slot.BatchEndExclusive0x0D; batchIndex++)
|
|
||||||
{
|
|
||||||
var batch = batches[batchIndex];
|
|
||||||
|
|
||||||
if (batch.IndexStart0x06 + batch.IndexCount0x06 > indices.Count)
|
|
||||||
{
|
{
|
||||||
Warn($"Piece {pieceIndex}, batch {batchIndex}: index range {batch.IndexStart0x06}:{batch.IndexCount0x06} out of range");
|
skippedSlots++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slotIndex >= geometry.Slots.Count)
|
||||||
|
{
|
||||||
|
Warn($"Piece {pieceIndex}: geometry slot {slotIndex} out of range");
|
||||||
|
skippedSlots++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var slot = geometry.Slots[slotIndex];
|
||||||
|
|
||||||
|
if (!slot.HasBatches)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot.BatchEndExclusive0x0D > batches.Count)
|
||||||
|
{
|
||||||
|
Warn($"Piece {pieceIndex}: batch range {slot.BatchStart0x0D}:{slot.BatchCount0x0D} out of range");
|
||||||
skippedBatches++;
|
skippedBatches++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.WriteLine($"# batch {batchIndex}, material {batch.MaterialIndex}, flags {batch.Flags}");
|
writer.WriteLine();
|
||||||
|
writer.WriteLine($"o piece_{pieceIndex}_{s}");
|
||||||
|
|
||||||
for (var i = 0; i + 2 < batch.IndexCount0x06; i += 3)
|
for (var batchIndex = slot.BatchStart0x0D; batchIndex < slot.BatchEndExclusive0x0D; batchIndex++)
|
||||||
{
|
{
|
||||||
var indexBase = (int)batch.IndexStart0x06 + i;
|
var batch = batches[batchIndex];
|
||||||
|
|
||||||
var v1 = checked((int)batch.BaseVertex0x03 + indices[indexBase + 0]);
|
if (batch.IndexStart0x06 + batch.IndexCount0x06 > indices.Count)
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
skippedFaces++;
|
Warn(
|
||||||
|
$"Piece {pieceIndex}, batch {batchIndex}: index range {batch.IndexStart0x06}:{batch.IndexCount0x06} out of range");
|
||||||
|
skippedBatches++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteFace(writer, v1, v2, v3);
|
writer.WriteLine($"# batch {batchIndex}, material {batch.MaterialIndex}, flags {batch.Flags}");
|
||||||
exportedFaces++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (batch.IndexCount0x06 % 3 != 0)
|
for (var i = 0; i + 2 < batch.IndexCount0x06; i += 3)
|
||||||
{
|
{
|
||||||
Warn($"Piece {pieceIndex}, batch {batchIndex}: index count {batch.IndexCount0x06} is not divisible by 3");
|
var indexBase = (int)batch.IndexStart0x06 + i;
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
skippedFaces++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteFace(writer, v1, v2, v3);
|
||||||
|
exportedFaces++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.IndexCount0x06 % 3 != 0)
|
||||||
|
{
|
||||||
|
Warn(
|
||||||
|
$"Piece {pieceIndex}, batch {batchIndex}: index count {batch.IndexCount0x06} is not divisible by 3");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
using Common;
|
using Common;
|
||||||
|
using ParkanPlayground;
|
||||||
|
|
||||||
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
|
||||||
|
|
||||||
|
var converter = new MshConverter();
|
||||||
|
|
||||||
|
converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\130_fr_b_plant.msh");
|
||||||
@@ -434,6 +434,8 @@ enum EffectTemplateFlags : uint32_t
|
|||||||
- `0x502` - ResTree
|
- `0x502` - ResTree
|
||||||
- `0x700` - INetWatcher
|
- `0x700` - INetWatcher
|
||||||
- `0x701` - INetworkInterface
|
- `0x701` - INetworkInterface
|
||||||
|
- `0x802` - INetSessionBrowser (придумал сам implemented by CNetManager in services.dll)
|
||||||
|
- `0x803` - INetManager
|
||||||
|
|
||||||
## SuperAI = Clan с точки зрения индексации
|
## SuperAI = Clan с точки зрения индексации
|
||||||
|
|
||||||
@@ -441,7 +443,7 @@ enum EffectTemplateFlags : uint32_t
|
|||||||
|
|
||||||
## Опции
|
## Опции
|
||||||
|
|
||||||
World3D.dll содержит функцию CreateGameSettings.
|
World3D.dll содержит singleton Registry в CreateGameSettings.
|
||||||
Она создаёт объект настроек и далее вызывает методы в соседних библиотеках.
|
Она создаёт объект настроек и далее вызывает методы в соседних библиотеках.
|
||||||
- Terrain.dll - InitializeSettings
|
- Terrain.dll - InitializeSettings
|
||||||
- Effect.dll - InitializeSettings
|
- Effect.dll - InitializeSettings
|
||||||
|
|||||||
Reference in New Issue
Block a user