summaryrefslogtreecommitdiff
path: root/NW4RTools/ObjExporter.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--NW4RTools/ObjExporter.cs187
1 files changed, 187 insertions, 0 deletions
diff --git a/NW4RTools/ObjExporter.cs b/NW4RTools/ObjExporter.cs
new file mode 100644
index 0000000..2bd791c
--- /dev/null
+++ b/NW4RTools/ObjExporter.cs
@@ -0,0 +1,187 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using NW4RTools.Models;
+
+namespace NW4RTools {
+ public class ObjExporter {
+ public static void WriteModel(TextWriter tw, ResFile file, string modelName) {
+ new ObjExporter(file).SaveModel(tw, modelName);
+ }
+
+
+
+ ResFile CurrentFile;
+ Models.Model CurrentModel;
+ TextWriter Output;
+
+ private ObjExporter(ResFile file) {
+ CurrentFile = file;
+ }
+
+ Dictionary<Models.VertexPosData, int> VtxPosOffsets;
+ Dictionary<Models.VertexNrmData, int> VtxNrmOffsets;
+ Dictionary<Models.VertexTexCoordData, int> VtxTexCoordOffsets;
+
+ public void SaveModel(TextWriter tw, string modelName) {
+ Output = tw;
+ CurrentModel = CurrentFile.GetGroup<Model>("3DModels(NW4R)")[modelName];
+
+ Output.WriteLine("# {0} exported by Treeki's NW4RTools", modelName);
+ Output.WriteLine("# {0}", DateTime.Now);
+ Output.WriteLine();
+
+ // Write vertex data pool
+ int Offset;
+
+ VtxPosOffsets = new Dictionary<VertexPosData, int>();
+ Offset = 1;
+ foreach (var kv in CurrentModel.VtxPosData) {
+ VtxPosOffsets[kv.Value] = Offset;
+ Output.WriteLine("# Vertex Positions: {0} [offset {1}]", kv.Key, Offset);
+
+ for (int i = 0; i < kv.Value.EntryCount; i++) {
+ float[] v = kv.Value.GetEntry(i);
+ Output.WriteLine("v {0} {1} {2}", v[0], v[1], v[2]);
+ }
+
+ Offset += kv.Value.EntryCount;
+ }
+
+ VtxNrmOffsets = new Dictionary<VertexNrmData, int>();
+ Offset = 1;
+ foreach (var kv in CurrentModel.VtxNrmData) {
+ VtxNrmOffsets[kv.Value] = Offset;
+ Output.WriteLine("# Vertex Normals: {0} [offset {1}]", kv.Key, Offset);
+
+ for (int i = 0; i < kv.Value.EntryCount; i++) {
+ float[] v = kv.Value.GetEntry(i);
+ Output.WriteLine("vn {0} {1} {2}", v[0], v[1], v[2]);
+ }
+
+ Offset += kv.Value.EntryCount;
+ }
+
+ VtxTexCoordOffsets = new Dictionary<VertexTexCoordData, int>();
+ Offset = 1;
+ foreach (var kv in CurrentModel.VtxTexCoordData) {
+ VtxTexCoordOffsets[kv.Value] = Offset;
+ Output.WriteLine("# Vertex TexCoords: {0} [offset {1}]", kv.Key, Offset);
+
+ for (int i = 0; i < kv.Value.EntryCount; i++) {
+ float[] v = kv.Value.GetEntry(i);
+ Output.WriteLine("vt {0} {1}", v[0], v[1]);
+ }
+
+ Offset += kv.Value.EntryCount;
+ }
+
+ Output.WriteLine();
+
+ // Write shapes
+ // TODO: replace with something using the Bytecode
+ foreach (var kv in CurrentModel.Shapes) {
+ Output.WriteLine("g {0}", kv.Key);
+
+ WriteShape(kv.Value);
+
+ Output.WriteLine();
+ }
+
+ Output.Flush();
+ }
+
+
+ private void WriteShape(Models.Shape shape) {
+ // first, parse the first DisplayList to get the attr info
+ // for now we'll hardcode the offsets. must be fixed later
+
+ var firstDL = new InputStream(shape.DisplayList1);
+ firstDL.Seek(0x0C);
+ UInt32 vtxDesc1 = firstDL.ReadUInt32();
+ firstDL.Seek(0x12);
+ UInt32 vtxDesc2 = firstDL.ReadUInt32();
+ firstDL.Seek(0x22);
+ UInt32 vtxAttr1 = firstDL.ReadUInt32();
+ firstDL.Seek(0x28);
+ UInt32 vtxAttr2 = firstDL.ReadUInt32();
+ firstDL.Seek(0x2E);
+ UInt32 vtxAttr3 = firstDL.ReadUInt32();
+
+ var vs = new VertexSettings();
+ vs.SetDesc(vtxDesc1, vtxDesc2);
+ vs.SetAttrFmt(vtxAttr1, vtxAttr2, vtxAttr3);
+
+ // get the Matrix to use
+ // todo: how to apply this?!
+ Matrix m = CurrentModel.Nodes[CurrentModel.MatrixIDtoNodeID[shape.MatrixID]].NodeMatrix;
+
+ // now parse the second DisplayList
+
+ int posOffset = VtxPosOffsets[shape.PosData];
+ int nrmOffset = shape.NrmData == null ? -1 : VtxNrmOffsets[shape.NrmData];
+ int tcOffset = shape.TexCoordData[0] == null ? -1 : VtxTexCoordOffsets[shape.TexCoordData[0]];
+ // TODO: Better DisplayList parsing, in a similar fashion to ByteCode
+ var dl = new InputStream(shape.DisplayList2);
+
+ while (true) {
+ if (dl.AtEnd)
+ break;
+
+ byte cmd = dl.ReadByte();
+ if (cmd == 0)
+ break;
+
+ PrimitiveType prim = (PrimitiveType)((cmd >> 3) & 7);
+ int vtxCount = dl.ReadUInt16();
+ Output.WriteLine("# Primitive: {0} ({1} vertices)", prim, vtxCount);
+
+ // first, parse it into a list of vertices
+ GXIndexedVertex[] vtxs = new GXIndexedVertex[vtxCount];
+ string[] pVtxs = new string[vtxCount];
+
+ for (int i = 0; i < vtxCount; i++) {
+ vtxs[i].LoadFrom(dl, vs);
+
+ string tc = (vtxs[i].TexCoords[0] == -1) ? "" : (tcOffset + vtxs[i].TexCoords[0]).ToString();
+ string n = (vtxs[i].Normal == -1) ? "" : (nrmOffset + vtxs[i].Normal).ToString();
+ pVtxs[i] = String.Format(" {0}/{1}/{2}", posOffset + vtxs[i].Position, tc, n);
+ }
+
+ switch (prim) {
+ case PrimitiveType.Triangles:
+ for (int i = 0; i < vtxCount; i += 3) {
+ Output.WriteLine("f {0} {1} {2}", pVtxs[i], pVtxs[i + 1], pVtxs[i + 2]);
+ }
+
+ break;
+
+ case PrimitiveType.TriangleStrip:
+ // De-stripify it!
+ for (int i = 2; i < vtxCount; i++) {
+ Output.Write("f");
+ if ((i & 1) == 0) {
+ // Even number
+ Output.Write(pVtxs[i - 2]);
+ Output.Write(pVtxs[i - 1]);
+ Output.Write(pVtxs[i]);
+ } else {
+ // Odd number
+ Output.Write(pVtxs[i - 1]);
+ Output.Write(pVtxs[i - 2]);
+ Output.Write(pVtxs[i]);
+ }
+ Output.WriteLine();
+ }
+
+ break;
+
+ default:
+ Output.WriteLine("# UNIMPLEMENTED");
+ return;
+ }
+ }
+ }
+ }
+}
+