fix textures UV. drop leftovers. properly map material_lo.

This commit is contained in:
bird_egop
2026-06-06 23:25:14 +03:00
parent 5baf3f324f
commit 64fd54635e
9 changed files with 36 additions and 73 deletions
+1 -25
View File
@@ -1,5 +1,6 @@
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Text; using System.Text;
using Common;
namespace MaterialLib; namespace MaterialLib;
@@ -168,28 +169,3 @@ public static class MaterialParser
return $"Интерполируется: {string.Join(", ", parts)} | Остальные компоненты копируются (Flags: 0x{(int)target:X})"; return $"Интерполируется: {string.Join(", ", parts)} | Остальные компоненты копируются (Flags: 0x{(int)target:X})";
} }
} }
// Helper extensions
internal static class StreamExtensions
{
public static float ReadFloatLittleEndian(this Stream stream)
{
Span<byte> buffer = stackalloc byte[4];
stream.ReadExactly(buffer);
return BinaryPrimitives.ReadSingleLittleEndian(buffer);
}
public static ushort ReadUInt16LittleEndian(this Stream stream)
{
Span<byte> buffer = stackalloc byte[2];
stream.ReadExactly(buffer);
return BinaryPrimitives.ReadUInt16LittleEndian(buffer);
}
public static uint ReadUInt32LittleEndian(this Stream stream)
{
Span<byte> buffer = stackalloc byte[4];
stream.ReadExactly(buffer);
return BinaryPrimitives.ReadUInt32LittleEndian(buffer);
}
}
-25
View File
@@ -104,11 +104,6 @@ public static class Msh0x01
ushort FallbackKey0x08, ushort FallbackKey0x08,
ushort[] Msh02SlotIndicesByStateAndLOD) ushort[] Msh02SlotIndicesByStateAndLOD)
{ {
public bool IsRoot => ParentIndexOrLink == ushort.MaxValue;
public int ParentIndexOrMinusOne =>
ParentIndexOrLink == ushort.MaxValue ? -1 : ParentIndexOrLink;
public ushort ResolveSlotIndex(int state, int lod = 0) public ushort ResolveSlotIndex(int state, int lod = 0)
{ {
@@ -129,26 +124,6 @@ public static class Msh0x01
? Msh02SlotIndicesByStateAndLOD[index] ? Msh02SlotIndicesByStateAndLOD[index]
: ushort.MaxValue; : ushort.MaxValue;
} }
public bool HasGeometrySlot(int lod, int group = 0) =>
ResolveSlotIndex(lod, group) != ushort.MaxValue;
public int CountLodsForGroup(int group = 0)
{
var count = 0;
for (var lod = 0; lod < StateCount; lod++)
{
if (!HasGeometrySlot(lod, group))
{
break;
}
count++;
}
return count;
}
} }
} }
+17 -12
View File
@@ -38,21 +38,25 @@ public static class Msh0x0D
return data return data
.Chunk(ElementSize) .Chunk(ElementSize)
.Select(x => new Batch( .Select(x => new Batch(
(BatchFlags)BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x00)), Flags: (BatchFlags)BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x00)),
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x02)), MaterialIndexHi: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x02)),
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x04)), MaterialIndexLo: x.AsSpan(0x04)[0],
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x06)), LightmapIndex: x.AsSpan(0x05)[0],
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x08)), LocalBatchIndex: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x06)),
BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(0x0A)), IndexCount0x06: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x08)),
BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x0E)), IndexStart0x06: BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(0x0A)),
BinaryPrimitives.ReadUInt32LittleEndian(x.AsSpan(0x10)))) VertexCount0x03: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x0E)),
BaseVertex0x03: BinaryPrimitives.ReadUInt16LittleEndian(x.AsSpan(0x10))
)
)
.ToList(); .ToList();
} }
/// <summary>MSH batch 0x0D, sizeof 0x14.</summary> /// <summary>MSH batch 0x0D, sizeof 0x14.</summary>
/// <param name="Flags">[0x00..0x02] Флаги batch.</param> /// <param name="Flags">[0x00..0x02] Флаги batch.</param>
/// <param name="MaterialIndex">[0x02..0x04] Индекс material slot. Может быть overridden CAniMesh::forced_material_index.</param> /// <param name="MaterialIndexHi">[0x02..0x04] Индекс material slot. Может быть overridden CAniMesh::forced_material_index.</param>
/// <param name="Opaque04">[0x04..0x06] Opaque. В GetBatchRenderData попадает в packed field вместе с material/lightmap данными.</param> /// <param name="MaterialIndexLo">[0x04..0x05] Индекс материала</param>
/// <param name="LightmapIndex">[0x05..0x06] Индекс карты освещения</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="IndexCount0x06">[0x08..0x0A] Количество индексов из component 0x06.</param> /// <param name="IndexCount0x06">[0x08..0x0A] Количество индексов из component 0x06.</param>
/// <param name="IndexStart0x06">[0x0A..0x0E] Первый индекс в component 0x06.</param> /// <param name="IndexStart0x06">[0x0A..0x0E] Первый индекс в component 0x06.</param>
@@ -60,8 +64,9 @@ public static class Msh0x0D
/// <param name="BaseVertex0x03">[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 MaterialIndexHi,
ushort Opaque04, byte MaterialIndexLo,
byte LightmapIndex,
ushort LocalBatchIndex, ushort LocalBatchIndex,
ushort IndexCount0x06, ushort IndexCount0x06,
uint IndexStart0x06, uint IndexStart0x06,
+1 -1
View File
@@ -185,7 +185,7 @@ public sealed class MshConverter
continue; continue;
} }
writer.WriteLine($"# batch {batchIndex}, material {batch.MaterialIndex}, flags {batch.Flags}"); writer.WriteLine($"# batch {batchIndex}, material {batch.MaterialIndexLo}, flags {batch.Flags}");
for (var i = 0; i + 2 < batch.IndexCount0x06; i += 3) for (var i = 0; i + 2 < batch.IndexCount0x06; i += 3)
{ {
@@ -88,7 +88,7 @@ public static class MshRestPoseBuilder
{ {
try try
{ {
return Convert.ToInt32(node.ParentIndexOrMinusOne); return Convert.ToInt32(node.ParentIndexOrLink);
} }
catch catch
{ {
@@ -134,7 +134,7 @@ public static class MshViewportLoader
var outIndices = new List<uint>(); var outIndices = new List<uint>();
var batchTriangleCount = 0; var batchTriangleCount = 0;
var material = materialLibrary.FindMaterial(batch.MaterialIndex) ?? ViewportMaterial.Untextured; var material = materialLibrary.FindMaterial(batch.MaterialIndexLo) ?? ViewportMaterial.Untextured;
var vertexColor = material.HasTexture ? Vector3.One : debugColor; var vertexColor = material.HasTexture ? Vector3.One : debugColor;
for (var i = 0; i + 2 < batch.IndexCount0x06; i += 3) for (var i = 0; i + 2 < batch.IndexCount0x06; i += 3)
@@ -183,7 +183,7 @@ public static class MshViewportLoader
return Vector2.Zero; return Vector2.Zero;
var uv = uvs[vertexIndex]; var uv = uvs[vertexIndex];
return new Vector2(uv.U / 1024.0f, 1.0f - uv.V / 1024.0f); return new Vector2(uv.U / 1024.0f, uv.V / 1024.0f);
} }
private static Vector3 ToNumericsVector3(Common.Vector3 position) private static Vector3 ToNumericsVector3(Common.Vector3 position)
@@ -46,9 +46,9 @@ public sealed class WeaMaterialLibrary
foreach (var materialRef in materialRefs) foreach (var materialRef in materialRefs)
{ {
var texture = TryLoadMaterialTexture(gl, materialFs, parseResult.Archive, materialRef.Name); var texture = TryLoadMaterialTexture(gl, materialFs, parseResult.Archive, materialRef.Name, out var texm);
result[materialRef.Id] = texture != null result[materialRef.Id] = texture != null
? new ViewportMaterial(materialRef.Name, texture.Value) ? new ViewportMaterial(materialRef.Name, texture.Value, texm)
: new ViewportMaterial(materialRef.Name); : new ViewportMaterial(materialRef.Name);
} }
@@ -194,9 +194,10 @@ public sealed class WeaMaterialLibrary
} }
private static uint? TryLoadMaterialTexture( private static uint? TryLoadMaterialTexture(
GL gl, FileStream materialFs, NResArchive materialArchive, string materialName GL gl, FileStream materialFs, NResArchive materialArchive, string materialName, out TexmFile? texm
) )
{ {
texm = null;
var entry = FindMaterialEntry(materialArchive, materialName); var entry = FindMaterialEntry(materialArchive, materialName);
if (entry == null) if (entry == null)
return null; return null;
@@ -235,6 +236,7 @@ public sealed class WeaMaterialLibrary
if (texmResult.TexmFile == null) if (texmResult.TexmFile == null)
return null; return null;
texm = texmResult.TexmFile;
var rgba = texmResult.TexmFile.GetRgba32BytesFromMipmap(0, out var width, out var height); var rgba = texmResult.TexmFile.GetRgba32BytesFromMipmap(0, out var width, out var height);
return ViewportTextureLoader.CreateRgbaTexture(gl, rgba, width, height); return ViewportTextureLoader.CreateRgbaTexture(gl, rgba, width, height);
} }
@@ -1,3 +1,5 @@
using TexmLib;
namespace NResUI.Rendering.Viewport; namespace NResUI.Rendering.Viewport;
public sealed class ViewportMaterial public sealed class ViewportMaterial
@@ -6,10 +8,13 @@ public sealed class ViewportMaterial
public uint TextureHandle { get; } public uint TextureHandle { get; }
public bool HasTexture => TextureHandle != 0; public bool HasTexture => TextureHandle != 0;
public ViewportMaterial(string name, uint textureHandle = 0) public TexmFile? Texm { get; }
public ViewportMaterial(string name, uint textureHandle = 0, TexmFile? texm = null)
{ {
Name = name; Name = name;
TextureHandle = textureHandle; TextureHandle = textureHandle;
Texm = texm;
} }
public static ViewportMaterial Untextured { get; } = new("Untextured"); public static ViewportMaterial Untextured { get; } = new("Untextured");
+2 -2
View File
@@ -277,9 +277,9 @@ public record class TexmFile(
// swap endianess back // swap endianess back
// (rawPixel[0], rawPixel[1], rawPixel[2], rawPixel[3]) = (rawPixel[3], rawPixel[2], rawPixel[1], rawPixel[0]); // (rawPixel[0], rawPixel[1], rawPixel[2], rawPixel[3]) = (rawPixel[3], rawPixel[2], rawPixel[1], rawPixel[0]);
var r = rawPixel[0]; var b = rawPixel[0];
var g = rawPixel[1]; var g = rawPixel[1];
var b = rawPixel[2]; var r = rawPixel[2];
var a = rawPixel[3]; var a = rawPixel[3];
result[i + 0] = r; result[i + 0] = r;