Skip to content

Commit

Permalink
Add support for loading and saving V6 models (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
BlueCube3310 authored Nov 22, 2024
1 parent 7fee87f commit 9ba5f0e
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 29 deletions.
20 changes: 14 additions & 6 deletions Source/SharpNeedle/HedgehogEngine/Mirage/Mesh.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
namespace SharpNeedle.HedgehogEngine.Mirage;
using System.IO;

public class Mesh : IBinarySerializable, IDisposable, ICloneable<Mesh>
public class Mesh : IBinarySerializable<uint>, IDisposable, ICloneable<Mesh>
{
public ResourceReference<Material> Material { get; set; }
public ushort[] Faces { get; set; }
public uint VertexSize { get; set; }
public uint VertexCount { get; set; }
public byte[] Vertices { get; set; }
public byte[] BoneIndices { get; set; }
public short[] BoneIndices { get; set; }
public List<VertexElement> Elements { get; set; }
public List<TextureUnit> Textures { get; set; }
public MeshSlot Slot { get; set; }

public void Read(BinaryObjectReader reader)
public void Read(BinaryObjectReader reader, uint version)
{
Elements ??= new List<VertexElement>();
Material = reader.ReadStringOffset();
Expand All @@ -36,12 +36,16 @@ public void Read(BinaryObjectReader reader)
}
});

BoneIndices = reader.ReadArrayOffset<byte>(reader.Read<int>());
if(version >= 6)
BoneIndices = reader.ReadArrayOffset<short>(reader.Read<int>());
else
BoneIndices = reader.ReadArrayOffset<byte>(reader.Read<int>()).Select(Convert.ToInt16).ToArray();

Textures = reader.ReadObject<BinaryList<BinaryPointer<TextureUnit>>>().Unwind();
SwapVertexEndianness();
}

public void Write(BinaryObjectWriter writer)
public void Write(BinaryObjectWriter writer, uint version)
{
writer.WriteStringOffset(StringBinaryFormat.NullTerminated, Path.GetFileNameWithoutExtension(Material.Name));
writer.Write(Faces.Length);
Expand Down Expand Up @@ -69,7 +73,11 @@ public void Write(BinaryObjectWriter writer)
});

writer.Write(BoneIndices.Length);
writer.WriteArrayOffset(BoneIndices);

if(version >= 6)
writer.WriteArrayOffset(BoneIndices);
else
writer.WriteArrayOffset(BoneIndices.Select(Convert.ToByte).ToArray());

writer.Write(Textures.Count);
writer.WriteOffset(() =>
Expand Down
26 changes: 12 additions & 14 deletions Source/SharpNeedle/HedgehogEngine/Mirage/MeshGroup.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
namespace SharpNeedle.HedgehogEngine.Mirage;

public class MeshGroup : List<Mesh>, IBinarySerializable, IDisposable, ICloneable<MeshGroup>
public class MeshGroup : List<Mesh>, IBinarySerializable<uint>, IDisposable, ICloneable<MeshGroup>
{
public string Name { get; set; }

public void Read(BinaryObjectReader reader)
=> Read(reader, true);

public void Read(BinaryObjectReader reader, bool readSpecial)
public void Read(BinaryObjectReader reader, uint version)
{
var opaqMeshes = reader.ReadObject<BinaryList<BinaryPointer<Mesh>>>();
var transMeshes = reader.ReadObject<BinaryList<BinaryPointer<Mesh>>>();
var punchMeshes = reader.ReadObject<BinaryList<BinaryPointer<Mesh>>>();
bool readSpecial = version >= 5;

var opaqMeshes = reader.ReadObject<BinaryList<BinaryPointer<Mesh, uint>, uint>, uint>(version);
var transMeshes = reader.ReadObject<BinaryList<BinaryPointer<Mesh, uint>, uint>, uint>(version);
var punchMeshes = reader.ReadObject<BinaryList<BinaryPointer<Mesh, uint>, uint>, uint>(version);

Clear();

Expand Down Expand Up @@ -65,7 +64,7 @@ public void Read(BinaryObjectReader reader, bool readSpecial)

Name = reader.ReadString(StringBinaryFormat.NullTerminated);

void AddMeshes(BinaryList<BinaryPointer<Mesh>> meshes, MeshSlot slot)
void AddMeshes(BinaryList<BinaryPointer<Mesh, uint>, uint> meshes, MeshSlot slot)
{
foreach (var mesh in meshes)
{
Expand All @@ -75,11 +74,10 @@ void AddMeshes(BinaryList<BinaryPointer<Mesh>> meshes, MeshSlot slot)
}
}

public void Write(BinaryObjectWriter writer)
=> Write(writer, true);

public void Write(BinaryObjectWriter writer, bool writeSpecial)
public void Write(BinaryObjectWriter writer, uint version)
{
bool writeSpecial = version >= 5;

WriteMeshes(this.Where(x => x.Slot == Mesh.Type.Opaque));
WriteMeshes(this.Where(x => x.Slot == Mesh.Type.Transparent));
WriteMeshes(this.Where(x => x.Slot == Mesh.Type.PunchThrough));
Expand Down Expand Up @@ -135,7 +133,7 @@ void WriteMeshes(IEnumerable<Mesh> meshes)
writer.WriteOffset(() =>
{
foreach (var mesh in meshes)
writer.WriteObjectOffset(mesh);
writer.WriteObjectOffset(mesh, version);
});
}
}
Expand Down
14 changes: 5 additions & 9 deletions Source/SharpNeedle/HedgehogEngine/Mirage/ModelBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,9 @@ protected override void Dispose(bool disposing)
protected void CommonRead(BinaryObjectReader reader)
{
if (DataVersion >= 5)
Groups = reader.ReadObject<BinaryList<BinaryPointer<MeshGroup>>>().Unwind();
Groups = reader.ReadObject<BinaryList<BinaryPointer<MeshGroup, uint>, uint>, uint>(DataVersion).Unwind();
else
{
var group = new MeshGroup();
group.Read(reader, false);
Groups = new List<MeshGroup> { group };
}
Groups = new List<MeshGroup> { reader.ReadObject<MeshGroup, uint>(DataVersion) };
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -41,19 +37,19 @@ protected void CommonWrite(BinaryObjectWriter writer)
writer.WriteOffset(() =>
{
foreach (var group in Groups)
writer.WriteObjectOffset(group);
writer.WriteObjectOffset(group, DataVersion);
});
}
else
{
if (Groups.Count == 1)
Groups[0].Write(writer, false);
writer.WriteObject(Groups[0], DataVersion);
else
{
var dummyMeshGroup = new MeshGroup();
dummyMeshGroup.Capacity = Groups.Sum(x => x.Count);
dummyMeshGroup.AddRange(Groups.SelectMany(x => x));
dummyMeshGroup.Write(writer, false);
writer.WriteObject(dummyMeshGroup, DataVersion);
}
}
}
Expand Down
9 changes: 9 additions & 0 deletions Source/SharpNeedle/Utilities/BinaryHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ public static void WriteOffsetValue(this BinaryObjectWriter writer, long value)
return result;
}

