Skip to content

Commit

Permalink
Added .skin import/export
Browse files Browse the repository at this point in the history
  • Loading branch information
gul4sch committed Aug 11, 2024
1 parent e01d895 commit 3446eef
Show file tree
Hide file tree
Showing 4 changed files with 281 additions and 38 deletions.
26 changes: 13 additions & 13 deletions Warcraft.NET.Docs/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Warcraft.NET.Docs.Steps;
using Warcraft.NET.Files.M2;
using Warcraft.NET.Files.phys;
using Warcraft.NET.Files.SKIN;

namespace Warcraft.NET.Docs
{
Expand All @@ -12,25 +13,24 @@ internal class Program
/// <param name="args"></param>
static void Main(string[] args)
{
Console.WriteLine($"BaseDirectory: {AppDomain.CurrentDomain.BaseDirectory}");

Console.WriteLine($"BaseDirectory: {AppDomain.CurrentDomain.BaseDirectory}");
if (args.Length == 0)
throw new System.Exception("Please provide an output folder");

if (args.Length == 0)
throw new System.Exception("Please provide an output folder");
string outputFolder = Path.GetFullPath(args[0]);
if (!Directory.Exists(outputFolder))
throw new Exception("Output folder does not exist");

string outputFolder = Path.GetFullPath(args[0]);
if (!Directory.Exists(outputFolder))
throw new Exception("Output folder does not exist");
Console.WriteLine($"Output folder: {outputFolder}");

Console.WriteLine($"Output folder: {outputFolder}");
Console.WriteLine("Generating documentation...");
var autoDocData = GenerateAutoDocDataStep.Process();

Console.WriteLine("Generating documentation...");
var autoDocData = GenerateAutoDocDataStep.Process();
Console.WriteLine("Converting to markdown...");
ConvertToMarkdownStep.Process(autoDocData, outputFolder);

Console.WriteLine("Converting to markdown...");
ConvertToMarkdownStep.Process(autoDocData, outputFolder);

Console.WriteLine("Done!");
Console.WriteLine("Done!");

}
}
Expand Down
27 changes: 27 additions & 0 deletions Warcraft.NET/Extensions/ExtendedIO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Numerics;
using Warcraft.NET.Exceptions;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Warcraft.NET.Extensions
{
Expand Down Expand Up @@ -410,6 +411,32 @@ public static void WriteChunkSignature(this BinaryWriter binaryWriter, string si
}
}

/// <summary>
/// Writes a struct to the BinaryWriter.
/// </summary>
/// <typeparam name="T">The type of struct to write.</typeparam>
/// <param name="binaryWriter">The BinaryWriter instance.</param>
/// <param name="value">The struct value to write.</param>
public static void WriteStruct<T>(this BinaryWriter binaryWriter, T value) where T : struct
{
int size = Marshal.SizeOf<T>();
byte[] bytes = new byte[size];

IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(value, ptr, true);
Marshal.Copy(ptr, bytes, 0, size);
}
finally
{
Marshal.FreeHGlobal(ptr);
}

// Write the padded bytes
binaryWriter.Write(bytes);
}

/// <summary>
/// Writes a 12-byte <see cref="Rotator"/> value to the data stream in Pitch/Yaw/Roll order.
/// </summary>
Expand Down
192 changes: 167 additions & 25 deletions Warcraft.NET/Files/SKIN/Skin.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,194 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Warcraft.NET.Attribute;
using Warcraft.NET.Extensions;
using Warcraft.NET.Files.Interfaces;
using Warcraft.NET.Files.phys.Chunks;
using Warcraft.NET.Files.Structures;

namespace Warcraft.NET.Files.phys
namespace Warcraft.NET.Files.SKIN
{
[AutoDocFile("skin")]
public class Skin
public class Skin
{
public List<ushort> Vertices{ get; set; } //index into M2s vertex list with offset <globalVertexOffset>
public List<M2Triangle> Triangles { get; set; } //3 indices per entry into vertices
public List<BoneStruct> BoneIndices { get; set; }
public List<M2SkinSection> Submeshes { get; set; }
public List<M2Batch> TextureUnits { get; set; }

public UInt32 magic;

public M2Array vertices;
public M2Array indices;
public M2Array bones;
public M2Array submeshes;
public M2Array batches;
public UInt32 globalVertexOffset;
public M2Array shadow_batches;

public UInt32 unk0;
public UInt32 unk1;
public UInt32 globalVertexOffset; //start offset into M2.Vertices -> something else in wotlk

public List<M2ShadowBatch> ShadowBatches;

public byte[] unk0; //Padding?

public bool wotlk = false;

public Skin(byte[] inData)
{
using (var ms = new MemoryStream(inData))
using (var br = new BinaryReader(ms))
{
magic = br.ReadUInt32();
vertices = new M2Array(br.ReadUInt32(),br.ReadUInt32());
indices = new M2Array(br.ReadUInt32(), br.ReadUInt32());
bones = new M2Array(br.ReadUInt32(), br.ReadUInt32());
submeshes = new M2Array(br.ReadUInt32(), br.ReadUInt32());
batches = new M2Array(br.ReadUInt32(), br.ReadUInt32());
var magic = br.ReadUInt32();
var nVertices = br.ReadUInt32();
var ofsVertices = br.ReadUInt32();
var nIndices = br.ReadUInt32();
var ofsIndices = br.ReadUInt32();
if (ofsVertices == 48)
{
wotlk = true;
}
var nBones = br.ReadUInt32();
var ofsBones = br.ReadUInt32();
var nSubmeshes = br.ReadUInt32();
var ofsSubmeshes = br.ReadUInt32();
var nBatches = br.ReadUInt32();
var ofsBatches = br.ReadUInt32();
globalVertexOffset = br.ReadUInt32();
shadow_batches = new M2Array(br.ReadUInt32(), br.ReadUInt32());
unk0 = br.ReadUInt32();
unk1 = br.ReadUInt32();

ShadowBatches = new List<M2ShadowBatch>();
unk0 = new byte[8];
if (!wotlk)
{
var nShadow_batches = br.ReadUInt32();
var ofsShadow_batches = br.ReadUInt32();
unk0 = br.ReadBytes(8);
ShadowBatches = ReadStructList<M2ShadowBatch>(nShadow_batches, ofsShadow_batches, br);
}

Vertices = ReadStructList<ushort>(nVertices, ofsVertices, br);
Triangles = ReadStructList<M2Triangle>(nIndices/3, ofsIndices, br);
BoneIndices = ReadStructList<BoneStruct>(nBones, ofsBones, br);
Submeshes = ReadStructList<M2SkinSection>(nSubmeshes, ofsSubmeshes, br);
TextureUnits = ReadStructList<M2Batch>(nBatches, ofsBatches, br);

}
}
private List<T> ReadStructList<T>(uint count, uint offset, BinaryReader br) where T : struct
{
br.BaseStream.Position = offset;
List<T> list = new List<T>();

byte[] Serialize()
for (var i = 0; i < count; i++)
list.Add(br.ReadStruct<T>());

return list;
}

public byte[] Serialize(long offset = 0)
{
return null;
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms))
{
bw.Write(new byte[64]); //placeholder header
foreach (ushort vertex in Vertices)
{
bw.Write(vertex);
}


int _ofsTriangles = (int)bw.BaseStream.Position;
foreach (M2Triangle triangle in Triangles)
{
bw.WriteStruct(triangle);
}


int _ofsBones = (int)bw.BaseStream.Position;
foreach (BoneStruct bone in BoneIndices)
{
bw.WriteStruct(bone);

}


int _ofsSubmeshes = (int)bw.BaseStream.Position;
foreach (M2SkinSection submesh in Submeshes)
{
bw.WriteStruct(submesh);
}


int _ofsTexUnits = (int)bw.BaseStream.Position;
foreach (M2Batch texUnit in TextureUnits)
{
bw.WriteStruct(texUnit);
}

int _ofsShadowBatches = (int)bw.BaseStream.Position;
if (!wotlk) {
foreach (M2ShadowBatch shadowBatch in ShadowBatches)
{
bw.WriteStruct(shadowBatch);
}
}





//Writing header
bw.BaseStream.Position = 0;
bw.Write('S');
bw.Write('K');
bw.Write('I');
bw.Write('N');

bw.Write(Vertices.Count);
var _ofsVertices = 64;
if (wotlk)
_ofsVertices = 48;
bw.Write(_ofsVertices);

bw.Write(Triangles.Count*3);
bw.Write(_ofsTriangles);

bw.Write(BoneIndices.Count);
bw.Write(_ofsBones);

bw.Write(Submeshes.Count);
bw.Write(_ofsSubmeshes);

bw.Write(TextureUnits.Count);
bw.Write(_ofsTexUnits);

bw.Write(globalVertexOffset);

if (!wotlk)
{
bw.Write(ShadowBatches.Count);
bw.Write(_ofsShadowBatches);
bw.Write(unk0);
}


return ms.ToArray();
}
}

/// <inheritdoc/>
public uint GetSize()
{
return (uint)Serialize().Length;
}

/*
public byte[] addPadding(byte[] bytes, int size)
{
// Calculate padding
int paddingSize = (4 - (size % 4)) % 4;
byte[] paddedBytes = new byte[size + paddingSize];
Array.Copy(bytes, paddedBytes, size);
return paddedBytes;
}
*/

}


}


74 changes: 74 additions & 0 deletions Warcraft.NET/Files/SKIN/SkinStructs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Text;
using Warcraft.NET.Files.Structures;

namespace Warcraft.NET.Files.SKIN
{
public struct M2SkinSection
{
public UInt16 skinSectionId; // Mesh part ID, see below.
public UInt16 Level; // (level << 16) is added (|ed) to startTriangle and alike to avoid having to increase those fields to uint32s.
public UInt16 vertexStart; // Starting vertex number.
public UInt16 vertexCount; // Number of vertices.
public UInt16 indexStart; // Starting triangle index (that's 3* the number of triangles drawn so far).
public UInt16 indexCount; // Number of triangle indices.
public UInt16 boneCount; // Number of elements in the bone lookup table. Max seems to be 256 in Wrath. Shall be ≠ 0.
public UInt16 boneComboIndex; // Starting index in the bone lookup table.
public UInt16 boneInfluences; // <= 4
// from <=BC documentation: Highest number of bones needed at one time in this Submesh --Tinyn (wowdev.org)
// In 2.x this is the amount of of bones up the parent-chain affecting the submesh --NaK
// Highest number of bones referenced by a vertex of this submesh. 3.3.5a and suspectedly all other client revisions. -- Skarn
public UInt16 centerBoneIndex;
public C3Vector centerPosition; // Average position of all the vertices in the sub mesh.
//≥BC
public C3Vector sortCenterPosition; // The center of the box when an axis aligned box is built around the vertices in the submesh.
public float sortRadius; // Distance of the vertex farthest from CenterBoundingBox.
}

public struct BoneStruct
{
public byte b1;
public byte b2;
public byte b3;
public byte b4;
}

public struct M2Batch
{
public byte flags; // Usually 16 for static textures, and 0 for animated textures. &0x1: materials invert something; &0x2: transform &0x4: projected texture; &0x10: something batch compatible; &0x20: projected texture?; &0x40: possibly don't multiply transparency by texture weight transparency to get final transparency value(?)
public sbyte priorityPlane;
public UInt16 shader_id; // See below.
public UInt16 skinSectionIndex; // A duplicate entry of a submesh from the list above.
public UInt16 geosetIndex; // See below. New name: flags2. 0x2 - projected. 0x8 - EDGF chunk in m2 is mandatory and data from is applied to this mesh
public UInt16 colorIndex; // A Color out of the Colors-Block or -1 if none.
public UInt16 materialIndex; // The renderflags used on this texture-unit.
public UInt16 materialLayer; // Capped at 7 (see CM2Scene::BeginDraw)
public UInt16 textureCount; // 1 to 4. See below. Also seems to be the number of textures to load, starting at the texture lookup in the next field (0x10).
public UInt16 textureComboIndex; // Index into Texture lookup table
public UInt16 textureCoordComboIndex; // Index into the texture mapping lookup table.
public UInt16 textureWeightComboIndex; // Index into transparency lookup table.
public UInt16 textureTransformComboIndex; // Index into uvanimation lookup table.
}

public struct M2ShadowBatch
{
byte flags; // if auto-generated: M2Batch.flags & 0xFF
byte flags2; // if auto-generated: (renderFlag[i].flags & 0x04 ? 0x01 : 0x00)
// | (!renderFlag[i].blendingmode ? 0x02 : 0x00)
// | (renderFlag[i].flags & 0x80 ? 0x04 : 0x00)
// | (renderFlag[i].flags & 0x400 ? 0x06 : 0x00)
public UInt16 _unknown1;
public UInt16 submesh_id;
public UInt16 texture_id; // already looked-up
public UInt16 color_id;
public UInt16 transparency_id; // already looked-up
}

public struct M2Triangle //Left handed
{
public ushort V1;
public ushort V3;
public ushort V2;
}
}

0 comments on commit 3446eef

Please sign in to comment.