mirror of
https://github.com/sampletext32/ParkanPlayground.git
synced 2025-09-13 10:20:28 +03:00
Hack .msh
This commit is contained in:
@@ -32,23 +32,24 @@ public static class Msh01
|
||||
OffsetIntoFile13 =
|
||||
BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 4)),
|
||||
IndexInFile08 =
|
||||
BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 6)),
|
||||
State00 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 8)),
|
||||
State01 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 10)),
|
||||
State10 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 12)),
|
||||
State11 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 14)),
|
||||
State20 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 16)),
|
||||
State21 = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 18)),
|
||||
S20 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 20)),
|
||||
S22 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 22)),
|
||||
S24 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 24)),
|
||||
S26 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 26)),
|
||||
S28 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 28)),
|
||||
S30 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 30)),
|
||||
S32 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 32)),
|
||||
S34 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 34)),
|
||||
S36 = BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 36))
|
||||
BinaryPrimitives.ReadInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 6))
|
||||
};
|
||||
|
||||
element.Lod[0] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 8));
|
||||
element.Lod[1] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 10));
|
||||
element.Lod[2] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 12));
|
||||
element.Lod[3] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 14));
|
||||
element.Lod[4] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 16));
|
||||
element.Lod[5] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 18));
|
||||
element.Lod[6] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 20));
|
||||
element.Lod[7] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 22));
|
||||
element.Lod[8] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 24));
|
||||
element.Lod[9] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 26));
|
||||
element.Lod[10] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 28));
|
||||
element.Lod[11] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 30));
|
||||
element.Lod[12] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 32));
|
||||
element.Lod[13] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 34));
|
||||
element.Lod[14] = BinaryPrimitives.ReadUInt16LittleEndian(dataSpan.Slice(i * headerFileEntry.ElementSize + 36));
|
||||
elements.Add(element);
|
||||
}
|
||||
|
||||
@@ -71,20 +72,6 @@ public static class Msh01
|
||||
public short ParentIndex { get; set; }
|
||||
public short OffsetIntoFile13 { get; set; }
|
||||
public short IndexInFile08 { get; set; }
|
||||
public ushort State00 { get; set; }
|
||||
public ushort State01 { get; set; }
|
||||
public ushort State10 { get; set; }
|
||||
public ushort State11 { get; set; }
|
||||
public ushort State20 { get; set; }
|
||||
public ushort State21 { get; set; }
|
||||
public short S20 { get; set; }
|
||||
public short S22 { get; set; }
|
||||
public short S24 { get; set; }
|
||||
public short S26 { get; set; }
|
||||
public short S28 { get; set; }
|
||||
public short S30 { get; set; }
|
||||
public short S32 { get; set; }
|
||||
public short S34 { get; set; }
|
||||
public short S36 { get; set; }
|
||||
public ushort[] Lod { get; set; } = new ushort[15];
|
||||
}
|
||||
}
|
@@ -159,18 +159,6 @@ public static class Msh02
|
||||
public float XYRadius { get; set; }
|
||||
}
|
||||
|
||||
public class BoundingBox
|
||||
{
|
||||
public Vector3 Vec1 { get; set; }
|
||||
public Vector3 Vec2 { get; set; }
|
||||
public Vector3 Vec3 { get; set; }
|
||||
public Vector3 Vec4 { get; set; }
|
||||
public Vector3 Vec5 { get; set; }
|
||||
public Vector3 Vec6 { get; set; }
|
||||
public Vector3 Vec7 { get; set; }
|
||||
public Vector3 Vec8 { get; set; }
|
||||
}
|
||||
|
||||
public class Msh02Element
|
||||
{
|
||||
public ushort StartIndexIn07 { get; set; }
|
||||
@@ -183,4 +171,16 @@ public static class Msh02
|
||||
public Vector3 Vector4 { get; set; }
|
||||
public Vector3 Vector5 { get; set; }
|
||||
}
|
||||
|
||||
public class BoundingBox
|
||||
{
|
||||
public Vector3 Vec1 { get; set; }
|
||||
public Vector3 Vec2 { get; set; }
|
||||
public Vector3 Vec3 { get; set; }
|
||||
public Vector3 Vec4 { get; set; }
|
||||
public Vector3 Vec5 { get; set; }
|
||||
public Vector3 Vec6 { get; set; }
|
||||
public Vector3 Vec7 { get; set; }
|
||||
public Vector3 Vec8 { get; set; }
|
||||
}
|
||||
}
|
35
ParkanPlayground/Msh03.cs
Normal file
35
ParkanPlayground/Msh03.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Buffers.Binary;
|
||||
using Common;
|
||||
using NResLib;
|
||||
|
||||
namespace ParkanPlayground;
|
||||
|
||||
public class Msh03
|
||||
{
|
||||
public static List<Vector3> ReadComponent(FileStream mshFs, NResArchive mshNres)
|
||||
{
|
||||
var verticesFileEntry = mshNres.Files.FirstOrDefault(x => x.FileType == "03 00 00 00");
|
||||
|
||||
if (verticesFileEntry is null)
|
||||
{
|
||||
throw new Exception("Archive doesn't contain vertices file (03)");
|
||||
}
|
||||
|
||||
if (verticesFileEntry.ElementSize != 12)
|
||||
{
|
||||
throw new Exception("Vertices file (03) element size is not 12");
|
||||
}
|
||||
|
||||
var verticesFile = new byte[verticesFileEntry.ElementCount * verticesFileEntry.ElementSize];
|
||||
mshFs.Seek(verticesFileEntry.OffsetInFile, SeekOrigin.Begin);
|
||||
mshFs.ReadExactly(verticesFile, 0, verticesFile.Length);
|
||||
|
||||
var vertices = verticesFile.Chunk(12).Select(x => new Vector3(
|
||||
BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(0)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(4)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(8))
|
||||
)
|
||||
).ToList();
|
||||
return vertices;
|
||||
}
|
||||
}
|
@@ -25,7 +25,8 @@ public static class Msh07
|
||||
{
|
||||
Flags = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0)),
|
||||
Magic02 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(2)),
|
||||
Magic04 = BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(4)),
|
||||
Magic04 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(4)),
|
||||
Magic06 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(6)),
|
||||
OffsetX = BinaryPrimitives.ReadInt16LittleEndian(x.AsSpan(8)),
|
||||
OffsetY = BinaryPrimitives.ReadInt16LittleEndian(x.AsSpan(10)),
|
||||
OffsetZ = BinaryPrimitives.ReadInt16LittleEndian(x.AsSpan(12)),
|
||||
@@ -39,7 +40,8 @@ public static class Msh07
|
||||
{
|
||||
public ushort Flags { get; set; }
|
||||
public ushort Magic02 { get; set; }
|
||||
public uint Magic04 { get; set; }
|
||||
public ushort Magic04 { get; set; }
|
||||
public ushort Magic06 { get; set; }
|
||||
// normalized vector X, need to divide by 32767 to get float in range -1..1
|
||||
public short OffsetX { get; set; }
|
||||
// normalized vector Y, need to divide by 32767 to get float in range -1..1
|
||||
|
@@ -26,11 +26,12 @@ public static class Msh0D
|
||||
var elements = elementBytes.Select(x => new Msh0DElement()
|
||||
{
|
||||
Flags = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0)),
|
||||
Magic04 = BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(4)),
|
||||
number_of_triangles = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(8)),
|
||||
IndexInto06 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(10)),
|
||||
Magic0C = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(14)),
|
||||
IndexInto03 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(16)),
|
||||
Magic04 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(4)),
|
||||
Magic06 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(6)),
|
||||
CountOf06 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(8)),
|
||||
IndexInto06 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0xA)),
|
||||
CountOf03 = BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0xE)),
|
||||
IndexInto03 = BinaryPrimitives.ReadInt32LittleEndian(x.AsSpan(0x10)),
|
||||
}).ToList();
|
||||
|
||||
return elements;
|
||||
@@ -39,10 +40,14 @@ public static class Msh0D
|
||||
public class Msh0DElement
|
||||
{
|
||||
public uint Flags { get; set; }
|
||||
public uint Magic04 { get; set; }
|
||||
public ushort number_of_triangles { get; set; }
|
||||
|
||||
// Magic04 и Magic06 обрабатываются вместе
|
||||
|
||||
public ushort Magic04 { get; set; }
|
||||
public ushort Magic06 { get; set; }
|
||||
public ushort CountOf06 { get; set; }
|
||||
public int IndexInto06 { get; set; }
|
||||
public ushort Magic0C { get; set; }
|
||||
public ushort CountOf03 { get; set; }
|
||||
public int IndexInto03 { get; set; }
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Buffers.Binary;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Common;
|
||||
using NResLib;
|
||||
@@ -19,11 +20,17 @@ public class MshConverter
|
||||
var component0A = Msh0A.ReadComponent(mshFs, mshNres);
|
||||
var component07 = Msh07.ReadComponent(mshFs, mshNres);
|
||||
var component0D = Msh0D.ReadComponent(mshFs, mshNres);
|
||||
|
||||
// Triangle Vertex Indices
|
||||
var component06 = Msh06.ReadComponent(mshFs, mshNres);
|
||||
var component03 = Read03Component(mshFs, mshNres);
|
||||
|
||||
// vertices
|
||||
var component03 = Msh03.ReadComponent(mshFs, mshNres);
|
||||
|
||||
_ = 5;
|
||||
|
||||
// --- Write OBJ ---
|
||||
using var sw = new StreamWriter("test.obj", false, Encoding.UTF8);
|
||||
using var sw = new StreamWriter("test.obj", false, new UTF8Encoding(false));
|
||||
|
||||
foreach (var v in component03)
|
||||
sw.WriteLine($"v {v.X:F8} {v.Y:F8} {v.Z:F8}");
|
||||
@@ -31,48 +38,56 @@ public class MshConverter
|
||||
var vertices = new List<Vector3>();
|
||||
var faces = new List<(int, int, int)>(); // store indices into vertices list
|
||||
|
||||
// 01 - это части меша (Piece)
|
||||
for (var pieceIndex = 0; pieceIndex < component01.Elements.Count; pieceIndex++)
|
||||
{
|
||||
var piece = component01.Elements[pieceIndex];
|
||||
var state = (piece.State00 == 0xffff) ? 0 : piece.State00;
|
||||
var piece01 = component01.Elements[pieceIndex];
|
||||
// var state = (piece.State00 == 0xffff) ? 0 : piece.State00;
|
||||
|
||||
var mesh02Element = component02.Elements[state];
|
||||
for (var lodIndex = 0; lodIndex < piece01.Lod.Length; lodIndex++)
|
||||
{
|
||||
var lod = piece01.Lod[lodIndex];
|
||||
if (lod == 0xffff)
|
||||
{
|
||||
Console.WriteLine($"Piece {pieceIndex} has lod -1 at {lodIndex}. Skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
int indexInto07 = mesh02Element.StartIndexIn07;
|
||||
sw.WriteLine($"o piece_{pieceIndex}_lod_{lodIndex}");
|
||||
// 02 - Submesh
|
||||
var part02 = component02.Elements[lod];
|
||||
|
||||
var element0Dstart = mesh02Element.StartOffsetIn0d;
|
||||
var element0Dcount = mesh02Element.ByteLengthIn0D;
|
||||
int indexInto07 = part02.StartIndexIn07;
|
||||
|
||||
Console.WriteLine($"Started piece {pieceIndex}. State={state}. 0D start={element0Dstart}, count={element0Dcount}");
|
||||
var element0Dstart = part02.StartOffsetIn0d;
|
||||
var element0Dcount = part02.ByteLengthIn0D;
|
||||
|
||||
// Console.WriteLine($"Started piece {pieceIndex}. LOD={lod}. 0D start={element0Dstart}, count={element0Dcount}");
|
||||
|
||||
for (var comp0Dindex = 0; comp0Dindex < element0Dcount; comp0Dindex++)
|
||||
{
|
||||
var element0D = component0D[element0Dstart + comp0Dindex];
|
||||
|
||||
var indexInto03 = element0D.IndexInto03;
|
||||
var indexInto06 = element0D.IndexInto06;
|
||||
var indexInto06 = element0D.IndexInto06; // indices
|
||||
|
||||
uint maxIndex = element0D.CountOf03;
|
||||
uint indicesCount = element0D.CountOf06;
|
||||
|
||||
var count = (uint)element0D.number_of_triangles;
|
||||
|
||||
// Convert IndexInto06 to ushort array index (3 ushorts per triangle)
|
||||
Console.WriteLine(
|
||||
$"Processing 0D element[{element0Dstart + comp0Dindex}]. IndexInto03={indexInto03}, IndexInto06={indexInto06}. Number of triangles={count}");
|
||||
// Console.WriteLine($"Processing 0D element[{element0Dstart + comp0Dindex}]. IndexInto03={indexInto03}, IndexInto06={indexInto06}. Number of triangles={indicesCount}");
|
||||
|
||||
if (count != 0)
|
||||
if (indicesCount != 0)
|
||||
{
|
||||
sw.WriteLine($"o piece_{pieceIndex++}_of_mesh_{comp0Dindex}");
|
||||
if (count % 3 != 0)
|
||||
{
|
||||
Console.WriteLine("Number of triangles is not multiple of 3");
|
||||
_ = 5;
|
||||
}
|
||||
// sw.WriteLine($"o piece_{pieceIndex}_of_mesh_{comp0Dindex}");
|
||||
|
||||
count = (count + 2) / 3; // number of triangles
|
||||
|
||||
for (int tri = 0; tri < count; tri++)
|
||||
for (int ind = 0; ind < indicesCount; ind += 3)
|
||||
{
|
||||
// Each triangle uses 3 consecutive ushorts in component06
|
||||
|
||||
// sw.WriteLine($"o piece_{pieceIndex}_of_mesh_{comp0Dindex}_tri_{ind}");
|
||||
|
||||
var comp07 = component07[indexInto07];
|
||||
|
||||
var i1 = indexInto03 + component06[indexInto06];
|
||||
@@ -103,6 +118,87 @@ public class MshConverter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Convert2(string mshPath)
|
||||
{
|
||||
var mshNresResult = NResParser.ReadFile(mshPath);
|
||||
var mshNres = mshNresResult.Archive!;
|
||||
|
||||
using var mshFs = new FileStream(mshPath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
|
||||
var component01 = Msh01.ReadComponent(mshFs, mshNres);
|
||||
var component02 = Msh02.ReadComponent(mshFs, mshNres);
|
||||
var component0A = Msh0A.ReadComponent(mshFs, mshNres);
|
||||
var component07 = Msh07.ReadComponent(mshFs, mshNres);
|
||||
var component0D = Msh0D.ReadComponent(mshFs, mshNres);
|
||||
|
||||
// Triangle Vertex Indices
|
||||
var component06 = Msh06.ReadComponent(mshFs, mshNres);
|
||||
|
||||
// vertices
|
||||
var component03 = Msh03.ReadComponent(mshFs, mshNres);
|
||||
|
||||
var csv06 = new StreamWriter("06.csv", false, Encoding.UTF8);
|
||||
|
||||
for (var i = 0; i < component06.Count; i += 3)
|
||||
{
|
||||
// csv06.WriteLine($"{component06[i]}");
|
||||
csv06.WriteLine($"{component06[i]}, {component06[i + 1]}, {component06[i + 2]}");
|
||||
}
|
||||
|
||||
csv06.Dispose();
|
||||
|
||||
var csv03 = new StreamWriter("03.obj", false, Encoding.UTF8);
|
||||
|
||||
csv03.WriteLine();
|
||||
for (var i = 7525; i < component03.Count; i++)
|
||||
{
|
||||
// csv06.WriteLine($"{component06[i]}");
|
||||
// csv03.WriteAsync($"o {i - 7525}");
|
||||
csv03.WriteLine(
|
||||
$"v {component03[i].X.ToString("F2", CultureInfo.InvariantCulture)} {component03[i].Y.ToString("F2", CultureInfo.InvariantCulture)} {component03[i].Z.ToString("F2", CultureInfo.InvariantCulture)}");
|
||||
}
|
||||
|
||||
for (var i = 10485; i < component06.Count; i += 3)
|
||||
{
|
||||
csv03.WriteLine($"f {component06[i] + 1} {component06[i + 1] + 1} {component06[i + 2] + 1}");
|
||||
}
|
||||
|
||||
csv03.Dispose();
|
||||
|
||||
// --- Write OBJ ---
|
||||
using var sw = new StreamWriter("test.obj", false, Encoding.UTF8);
|
||||
|
||||
for (var index = 0; index < component03.Count; index++)
|
||||
{
|
||||
var v = component03[index];
|
||||
sw.WriteLine($"v {v.X:F8} {v.Y:F8} {v.Z:F8}");
|
||||
}
|
||||
|
||||
for (var i = 0; i < 1; i++)
|
||||
{
|
||||
sw.WriteLine($"o elem_0d_{i}");
|
||||
var element0D = component0D[i];
|
||||
|
||||
Console.WriteLine($"Processing element 0D [{i}]:");
|
||||
|
||||
var elementsOf06 = component06
|
||||
.Skip(element0D.IndexInto06)
|
||||
.Take(element0D.CountOf06)
|
||||
.ToList();
|
||||
|
||||
Console.WriteLine($"\tCount of 06: {elementsOf06.Count}, starting from {element0D.IndexInto06}");
|
||||
var indexInto03 = element0D.IndexInto03;
|
||||
|
||||
Console.WriteLine($"\tIndexInto03: {indexInto03}");
|
||||
|
||||
if (elementsOf06.Count < 3)
|
||||
{
|
||||
throw new Exception("Less than 3 points");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record Face(Vector3 P1, Vector3 P2, Vector3 P3);
|
||||
|
||||
@@ -189,34 +285,6 @@ public class MshConverter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static List<Vector3> Read03Component(FileStream mshFs, NResArchive mshNres)
|
||||
{
|
||||
var verticesFileEntry = mshNres.Files.FirstOrDefault(x => x.FileType == "03 00 00 00");
|
||||
|
||||
if (verticesFileEntry is null)
|
||||
{
|
||||
throw new Exception("Archive doesn't contain vertices file (03)");
|
||||
}
|
||||
|
||||
if (verticesFileEntry.ElementSize != 12)
|
||||
{
|
||||
throw new Exception("Vertices file (03) element size is not 12");
|
||||
}
|
||||
|
||||
var verticesFile = new byte[verticesFileEntry.ElementCount * verticesFileEntry.ElementSize];
|
||||
mshFs.Seek(verticesFileEntry.OffsetInFile, SeekOrigin.Begin);
|
||||
mshFs.ReadExactly(verticesFile, 0, verticesFile.Length);
|
||||
|
||||
var vertices = verticesFile.Chunk(12).Select(x => new Vector3(
|
||||
BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(0)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(4)),
|
||||
BinaryPrimitives.ReadSingleLittleEndian(x.AsSpan(8))
|
||||
)
|
||||
).ToList();
|
||||
return vertices;
|
||||
}
|
||||
|
||||
void Export(string filePath, IEnumerable<Vector3> vertices, List<IndexedEdge> edges)
|
||||
{
|
||||
using (var writer = new StreamWriter(filePath))
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using System.Buffers.Binary;
|
||||
using System.Buffers.Binary;
|
||||
using Common;
|
||||
using MissionTmaLib.Parsing;
|
||||
using NResLib;
|
||||
@@ -10,6 +10,7 @@ using ParkanPlayground;
|
||||
var converter = new MshConverter();
|
||||
|
||||
converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\133_fr_m_bunker.msh");
|
||||
// converter.Convert("E:\\ParkanUnpacked\\fortif.rlb\\73_fr_m_brige.msh");
|
||||
// converter.Convert("E:\\ParkanUnpacked\\intsys.rlb\\277_MESH_o_pws_l_01.msh");
|
||||
// converter.Convert("E:\\ParkanUnpacked\\static.rlb\\2_MESH_s_stn_0_01.msh");
|
||||
// converter.Convert("E:\\ParkanUnpacked\\bases.rlb\\25_MESH_R_H_02.msh");
|
36
README.md
36
README.md
@@ -87,6 +87,10 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
|
||||
|
||||
Затем `Comp.ini` - тут системные функции, которые используются для загрузки объектов.
|
||||
|
||||
```
|
||||
IComponent ** LoadSomething(undefined4, undefined4, undefined4, undefined4)
|
||||
```
|
||||
|
||||
- `Host.url` - этого файла нет
|
||||
- `palettes.lib` - тут палитры, но этот NRes пустой
|
||||
- `system.rlb` - не понятно что
|
||||
@@ -211,17 +215,16 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
|
||||
|
||||
Загружается в `AniMesh.dll/LoadAniMesh`
|
||||
|
||||
- Тип 01 - заголовок
|
||||
- Тип 01 - заголовок. Он хранит список деталей (submesh) в разных LOD
|
||||
```
|
||||
нулевому элементу добавляется флаг 0x1000000
|
||||
Хранит стейты меша (в один стейт может входить несколько submesh)
|
||||
Содержит 2 ссылки на файлы анимаций (короткие - файл 13, длинные - файл 08)
|
||||
Если интерполируется анимация -0.5s короче чем magic1 у файла 13
|
||||
И у файла есть OffsetIntoFile13
|
||||
И ushort значение в файле 13 по этому оффсету > IndexInFile08 (это по-моему выполняется всегда)
|
||||
Тогда вместо IndexInFile08 используется значение из файла 13 по этому оффсету (второй байт)
|
||||
```
|
||||
- Тип 02 - описание submesh
|
||||
- Тип 02 - описание одного LOD Submesh
|
||||
```
|
||||
Вначале идёт заголовок 0x8C (140) байт
|
||||
В заголовке:
|
||||
@@ -233,7 +236,7 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
|
||||
Далее инфа про куски меша
|
||||
```
|
||||
- Тип 03 - это вершины (vertex)
|
||||
- Тип 06 -
|
||||
- Тип 06 - индексы треугольников в файле 03
|
||||
- Тип 04 - скорее всего какие-то цвета RGBA или типа того
|
||||
- Тип 08 - меш-анимации (см файл 01)
|
||||
```
|
||||
@@ -257,21 +260,19 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
|
||||
Буквально (hex)
|
||||
00 01 01 02 ...
|
||||
```
|
||||
- Тип 0A
|
||||
- Тип 0A - ссылка на части меша, не упакованные в текущий меш (например у бункера 4 и 5 части хранятся в parts.rlb)
|
||||
```
|
||||
Не имеет фиксированной длины. Хранит какие-то строки в следующем формате.
|
||||
Не имеет фиксированной длины. Хранит строки в следующем формате.
|
||||
Игра обращается по индексу, пропуская суммарную длину и пропуская 4 байта на каждую строку (длина).
|
||||
т.е. буквально файл выглядит так
|
||||
00 00 00 00 - пустая строка
|
||||
03 00 00 00 - длина строки 1
|
||||
73 74 72 00 - строка "str" + null terminator
|
||||
.. и повторяется до конца файла
|
||||
Кол-во элементов из NRes должно быть равно кол-ву строк в этом файле, хотя игра это не проверяет.
|
||||
Кол-во элементов из файла 01 должно быть равно кол-ву строк в этом файле, хотя игра это не проверяет.
|
||||
Если у элемента эта строка равна "central", ему выставляется флаг (flag |= 1)
|
||||
```
|
||||
|
||||
Тип 02 имеет заголовок 140 байт.
|
||||
|
||||
## `.wea`
|
||||
|
||||
Загружается в `World3D.dll/LoadMatManager`
|
||||
@@ -289,26 +290,32 @@ grep -rlU $'\x73\x5f\x74\x72\x65\x65\x5f\x30\x35' .
|
||||
- `0xb` - IAnimation
|
||||
- `0xd` - IMatManager
|
||||
- `0xe` - ILightManager
|
||||
- `0x10` - unknown (implemented by Wizard in Wizard.dll, also by Hallway in ArealMap.dll)
|
||||
- `0x10` - IBehavior
|
||||
- `0x11` - IBasement
|
||||
- `0x12` - ICamera2 - BufferingCamera
|
||||
- `0x12` - ICamera2 или IBufferingCamera
|
||||
- `0x13` - IEffectManager
|
||||
- `0x14` - IPosition
|
||||
- `0x15` - IAgent
|
||||
- `0x16` - ILifeSystem
|
||||
- `0x17` - IBuilding
|
||||
- `0x17` - IBuilding - точно он, т.к. ArealMap.CreateObject на него проверяет
|
||||
- `0x18` - IMesh2
|
||||
- `0x19` - unknown (implemented by Wizard in Wizard.dll, also by Agent in AniMesh.dll)
|
||||
- `0x19` - IManManager
|
||||
- `0x20` - IJointMesh
|
||||
- `0x21` - IShade
|
||||
- `0x23` - IGameSettings
|
||||
- `0x24` - IGameObject2
|
||||
- `0x25` - unknown (implemented by AniMesh)
|
||||
- `0x26` - unknown (implemented by AniMesh)
|
||||
- `0x28` - ICollObject
|
||||
- `0x101` - 3DRender
|
||||
- `0x105` - NResFile
|
||||
- `0x201` - IWizard
|
||||
- `0x202` - IItemManager
|
||||
- `0x203` - ICollManager
|
||||
- `0x301` - IArealMap
|
||||
- `0x302` - ISystemArealMap
|
||||
- `0x303` - IHallway
|
||||
- `0x304` - Distributor
|
||||
- `0x401` - ISuperAI
|
||||
- `0x105` - NResFile
|
||||
- `0x106` - NResFileMetadata
|
||||
@@ -330,7 +337,7 @@ World3D.dll содержит функцию CreateGameSettings.
|
||||
Остальные наверное не трогают настройки.
|
||||
|
||||
| Resource ID | wOptionID | Name | Default | Description |
|
||||
|:-----------:|:---------------:|:--------------------------:|:-------:|---------------------|
|
||||
|:-----------:|:---------------:|:--------------------------:|:-------:|--------------------|
|
||||
| 1 | 100 (0x64) | "Texture detail" | | |
|
||||
| 2 | 101 (0x65) | "3D Sound" | | |
|
||||
| 3 | 102 (0x66) | "Mouse sensitivity" | | |
|
||||
@@ -348,6 +355,7 @@ World3D.dll содержит функцию CreateGameSettings.
|
||||
| 15 | 92 (0x5c) | "Play sound buffer always" | | |
|
||||
| 16 | 93 (0x5d) | "Select best sound device" | | |
|
||||
| ---- | 30 (0x1e) | ShadeConfig | | из файла shade.cfg |
|
||||
| ---- | (0x8001e) | | | добавляет AniMesh |
|
||||
|
||||
|
||||
## Контакты
|
||||
|
Reference in New Issue
Block a user