public static List<T> Unwind<T, TContext>(this BinaryList<BinaryPointer<T, TContext>, TContext> list) where T : IBinarySerializable<TContext>, new()
{
var result = new List<T>(list.Count);
foreach (var item in list)
result.Add(item);

return result;
}

public static unsafe T* Pointer<T>(this T[] data) where T : unmanaged
{
if (data == null || data.Length == 0)
Expand Down
109 changes: 109 additions & 0 deletions Source/SharpNeedle/Utilities/BinaryList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,113 @@ public T this[int index]

public static implicit operator BinaryList<T>(List<T> items) => new(items);
public static implicit operator List<T>(BinaryList<T> self) => self.Items;
}

public struct BinaryList<T, TContext> : IBinarySerializable<TContext>, IList<T> where T : IBinarySerializable<TContext>, new()
{
public List<T> Items { get; set; }
public int Count => Items.Count;
public bool IsReadOnly => false;

public BinaryList(List<T> items)
{
Items = items;
}


[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Read(BinaryObjectReader reader, TContext context)
{
var count = reader.ReadOffsetValue();

// Use token because accessing `this` with lambda on structs is a nightmare
var offset = reader.ReadOffsetValue();
if (offset == 0)
return;

Items = new List<T>((int)count);
using var token = reader.AtOffset(offset);
for (long i = 0; i < count; i++)
{
var obj = reader.ReadObject<T, TContext>(context);
Items.Add(obj);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(BinaryObjectWriter writer, TContext context)
{
if (Items == null)
{
writer.WriteOffsetValue(0);
writer.WriteOffsetValue(0);
return;
}

writer.WriteOffsetValue(Items.Count);
writer.WriteOffset(writer.DefaultAlignment, null, Items, (_, items) =>
{
foreach (var item in (List<T>)items)
writer.WriteObject(item, context);
});
}

public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)Items).GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

public void Add(T item)
{
Items.Add(item);
}

public void Clear()
{
Items.Clear();
}

public bool Contains(T item)
{
return Items.Contains(item);
}

public void CopyTo(T[] array, int arrayIndex)
{
Items.CopyTo(array, arrayIndex);
}

public bool Remove(T item)
{
return Items.Remove(item);
}

public int IndexOf(T item)
{
return Items.IndexOf(item);
}

public void Insert(int index, T item)
{
Items.Insert(index, item);
}

public void RemoveAt(int index)
{
Items.RemoveAt(index);
}

public T this[int index]
{
get => Items[index];
set => Items[index] = value;
}

public static implicit operator BinaryList<T, TContext>(List<T> items) => new(items);
public static implicit operator List<T>(BinaryList<T, TContext> self) => self.Items;
}
32 changes: 32 additions & 0 deletions Source/SharpNeedle/Utilities/BinaryPointer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,36 @@ public void Write(BinaryObjectWriter writer)

public static implicit operator T(BinaryPointer<T> self) => self.Value;
public static implicit operator BinaryPointer<T>(T value) => new(value);
}

public struct BinaryPointer<T, TContext> : IBinarySerializable<TContext> where T : IBinarySerializable<TContext>, new()
{
public T Value;

public BinaryPointer(T value)
{
Value = value;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Read(BinaryObjectReader reader, TContext context)
{
var offset = reader.ReadOffsetValue();
if (offset == 0)
return;

Value = reader.ReadObjectAtOffset<T, TContext>(offset, context);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(BinaryObjectWriter writer, TContext context)
{
if (Value is null)
writer.WriteOffsetValue(0);
else
writer.WriteObjectOffset(Value, context);
}

public static implicit operator T(BinaryPointer<T, TContext> self) => self.Value;
public static implicit operator BinaryPointer<T, TContext>(T value) => new(value);
}

0 comments on commit 9ba5f0e

Please sign in to comment.