summaryrefslogtreecommitdiff
path: root/NW4RTools/ColladaWriter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'NW4RTools/ColladaWriter.cs')
-rw-r--r--NW4RTools/ColladaWriter.cs344
1 files changed, 344 insertions, 0 deletions
diff --git a/NW4RTools/ColladaWriter.cs b/NW4RTools/ColladaWriter.cs
new file mode 100644
index 0000000..85b6867
--- /dev/null
+++ b/NW4RTools/ColladaWriter.cs
@@ -0,0 +1,344 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Text;
+using NW4RTools.Models;
+using Collada141;
+
+namespace NW4RTools {
+ public class ColladaWriter {
+ public static void WriteModel(Stream outputStream, ResFile file, string modelName) {
+ new ColladaWriter(file).SaveModel(outputStream, modelName);
+ }
+
+
+
+ ResFile CurrentFile;
+ Models.Model CurrentModel;
+ COLLADA Collada;
+
+ library_geometries LibGeometries;
+
+ private ColladaWriter(ResFile file) {
+ CurrentFile = file;
+ }
+
+ public void SaveModel(Stream outputStream, string modelName) {
+ CurrentModel = CurrentFile.GetGroup<Model>("3DModels(NW4R)")[modelName];
+ Collada = new COLLADA();
+
+ Collada.asset = new asset();
+
+ Collada.asset.contributor = new assetContributor[1];
+ Collada.asset.contributor[0] = new assetContributor();
+ Collada.asset.contributor[0].authoring_tool = "NW4RTools Collada exporter by Treeki";
+ Collada.asset.contributor[0].source_data = "NW4R model: " + modelName;
+ Collada.asset.created = DateTime.Now;
+ Collada.asset.modified = DateTime.Now;
+ Collada.asset.unit = new assetUnit();
+ Collada.asset.unit.meter = 1.0;
+ Collada.asset.up_axis = UpAxisType.Y_UP;
+
+ Collada.Items = new object[1];
+ Collada.Items[0] = LibGeometries = new library_geometries();
+
+ LibGeometries.geometry = new geometry[CurrentModel.Shapes.Count];
+
+ int geoIndex = 0;
+ foreach (var kv in CurrentModel.Shapes) {
+ var geo = LibGeometries.geometry[geoIndex] = new geometry();
+ Shape shape = kv.Value;
+
+ geo.id = kv.Key + "-lib";
+ geo.name = kv.Key + "Mesh";
+
+ var m = new mesh();
+ geo.Item = m;
+
+ // Vertex settings
+ 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);
+
+ // Figure out how many elements we need in the Source array
+ // Position data ALWAYS exists
+ int sourceCount = 1;
+ sourceCount += (shape.NrmData != null) ? 1 : 0;
+ for (int i = 0; i < 8; i++)
+ sourceCount += (shape.TexCoordData[i] != null) ? 1 : 0;
+
+ m.source = new source[sourceCount];
+ int currentSource = 0;
+
+ // TODO: Refactor this messy code!
+
+ int dest;
+
+ // Write position data
+ var posData = shape.PosData;
+ var posSource = new source();
+ posSource.id = kv.Key + "-lib-Position";
+ m.source[currentSource++] = posSource;
+
+ var posArray = new float_array();
+ posArray.id = kv.Key + "-lib-Position-array";
+ posArray.count = (ulong)(posData.GetRealCount() * posData.EntryCount);
+ posArray.Values = new double[posArray.count];
+
+ dest = 0;
+ for (int i = 0; i < posData.EntryCount; i++) {
+ float[] data = posData.GetEntry(i);
+ for (int j = 0; j < data.Length; j++) {
+ posArray.Values[dest++] = data[j];
+ }
+ }
+
+ posSource.Item = posArray;
+
+ // Write position technique
+ posSource.technique_common = new sourceTechnique_common();
+ var posAcc = posSource.technique_common.accessor = new accessor();
+ posAcc.source = String.Format("#{0}-lib-Position-array", kv.Key);
+ posAcc.count = posData.EntryCount;
+ posAcc.stride = (ulong)posData.GetRealCount();
+
+ posAcc.param = new param[posData.GetRealCount()];
+ string[] posParamNames = new string[] { "X", "Y", "Z" };
+
+ for (int i = 0; i < posAcc.param.Length; i++) {
+ posAcc.param[i] = new param();
+ posAcc.param[i].name = posParamNames[i];
+ posAcc.param[i].type = "float";
+ }
+
+
+ // Write normal data
+ if (shape.NrmData != null) {
+ var nrmData = shape.NrmData;
+ var nrmSource = new source();
+ nrmSource.id = kv.Key + "-lib-Normal";
+ m.source[currentSource++] = nrmSource;
+
+ var nrmArray = new float_array();
+ nrmArray.id = kv.Key + "-lib-Normal-array";
+ nrmArray.count = (ulong)(nrmData.GetRealCount() * nrmData.EntryCount);
+ nrmArray.Values = new double[nrmArray.count];
+
+ dest = 0;
+ for (int i = 0; i < nrmData.EntryCount; i++) {
+ float[] data = nrmData.GetEntry(i);
+ for (int j = 0; j < data.Length; j++) {
+ nrmArray.Values[dest++] = data[j];
+ }
+ }
+
+ nrmSource.Item = nrmArray;
+
+ // Write normal technique
+ nrmSource.technique_common = new sourceTechnique_common();
+ var nrmAcc = nrmSource.technique_common.accessor = new accessor();
+ nrmAcc.source = String.Format("#{0}-lib-Normal-array", kv.Key);
+ nrmAcc.count = nrmData.EntryCount;
+ nrmAcc.stride = (ulong)nrmData.GetRealCount();
+
+ nrmAcc.param = new param[nrmData.GetRealCount()];
+ string[] nrmParamNames = new string[] { "X", "Y", "Z" };
+
+ for (int i = 0; i < nrmAcc.param.Length; i++) {
+ nrmAcc.param[i] = new param();
+ nrmAcc.param[i].name = nrmParamNames[i];
+ nrmAcc.param[i].type = "float";
+ }
+ }
+
+
+ // Write TexCoord data
+ for (int tcIndex = 0; tcIndex < 8; tcIndex++) {
+ if (shape.TexCoordData[tcIndex] != null) {
+ var tcData = shape.TexCoordData[tcIndex];
+ var tcSource = new source();
+ tcSource.id = String.Format("{0}-lib-TexCoord{1}", kv.Key, tcIndex);
+ m.source[currentSource++] = tcSource;
+
+ var tcArray = new float_array();
+ tcArray.id = String.Format("{0}-lib-TexCoord{1}-array", kv.Key, tcIndex);
+ tcArray.count = (ulong)(tcData.GetRealCount() * tcData.EntryCount);
+ tcArray.Values = new double[tcArray.count];
+
+ dest = 0;
+ for (int i = 0; i < tcData.EntryCount; i++) {
+ float[] data = tcData.GetEntry(i);
+ for (int j = 0; j < data.Length; j++) {
+ tcArray.Values[dest++] = data[j];
+ }
+ }
+
+ tcSource.Item = tcArray;
+
+ // Write texcoord technique
+ tcSource.technique_common = new sourceTechnique_common();
+ var tcAcc = tcSource.technique_common.accessor = new accessor();
+ tcAcc.source = String.Format("#{0}-lib-TexCoord{1}-array", kv.Key, tcIndex);
+ tcAcc.count = tcData.EntryCount;
+ tcAcc.stride = (ulong)tcData.GetRealCount();
+
+ tcAcc.param = new param[tcData.GetRealCount()];
+ string[] tcParamNames = new string[] { "S", "T" };
+
+ for (int i = 0; i < tcAcc.param.Length; i++) {
+ tcAcc.param[i] = new param();
+ tcAcc.param[i].name = tcParamNames[i];
+ tcAcc.param[i].type = "float";
+ }
+ }
+ }
+
+
+
+ // Ok, we've written all the raw float data, now set up vertices
+ m.vertices = new vertices();
+ m.vertices.id = String.Format("{0}-lib-Vertex", kv.Key);
+ m.vertices.input = new InputLocal[1];
+ m.vertices.input[0] = new InputLocal();
+ m.vertices.input[0].semantic = "POSITION";
+ m.vertices.input[0].source = String.Format("#{0}-lib-Position", kv.Key);
+
+ // And before we finish, write the polygon data of course
+ var dl = new InputStream(shape.DisplayList2);
+
+ List<object> meshItems = new List<object>();
+
+ // create the Input array -- we can reuse sourceCount!
+ var inputArray = new InputLocalOffset[sourceCount];
+ currentSource = 0;
+
+ var posInput = inputArray[currentSource] = new InputLocalOffset();
+ posInput.semantic = "VERTEX";
+ posInput.offset = (ulong)currentSource;
+ posInput.source = String.Format("#{0}-lib-Vertex", kv.Key);
+ currentSource++;
+
+ if (shape.NrmData != null) {
+ var nrmInput = inputArray[currentSource] = new InputLocalOffset();
+ nrmInput.semantic = "NORMAL";
+ nrmInput.offset = (ulong)currentSource;
+ nrmInput.source = String.Format("#{0}-lib-Normal", kv.Key);
+ currentSource++;
+ }
+
+ for (int i = 0; i < 8; i++) {
+ if (shape.TexCoordData[i] != null) {
+ var tcInput = inputArray[currentSource] = new InputLocalOffset();
+ tcInput.semantic = "TEXCOORD";
+ tcInput.offset = (ulong)currentSource;
+ tcInput.set = (ulong)i;
+ tcInput.source = String.Format("#{0}-lib-TexCoord{1}", kv.Key, i);
+ currentSource++;
+ }
+ }
+
+
+ // Create a list for tristrips beforehand, because they're THE most common
+ List<string> triStrips = new List<string>();
+
+
+ // Now go through the display list
+ while (true) {
+ if (dl.AtEnd)
+ break;
+
+ byte cmd = dl.ReadByte();
+ if (cmd == 0)
+ break;
+
+ PrimitiveType prim = (PrimitiveType)((cmd >> 3) & 7);
+ int vtxCount = dl.ReadUInt16();
+
+ // 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);
+
+ pVtxs[i] = vtxs[i].Position.ToString();
+
+ if (vs.NormalDesc != VertexSettings.DescType.None)
+ pVtxs[i] += " " + vtxs[i].Normal.ToString();
+
+ for (int j = 0; j < 8; j++) {
+ if (vs.TexCoordDesc[j] != VertexSettings.DescType.None) {
+ pVtxs[i] += " " + vtxs[i].TexCoords[j].ToString();
+ }
+ }
+ }
+
+ switch (prim) {
+ case PrimitiveType.Triangles:
+ var pTri = new triangles();
+ pTri.count = (ulong)(vtxCount / 3); // should be 1? dunno
+ pTri.input = inputArray;
+
+ StringBuilder pTriData = new StringBuilder();
+
+ for (int i = 0; i < vtxCount; i++) {
+ pTriData.AppendFormat("{0} ", pVtxs[i]);
+ }
+
+ pTri.p = pTriData.ToString();
+
+ meshItems.Add(pTri);
+ break;
+
+ case PrimitiveType.TriangleStrip:
+ StringBuilder pTriStripData = new StringBuilder();
+
+ for (int i = 0; i < vtxCount; i++) {
+ pTriStripData.AppendFormat("{0} ", pVtxs[i]);
+ }
+
+ triStrips.Add(pTriStripData.ToString());
+ break;
+
+ default:
+
+ Console.WriteLine("UNIMPLEMENTED PRIMITIVE TYPE");
+ return;
+ }
+ }
+
+
+ // If any tristrips were found, add them!
+ if (triStrips.Count > 0) {
+ var pTriStrips = new tristrips();
+ pTriStrips.input = inputArray;
+ pTriStrips.count = (ulong)triStrips.Count;
+ pTriStrips.p = triStrips.ToArray();
+ meshItems.Add(pTriStrips);
+ }
+
+
+ m.Items = meshItems.ToArray();
+
+ // FINALLY DONE!
+
+ geoIndex += 1;
+ }
+
+ Collada.Save(outputStream);
+ }
+ }
+}
+