Skip to content

Instantly share code, notes, and snippets.

@akderebur
Created August 10, 2020 16:18
Show Gist options
  • Save akderebur/24bfe5ff1c1407915b98c5153b9dc24b to your computer and use it in GitHub Desktop.
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.
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