-
-
Save coolzoom/57ba0b5e6c6f817767ecfeba25dd9dc3 to your computer and use it in GitHub Desktop.
Wildstar M3 Model Load/Export
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Text; | |
namespace M3Exporter | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
if (args.Length == 0) | |
return; | |
string file = args[0]; | |
float divider = short.MaxValue; | |
FileStream fs = new FileStream(file, FileMode.Open); | |
BinaryReader reader = new BinaryReader(fs); | |
int magic = reader.ReadInt32(); | |
if (magic != 1297040460) // LDOM | |
return; | |
fs.Seek(600, SeekOrigin.Begin); | |
long mTOff = reader.ReadInt64(); // mesh table offset | |
fs.Seek(1584 + mTOff, SeekOrigin.Begin); // 1584 = Header End | |
// Mesh Table | |
fs.Seek(24, SeekOrigin.Current); | |
int vertCount = reader.ReadInt32(); | |
int blockLen = reader.ReadInt16(); | |
fs.Seek(74, SeekOrigin.Current); | |
int indCount = reader.ReadInt32(); | |
fs.Seek(12, SeekOrigin.Current); | |
long indOff = reader.ReadInt64(); | |
long smCount = reader.ReadInt64(); // submeshes | |
long smTOff = reader.ReadInt64(); | |
// Note data offsets | |
long vbStart = fs.Position + 64; | |
long indStart = vbStart + indOff; | |
long smStart = vbStart + smTOff; | |
// Help for arrays | |
int vInd = 0, uvInd = 0; | |
var meshes = new Submesh[smCount]; | |
// Submesh Table | |
for (int s = 0; s < smCount; s++) | |
{ | |
fs.Seek(smStart + 112 * s, SeekOrigin.Begin); | |
int startIndex = reader.ReadInt32(); | |
int startVertex = reader.ReadInt32(); | |
int sIndCount = reader.ReadInt32(); | |
int sVertCount = reader.ReadInt32(); | |
var sVertices = new float[sVertCount * 3]; | |
var sUVs = new float[sVertCount * 2]; | |
var sIndices = new int[sIndCount]; | |
// Read mesh data | |
fs.Seek(vbStart + blockLen * startVertex, SeekOrigin.Begin); | |
for (int v = 0; v < sVertCount; v++) | |
{ | |
vInd = v * 3; | |
uvInd = v * 2; | |
// Vertex position | |
sVertices[vInd] = reader.ReadInt16() / divider; | |
sVertices[vInd + 1] = reader.ReadInt16() / divider; | |
sVertices[vInd + 2] = reader.ReadInt16() / divider; | |
// UV | |
fs.Seek(blockLen - 10, SeekOrigin.Current); // assume uv always last 4 bytes | |
sUVs[uvInd] = Half.ToHalf(reader.ReadBytes(2), 0); | |
sUVs[uvInd + 1] = Half.ToHalf(reader.ReadBytes(2), 0); | |
} | |
fs.Seek(indStart + 2 * startIndex, SeekOrigin.Begin); | |
for (int i = 0; i < sIndCount; i++) | |
sIndices[i] = reader.ReadUInt16(); | |
Submesh sm = new Submesh() | |
{ | |
vertices = sVertices, | |
uvs = sUVs, | |
indices = sIndices | |
}; | |
meshes[s] = sm; | |
} | |
reader.Close(); | |
// Export meshes | |
ExportOBJ(meshes); | |
} | |
public static void ExportOBJ(Submesh[] meshes) | |
{ | |
if (meshes == null || meshes.Length == 0) | |
return; | |
int startIndex = 0, mIndex = -1; | |
StringBuilder objS = new StringBuilder(); | |
foreach (var mesh in meshes) | |
{ | |
int vertCount = mesh.vertices.Length / 3; | |
int indCount = mesh.indices.Length; | |
objS.AppendLine("g mesh" + ++mIndex); | |
StringBuilder vertS = new StringBuilder(); | |
StringBuilder uvS = new StringBuilder(); | |
for (int i = 0; i < vertCount; i++) | |
{ | |
int vInd = 3 * i; | |
int uvInd = 2 * i; | |
vertS.AppendLine(string.Format("v {0} {1} {2}", mesh.vertices[vInd], mesh.vertices[vInd + 1], mesh.vertices[vInd + 2])); | |
uvS.AppendLine(string.Format("vt {0} {1}", mesh.uvs[uvInd], (1f - mesh.uvs[uvInd + 1]))); | |
} | |
objS.Append(vertS.ToString()); | |
objS.Append(uvS.ToString()); | |
// faces | |
for (int i = 0; i < indCount; i += 3) | |
{ | |
objS.AppendLine(string.Format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}", mesh.indices[i] + 1 + startIndex, mesh.indices[i + 1] + 1 + startIndex, mesh.indices[i + 2] + 1 + startIndex)); | |
} | |
startIndex += vertCount; | |
} | |
File.WriteAllText("test.obj", objS.ToString()); | |
} | |
public class Submesh | |
{ | |
public float[] vertices { get; set; } | |
public float[] uvs { get; set; } | |
public int[] indices { get; set; } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment