Created
August 10, 2020 16:18
-
-
Save akderebur/24bfe5ff1c1407915b98c5153b9dc24b to your computer and use it in GitHub Desktop.
Very crude OBJ exporter for GoW 2018 that needs a lot of work. Only vertex position and indices. Most of the meshes won't be loaded because of wrong offset/file being read. It will just export the base meshes in most cases.
This file contains 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.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
namespace GowObj | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
LoadMG(File.ReadAllBytes(@"MG_heroa00_0"), File.ReadAllBytes(@"MG_heroa00_0_gpu")); | |
} | |
private static void LoadMG(byte[] mgFile, byte[] mgGpu) | |
{ | |
List<Mesh> expMeshes = new List<Mesh>(); | |
MemoryStream fs = new MemoryStream(mgFile); | |
BinaryReader br = new BinaryReader(fs); | |
MemoryStream gFs = new MemoryStream(mgGpu); | |
BinaryReader gBr = new BinaryReader(gFs); | |
fs.Position = 56; | |
int mainEntC = br.ReadInt16(); | |
long mainS = 76; | |
for (int a = 0; a < mainEntC; a++) | |
{ | |
fs.Position = mainS + 4 * a; | |
int meshOff = br.ReadInt32(); | |
fs.Position = meshOff + 2; | |
int smC = br.ReadByte(); | |
for (int s = 0; s < smC; s++) | |
{ | |
fs.Position = meshOff + 60 + 4 * s; | |
int smOff = br.ReadInt32(); | |
fs.Position = meshOff + smOff; | |
int partC = br.ReadInt32(); | |
if (partC == 0) | |
continue; | |
Console.WriteLine("SM - " + (meshOff + smOff) + " - " + meshOff); | |
long partOffS = fs.Position + 8; | |
for (int p = 0; p < partC; p++) | |
{ | |
fs.Position = partOffS + 4 * p; | |
long partOff = fs.Position + br.ReadInt32(); | |
//long smBase = meshOff + smOff + 16; | |
long smBase = partOff; | |
fs.Position = smBase + 48; | |
int indOff = br.ReadInt32(); | |
int ind2Off = br.ReadInt32(); | |
int vertOff = br.ReadInt32(); | |
int vert2Off = br.ReadInt32(); | |
int vertC = br.ReadInt32(); | |
int indC = br.ReadInt32(); | |
if (vertC < 300) // For removing collision meshes | |
continue; | |
fs.Position = smBase + 16; | |
Vector3 extent = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); | |
Vector3 origin = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle()); | |
Vector3 scale = new Vector3(extent.x * 2, extent.y * 2, extent.z * 2); | |
Vector3 min = new Vector3(origin.x - extent.x, origin.y - extent.y, origin.z - extent.z); | |
fs.Position = smBase + 84; | |
int vbDOff = br.ReadInt32(); | |
int vbOffsOffs = br.ReadInt32(); | |
fs.Position = smBase + vbDOff - 8; | |
int buffC = br.ReadByte(); | |
br.ReadInt16(); | |
int compC = br.ReadByte(); | |
List<VertComp> vertComps = new List<VertComp>(); | |
long vbDS = smBase + vbDOff; | |
fs.Position = vbDS; | |
for (int d = 0; d < compC; d++) | |
{ | |
VertComp vc = new VertComp() | |
{ | |
compId = br.ReadByte(), | |
dataType = br.ReadByte(), | |
elemC = br.ReadByte(), | |
buffOff = br.ReadByte(), | |
buffId = br.ReadByte() | |
}; | |
fs.Position += 3; | |
vertComps.Add(vc); | |
} | |
List<VBuffer> vBuffs = new List<VBuffer>(); | |
fs.Position = smBase + vbOffsOffs; | |
for (int b = 0; b < buffC; b++) | |
{ | |
VBuffer vBuff = new VBuffer(); | |
vBuff.offset = br.ReadInt32(); | |
List<VertComp> buffComps = vertComps.Where(v => v.buffId == b).ToList(); | |
if (buffComps == null || buffComps.Count == 0) | |
continue; | |
VertComp lastComp = buffComps.OrderByDescending(v => v.buffOff).First(); | |
int stride = lastComp.buffOff; | |
switch (lastComp.dataType) | |
{ | |
case 0: | |
case 3: | |
stride += lastComp.elemC * 4; | |
break; | |
case 4: | |
case 6: | |
stride += lastComp.elemC * 2; | |
break; | |
default: | |
break; | |
} | |
vBuff.stride = stride; | |
vBuffs.Add(vBuff); | |
} | |
// Vert Only | |
VertComp vp = vertComps.FirstOrDefault(v => v.compId == 0); | |
if (vp == null) | |
continue; | |
long vbS = vBuffs[vp.buffId].offset; | |
int vpStride = vBuffs[vp.buffId].stride; | |
int maxX = 0; | |
for (int v = 0; v < vertC; v++) | |
{ | |
gFs.Position = vbS + vpStride * v; | |
if (vp.dataType == 6) | |
{ | |
int xVal = br.ReadUInt16(); | |
int yVal = br.ReadUInt16(); | |
if (yVal > maxX) | |
maxX = yVal; | |
} | |
} | |
Vector3[] vertices = new Vector3[vertC]; | |
uint[] indices = new uint[indC]; | |
for (int v = 0; v < vertC; v++) | |
{ | |
gFs.Position = vbS + vpStride * v; | |
if (vp.dataType == 6) | |
{ | |
Vector3 vec = new Vector3(gBr.ReadUInt16() / 65535f, gBr.ReadUInt16() / 65535f, gBr.ReadUInt16() / 65535f); | |
vertices[v] = new Vector3(vec.x * scale.x + min.x, vec.y * scale.y + min.y, vec.z * scale.z + min.z); | |
} | |
else | |
vertices[v] = new Vector3(gBr.ReadSingle(), gBr.ReadSingle(), gBr.ReadSingle()); | |
} | |
gFs.Position = indOff; | |
for (int i = 0; i < indC; i++) | |
{ | |
indices[i] = gBr.ReadUInt16(); | |
} | |
// Wrong offset / data / file ??? | |
uint vMax = indices.Max(i => i); | |
if (vMax != (vertC - 1)) | |
{ | |
continue; | |
} | |
Mesh mesh = new Mesh() | |
{ | |
vertices = vertices, | |
indices = indices | |
}; | |
expMeshes.Add(mesh); | |
} | |
} | |
} | |
br.Close(); | |
gBr.Close(); | |
ExportOBJ(expMeshes); | |
} | |
public static void ExportOBJ(List<Mesh> meshes) | |
{ | |
if (meshes == null || meshes.Count == 0) | |
return; | |
int startIndex = 0, mIndex = -1; | |
StringBuilder objS = new StringBuilder(); | |
foreach (var mesh in meshes) | |
{ | |
int vertCount = mesh.vertices.Length; | |
int indCount = mesh.indices.Length; | |
objS.AppendLine("g mesh" + ++mIndex); | |
StringBuilder vertS = new StringBuilder(); | |
for (int i = 0; i < vertCount; i++) | |
{ | |
Vector3 vert = mesh.vertices[i]; | |
vertS.AppendLine(string.Format("v {0} {1} {2}", vert.x, vert.y, vert.z)); | |
} | |
objS.Append(vertS.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] + 1 + startIndex, mesh.indices[i] + 1 + startIndex, mesh.indices[i + 2] + 1 + startIndex)); | |
} | |
startIndex += vertCount; | |
} | |
File.WriteAllText("test.obj", objS.ToString()); | |
} | |
} | |
// Binary data container | |
class VertComp | |
{ | |
public int compId { get; set; } | |
public int dataType { get; set; } | |
public int elemC { get; set; } | |
public int buffOff { get; set; } | |
public int buffId { get; set; } | |
} | |
class VBuffer | |
{ | |
public int stride { get; set; } | |
public int offset { get; set; } | |
} | |
// Helper container | |
public class Vector3 | |
{ | |
public float x, y, z; | |
public Vector3(float x, float y, float z) | |
{ | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
} | |
} | |
public class Mesh | |
{ | |
public Vector3[] vertices { get; set; } | |
public uint[] indices { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment