diff options
author | Treeki <treeki@gmail.com> | 2011-03-05 05:25:14 +0100 |
---|---|---|
committer | Treeki <treeki@gmail.com> | 2011-03-05 05:25:14 +0100 |
commit | e962fd89af865d6e01522e9752f5fbd855ce128a (patch) | |
tree | 0fb19e74a2d87b2ece48152fee2b2f4ed963828c /NW4RTools/ObjExporter.cs | |
parent | a5b6dc1789f2e06a26fe6a0510aee04aeccdc70b (diff) | |
download | nw4rtools-e962fd89af865d6e01522e9752f5fbd855ce128a.tar.gz nw4rtools-e962fd89af865d6e01522e9752f5fbd855ce128a.zip |
partially working obj importer. still untested in-game
Diffstat (limited to 'NW4RTools/ObjExporter.cs')
-rw-r--r-- | NW4RTools/ObjExporter.cs | 187 |
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; + } + } + } + } +} + |