summaryrefslogtreecommitdiff
path: root/NW4RTools
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--NW4RTools/BrresReader.cs3
-rw-r--r--NW4RTools/ColladaExporter.cs732
-rw-r--r--NW4RTools/Models/Material.cs5
-rw-r--r--NW4RTools/Models/Model.cs11
-rw-r--r--NW4RTools/Models/Shape.cs3
-rw-r--r--NW4RTools/NW4RTools.csproj2
-rw-r--r--NW4RTools/Texture.cs2
-rw-r--r--NW4RTools/Util/NVDXT.cs2
-rwxr-xr-xNW4RTools/bin/Debug/NW4RTools.dllbin221696 -> 222208 bytes
-rw-r--r--NW4RTools/bin/Debug/NW4RTools.dll.mdbbin109673 -> 109819 bytes
10 files changed, 754 insertions, 6 deletions
diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs
index e67581b..d5db720 100644
--- a/NW4RTools/BrresReader.cs
+++ b/NW4RTools/BrresReader.cs
@@ -275,7 +275,6 @@ namespace NW4RTools {
using (var c2 = Debug.Push("Vertex Normal Data")) {
if (vtxNrmOffset == 0) {
Debug.Send("None");
- mdl.VtxNrmData = new ResDict<VertexNrmData>();
} else {
mdl.VtxNrmData = ReadAndConvertDict<VertexNrmData>(ins.At(startPos + vtxNrmOffset), ConvertVtxNrmData);
}
@@ -284,7 +283,6 @@ namespace NW4RTools {
using (var c2 = Debug.Push("Vertex Colour Data")) {
if (vtxClrOffset == 0) {
Debug.Send("None");
- mdl.VtxClrData = new ResDict<VertexClrData>();
} else {
mdl.VtxClrData = ReadAndConvertDict<VertexClrData>(ins.At(startPos + vtxClrOffset), ConvertVtxClrData);
}
@@ -293,7 +291,6 @@ namespace NW4RTools {
using (var c2 = Debug.Push("Vertex TexCoord Data")) {
if (texCoordOffset == 0) {
Debug.Send("None");
- mdl.VtxTexCoordData = new ResDict<VertexTexCoordData>();
} else {
mdl.VtxTexCoordData = ReadAndConvertDict<VertexTexCoordData>(ins.At(startPos + texCoordOffset), ConvertVtxTexCoordData);
}
diff --git a/NW4RTools/ColladaExporter.cs b/NW4RTools/ColladaExporter.cs
new file mode 100644
index 0000000..f18bfed
--- /dev/null
+++ b/NW4RTools/ColladaExporter.cs
@@ -0,0 +1,732 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Text;
+using NW4RTools.Models;
+using Collada141;
+
+namespace NW4RTools {
+ public class ColladaExporter {
+ public static void WriteModel(Stream outputStream, ResFile file, string modelName) {
+ new ColladaExporter(file).SaveModel(outputStream, modelName);
+ }
+
+
+
+ ResFile CurrentFile;
+ Models.Model CurrentModel;
+ COLLADA Collada;
+
+ library_geometries LibGeometries;
+ library_visual_scenes LibVisualScenes;
+ library_images LibImages;
+ library_materials LibMaterials;
+ library_effects LibEffects;
+
+ private ColladaExporter(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;
+
+ var ColladaItems = new List<object>();
+
+ // First up, before anything else: images and materials
+ // Compile a list of every texture we use in the model
+ var usedTextures = new List<string>();
+
+ foreach (var kv in CurrentModel.Materials) {
+ foreach (var texInfo in kv.Value.TextureInfos) {
+ if (!usedTextures.Contains(texInfo.TextureName))
+ usedTextures.Add(texInfo.TextureName);
+ }
+ }
+
+ // Create a Collada image for these
+ LibImages = new library_images();
+ ColladaItems.Add(LibImages);
+
+ var ImageList = new List<image>();
+ var TexGroup = CurrentFile.GetGroup<Texture>("Textures(NW4R)");
+
+ foreach (var texName in usedTextures) {
+ var img = new image();
+ var tex = TexGroup[texName];
+
+ img.name = "Texture-" + texName;
+ img.id = img.name;
+ img.Item = "./images/" + texName + ".png";
+
+ ImageList.Add(img);
+ }
+
+ LibImages.image = ImageList.ToArray();
+
+ // Make a quick reference material for each one
+ LibMaterials = new library_materials();
+ ColladaItems.Add(LibMaterials);
+
+ var MaterialList = new List<material>();
+
+ foreach (var kv in CurrentModel.Materials) {
+ var mat = new material();
+
+ mat.name = "Material-" + kv.Key;
+ mat.id = mat.name;
+ mat.instance_effect = new instance_effect();
+ mat.instance_effect.url = "#Material-Effect-" + kv.Key;
+
+ MaterialList.Add(mat);
+ }
+
+ LibMaterials.material = MaterialList.ToArray();
+
+ // Now create an effect for each material (this is where all the real work is done!)
+ LibEffects = new library_effects();
+ ColladaItems.Add(LibEffects);
+
+ var EffectList = new List<effect>();
+ foreach (var kv in CurrentModel.Materials) {
+ EffectList.Add(CreateEffectFromMaterial(kv.Key, kv.Value));
+ }
+
+ LibEffects.effect = EffectList.ToArray();
+
+ // Now shapes
+ LibGeometries = new library_geometries();
+ ColladaItems.Add(LibGeometries);
+
+ var GeometryList = new List<geometry>();
+ foreach (var kv in CurrentModel.Shapes) {
+ GeometryList.Add(CreateGeometryFromShape(kv.Key, kv.Value));
+ }
+
+ LibGeometries.geometry = GeometryList.ToArray();
+
+ // SHAPES ARE DONE.
+ // Next up: Visual Scenes (I will just create one atm)
+
+ LibVisualScenes = new library_visual_scenes();
+ ColladaItems.Add(LibVisualScenes);
+
+ LibVisualScenes.visual_scene = new visual_scene[1];
+ var mainScene = LibVisualScenes.visual_scene[0] = new visual_scene();
+
+ // TODO: Change this so it doesn't have the possibility of name collisions with shapes
+ mainScene.id = "RootNode";
+ mainScene.name = "RootNode";
+ var mainSceneNodeList = new List<node>();
+
+ // OK, so here's what's up: first off, we must create a definition for every node
+ var NodeDefs = new Dictionary<Node, node>();
+
+ foreach (var kv in CurrentModel.Nodes) {
+ string nodeName = kv.Key;
+ Node origNode = kv.Value;
+ var cNode = new node();
+
+ cNode.id = nodeName;
+ cNode.name = nodeName;
+ //cNode.type = NodeType.JOINT;
+ cNode.node1 = new node[0];
+
+ double cosX = Math.Cos(origNode.Rotation.x / 180 * Math.PI);
+ double cosY = Math.Cos(origNode.Rotation.y / 180 * Math.PI);
+ double cosZ = Math.Cos(origNode.Rotation.z / 180 * Math.PI);
+ double sinX = Math.Sin(origNode.Rotation.x / 180 * Math.PI);
+ double sinY = Math.Sin(origNode.Rotation.y / 180 * Math.PI);
+ double sinZ = Math.Sin(origNode.Rotation.z / 180 * Math.PI);
+
+ var nodeMatrix = new matrix();
+ nodeMatrix.Values = new double[] {
+ origNode.Scale.x * cosY * cosZ,
+ origNode.Scale.y * (sinX * cosZ * sinY - cosX * sinZ),
+ origNode.Scale.z * (sinX * sinZ + cosX * cosZ * sinY),
+ origNode.Translation.x,
+ origNode.Scale.x * sinZ * cosY,
+ origNode.Scale.y * (sinX * sinZ * sinY + cosZ * cosX),
+ origNode.Scale.z * (cosX * sinZ * sinY - sinX * cosZ),
+ origNode.Translation.y,
+ -origNode.Scale.x * sinY,
+ origNode.Scale.y * sinX * cosY,
+ origNode.Scale.z * cosX * cosY,
+ origNode.Translation.z,
+ 0, 0, 0, 1
+ };
+
+ cNode.Items = new object[] { nodeMatrix };
+ cNode.ItemsElementName = new ItemsChoiceType2[] { ItemsChoiceType2.matrix };
+
+ NodeDefs[origNode] = cNode;
+ }
+
+ // Now add them to the hierarchy
+ foreach (var kv in NodeDefs) {
+ Node origNode = kv.Key;
+ node cNode = kv.Value;
+
+ if (origNode.Parent == null) {
+ mainSceneNodeList.Add(cNode);
+ } else {
+ var parentNode = NodeDefs[origNode.Parent];
+
+ // this is stupid, thanks C#
+ node[] nodeArrayCopy = parentNode.node1;
+ Array.Resize<node>(ref nodeArrayCopy, nodeArrayCopy.Length + 1);
+ nodeArrayCopy[nodeArrayCopy.Length - 1] = cNode;
+ parentNode.node1 = nodeArrayCopy;
+ }
+ }
+
+ // Apply shapes to nodes
+ /*foreach (var kv in CurrentModel.Shapes) {
+ Shape shape = kv.Value;
+ Node origNode = CurrentModel.Nodes[CurrentModel.MatrixIDtoNodeID[shape.MatrixID]];
+ node cNode = NodeDefs[origNode];
+
+ var newGeoEntry = new instance_geometry();
+ newGeoEntry.name = kv.Key;
+ newGeoEntry.url = String.Format("#{0}-lib", kv.Key);
+
+ instance_geometry[] geoArrayCopy = cNode.instance_geometry;
+ if (geoArrayCopy == null)
+ geoArrayCopy = new instance_geometry[1];
+ else
+ Array.Resize<instance_geometry>(ref geoArrayCopy, geoArrayCopy.Length + 1);
+ geoArrayCopy[geoArrayCopy.Length - 1] = newGeoEntry;
+ cNode.instance_geometry = geoArrayCopy;
+
+ // TODO: Add material handling, I'll probably have to parse DrawOpa/DrawXlu for this...
+ }*/
+
+ // WARNING: THIS NEEDS REFACTORING
+
+ int drawID = 0;
+
+ foreach (var insn in CurrentModel.Bytecode["DrawOpa"].Instructions) {
+ if (insn is ByteCode.DrawShapeInstruction) {
+ var dsInsn = insn as ByteCode.DrawShapeInstruction;
+
+ Shape shape = CurrentModel.Shapes[dsInsn.ShapeID];
+ string shapeName = CurrentModel.Shapes.GetKeyForIndex(dsInsn.ShapeID);
+
+ Node origNode = CurrentModel.Nodes[dsInsn.NodeID];
+ node cNode = NodeDefs[origNode];
+
+ Material mat = CurrentModel.Materials[dsInsn.MaterialID];
+ string matName = CurrentModel.Materials.GetKeyForIndex(dsInsn.MaterialID);
+
+ var newGeoEntry = new instance_geometry();
+ newGeoEntry.name = String.Format("DrawOpa{0}-{1}", drawID, shapeName);
+ newGeoEntry.url = String.Format("#{0}-lib", shapeName);
+
+ // now add the material
+ var bindMaterial = newGeoEntry.bind_material = new bind_material();
+ bindMaterial.technique_common = new instance_material[1];
+ var matTechnique = bindMaterial.technique_common[0] = new instance_material();
+
+ // constant marker so that I don't have to set a unique material name in each primitive
+ // it doesn't matter, since each geometry instance only uses one material anyway
+ matTechnique.symbol = "NW4R_MATERIAL";
+ matTechnique.target = "#Material-" + matName;
+
+ matTechnique.bind_vertex_input = new instance_materialBind_vertex_input[1];
+ matTechnique.bind_vertex_input[0] = new instance_materialBind_vertex_input();
+ matTechnique.bind_vertex_input[0].semantic = "CHANNEL1";
+ matTechnique.bind_vertex_input[0].input_semantic = "TEXCOORD";
+ matTechnique.bind_vertex_input[0].input_set = 0;
+
+ // ok, now add the instance_geometry into the node
+ instance_geometry[] geoArrayCopy = cNode.instance_geometry;
+ if (geoArrayCopy == null)
+ geoArrayCopy = new instance_geometry[1];
+ else
+ Array.Resize<instance_geometry>(ref geoArrayCopy, geoArrayCopy.Length + 1);
+ geoArrayCopy[geoArrayCopy.Length - 1] = newGeoEntry;
+ cNode.instance_geometry = geoArrayCopy;
+
+ drawID++;
+ }
+ }
+
+ drawID = 0;
+
+ foreach (var insn in CurrentModel.Bytecode["DrawXlu"].Instructions) {
+ if (insn is ByteCode.DrawShapeInstruction) {
+ var dsInsn = insn as ByteCode.DrawShapeInstruction;
+
+ Shape shape = CurrentModel.Shapes[dsInsn.ShapeID];
+ string shapeName = CurrentModel.Shapes.GetKeyForIndex(dsInsn.ShapeID);
+
+ Node origNode = CurrentModel.Nodes[dsInsn.NodeID];
+ node cNode = NodeDefs[origNode];
+
+ Material mat = CurrentModel.Materials[dsInsn.MaterialID];
+ string matName = CurrentModel.Materials.GetKeyForIndex(dsInsn.MaterialID);
+
+ var newGeoEntry = new instance_geometry();
+ newGeoEntry.name = String.Format("DrawXlu{0}-{1}", drawID, shapeName);
+ newGeoEntry.url = String.Format("#{0}-lib", shapeName);
+
+ // now add the material
+ var bindMaterial = newGeoEntry.bind_material = new bind_material();
+ bindMaterial.technique_common = new instance_material[1];
+ var matTechnique = bindMaterial.technique_common[0] = new instance_material();
+
+ // constant marker so that I don't have to set a unique material name in each primitive
+ // it doesn't matter, since each geometry instance only uses one material anyway
+ matTechnique.symbol = "NW4R_MATERIAL";
+ matTechnique.target = "#Material-" + matName;
+
+ matTechnique.bind_vertex_input = new instance_materialBind_vertex_input[1];
+ matTechnique.bind_vertex_input[0] = new instance_materialBind_vertex_input();
+ matTechnique.bind_vertex_input[0].semantic = "CHANNEL1";
+ matTechnique.bind_vertex_input[0].input_semantic = "TEXCOORD";
+ matTechnique.bind_vertex_input[0].input_set = 0;
+
+ // ok, now add the instance_geometry into the node
+ instance_geometry[] geoArrayCopy = cNode.instance_geometry;
+ if (geoArrayCopy == null)
+ geoArrayCopy = new instance_geometry[1];
+ else
+ Array.Resize<instance_geometry>(ref geoArrayCopy, geoArrayCopy.Length + 1);
+ geoArrayCopy[geoArrayCopy.Length - 1] = newGeoEntry;
+ cNode.instance_geometry = geoArrayCopy;
+
+ drawID++;
+ }
+ }
+
+
+ /*foreach (var kv in CurrentModel.Shapes) {
+ var thisNode = new node();
+
+ thisNode.id = kv.Key;
+ thisNode.name = kv.Key;
+ thisNode.instance_geometry = new instance_geometry[1];
+ thisNode.instance_geometry[0] = new instance_geometry();
+ thisNode.instance_geometry[0].url = String.Format("#{0}-lib", kv.Key);
+
+ mainSceneNodeList.Add(thisNode);
+ }*/
+
+ mainScene.node = mainSceneNodeList.ToArray();
+
+
+ // Finally, create a scene
+ Collada.scene = new COLLADAScene();
+ Collada.scene.instance_visual_scene = new InstanceWithExtra();
+ Collada.scene.instance_visual_scene.url = "#RootNode";
+
+ Collada.Items = ColladaItems.ToArray();
+ Collada.Save(outputStream);
+ }
+
+
+
+ private effect CreateEffectFromMaterial(string name, Material mat) {
+ var eff = new effect();
+
+ eff.id = "Material-Effect-" + name;
+ eff.name = eff.id;
+
+
+ // HACK!!
+ if (mat.TextureInfos.Count == 0)
+ return eff;
+
+ // this is based off what colladamax outputs
+ eff.Items = new effectFx_profile_abstractProfile_COMMON[1];
+ var profile = eff.Items[0] = new effectFx_profile_abstractProfile_COMMON();
+
+ // TODO: handle this correctly for multiple textures ETC and check how bb does it
+
+ profile.Items = new object[2];
+
+ // create a surface newparam
+ var surfaceParam = new common_newparam_type();
+ var surface = new fx_surface_common();
+
+ surface.type = fx_surface_type_enum.Item2D;
+ surface.init_from = new fx_surface_init_from_common[1];
+ surface.init_from[0] = new fx_surface_init_from_common();
+ surface.init_from[0].Value = "Texture-" + mat.TextureInfos[0].TextureName;
+
+ surfaceParam.sid = "Surface-" + mat.TextureInfos[0].TextureName;
+ surfaceParam.ItemElementName = ItemChoiceType.surface;
+ surfaceParam.Item = surface;
+ profile.Items[0] = surfaceParam;
+
+ // now create a sampler newparam
+ var samplerParam = new common_newparam_type();
+ var sampler2d = new fx_sampler2D_common();
+
+ // add wrapping
+ sampler2d.source = "Surface-" + mat.TextureInfos[0].TextureName;
+ sampler2d.wrap_s = mat.TextureInfos[0].WrapS.ToColladaSamplerWrap();
+ sampler2d.wrap_t = mat.TextureInfos[0].WrapT.ToColladaSamplerWrap();
+
+ samplerParam.sid = "Sampler-" + mat.TextureInfos[0].TextureName;
+ samplerParam.ItemElementName = ItemChoiceType.sampler2D;
+ samplerParam.Item = sampler2d;
+ profile.Items[1] = samplerParam;
+
+ // now make a technique
+ // should I really use blinn...?
+ profile.technique = new effectFx_profile_abstractProfile_COMMONTechnique();
+ profile.technique.sid = "common";
+
+ var pShader = new effectFx_profile_abstractProfile_COMMONTechniquePhong();
+ pShader.diffuse = new common_color_or_texture_type();
+
+ var diffuseTex = new common_color_or_texture_typeTexture();
+ diffuseTex.texture = "Sampler-" + mat.TextureInfos[0].TextureName;
+ diffuseTex.texcoord = "CHANNEL1";
+
+ pShader.diffuse.Item = diffuseTex;
+
+ pShader.emission = new common_color_or_texture_type();
+ var emissionClr = new common_color_or_texture_typeColor();
+ emissionClr.Values = new double[] { 0, 0, 0, 1 };
+ pShader.emission.Item = emissionClr;
+
+ pShader.ambient = new common_color_or_texture_type();
+ var ambientClr = new common_color_or_texture_typeColor();
+ ambientClr.Values = new double[] { 0, 0, 0, 1 };
+ pShader.ambient.Item = ambientClr;
+
+ pShader.transparent = new common_transparent_type();
+ pShader.transparent.opaque = fx_opaque_enum.A_ONE;
+ var transparentClr = new common_color_or_texture_typeColor();
+ transparentClr.Values = new double[] { 1, 1, 1, 1 };
+ pShader.transparent.Item = transparentClr;
+ pShader.transparency = new common_float_or_param_type();
+ var bs = new common_float_or_param_typeFloat();
+ bs.Value = 1;
+ pShader.transparency.Item = bs;
+ // we can reuse DiffuseTex!
+ pShader.transparent.Item = diffuseTex;
+
+ profile.technique.Item = pShader;
+
+ return eff;
+ }
+
+
+
+ private geometry CreateGeometryFromShape(string name, Shape shape) {
+ var geo = new geometry();
+
+ geo.id = name + "-lib";
+ geo.name = name + "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 = name + "-lib-Position";
+ m.source[currentSource++] = posSource;
+
+ var posArray = new float_array();
+ posArray.id = name + "-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", name);
+ 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 = name + "-lib-Normal";
+ m.source[currentSource++] = nrmSource;
+
+ var nrmArray = new float_array();
+ nrmArray.id = name + "-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", name);
+ 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}", name, tcIndex);
+ m.source[currentSource++] = tcSource;
+
+ var tcArray = new float_array();
+ tcArray.id = String.Format("{0}-lib-TexCoord{1}-array", name, 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++) {
+ // flip T (Y axis) coordinate
+ if (j == 1)
+ tcArray.Values[dest++] = 1.0 - data[j];
+ else
+ 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", name, 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
+ // TODO: Vertex colours
+ m.vertices = new vertices();
+ m.vertices.id = String.Format("{0}-lib-Vertex", name);
+ 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", name);
+
+ // 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", name);
+ 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", name);
+ 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}", name, 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.material = "NW4R_MATERIAL";
+ 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 geo;
+ }
+ }
+
+
+ // If any tristrips were found, add them!
+ if (triStrips.Count > 0) {
+ var pTriStrips = new tristrips();
+ pTriStrips.material = "NW4R_MATERIAL";
+ pTriStrips.input = inputArray;
+ pTriStrips.count = (ulong)triStrips.Count;
+ pTriStrips.p = triStrips.ToArray();
+ meshItems.Add(pTriStrips);
+ }
+
+
+ m.Items = meshItems.ToArray();
+
+ // FINALLY DONE!
+ return geo;
+ }
+ }
+}
+
diff --git a/NW4RTools/Models/Material.cs b/NW4RTools/Models/Material.cs
index 9e90348..0bf0acc 100644
--- a/NW4RTools/Models/Material.cs
+++ b/NW4RTools/Models/Material.cs
@@ -58,6 +58,11 @@ namespace NW4RTools.Models {
public byte[] TexCoordGenDL;
public Material() {
+ IndirectTexMtxCalcMethod1 = new byte[4];
+ IndirectTexMtxCalcMethod2 = new byte[4];
+ TexObj = new byte[8][];
+ // todo
+ TextureInfos = new List<TextureInfo>();
}
}
}
diff --git a/NW4RTools/Models/Model.cs b/NW4RTools/Models/Model.cs
index 71edaf3..7c7e912 100644
--- a/NW4RTools/Models/Model.cs
+++ b/NW4RTools/Models/Model.cs
@@ -40,6 +40,17 @@ namespace NW4RTools.Models {
public Model() {
+ Bytecode = new ResDict<ByteCode>();
+ Nodes = new ResDict<Node>();
+ VtxPosData = new ResDict<VertexPosData>();
+ VtxNrmData = new ResDict<VertexNrmData>();
+ VtxClrData = new ResDict<VertexClrData>();
+ VtxTexCoordData = new ResDict<VertexTexCoordData>();
+ Materials = new ResDict<Material>();
+ Shaders = new ResDict<Shader>();
+ Shapes = new ResDict<Shape>();
+ PairingLookupByTexture = new ResDict<List<TexMatPairing>>();
+ PairingLookupByPalette = new ResDict<List<TexMatPairing>>();
}
}
}
diff --git a/NW4RTools/Models/Shape.cs b/NW4RTools/Models/Shape.cs
index 87bf370..93ff70b 100644
--- a/NW4RTools/Models/Shape.cs
+++ b/NW4RTools/Models/Shape.cs
@@ -24,6 +24,9 @@ namespace NW4RTools.Models {
public UInt16[] ExtraData;
public Shape() {
+ Unk = new byte[12];
+ ClrData = new VertexClrData[2];
+ TexCoordData = new VertexTexCoordData[8];
}
}
}
diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj
index 89af12e..80115cd 100644
--- a/NW4RTools/NW4RTools.csproj
+++ b/NW4RTools/NW4RTools.csproj
@@ -64,7 +64,6 @@
<Compile Include="DisplayList.cs" />
<Compile Include="VertexSettings.cs" />
<Compile Include="Util\collada_schema_1_4.cs" />
- <Compile Include="ColladaWriter.cs" />
<Compile Include="Texture.cs" />
<Compile Include="Misc.cs" />
<Compile Include="Models\OpenGL\GLModel.cs" />
@@ -76,6 +75,7 @@
<Compile Include="Models\Animation\CharacterAnim.cs" />
<Compile Include="Models\Animation\TextureSRTAnim.cs" />
<Compile Include="Util\NVDXT.cs" />
+ <Compile Include="ColladaExporter.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
diff --git a/NW4RTools/Texture.cs b/NW4RTools/Texture.cs
index 3dadce8..e45555d 100644
--- a/NW4RTools/Texture.cs
+++ b/NW4RTools/Texture.cs
@@ -236,7 +236,7 @@ namespace NW4RTools {
for (int x = 0; x < width; x += 8) {
for (int iBlockY = 0; iBlockY < 2; iBlockY++) {
for (int iBlockX = 0; iBlockX < 2; iBlockX++) {
- var block = Util.NVDXT.compressDXT1a(bits.Scan0,
+ var block = Util.NVDXT.compressDXT1a((Util.NVDXT.ARGBPixel*)bits.Scan0,
x + (iBlockX * 4), y + (iBlockY * 4),
bits.Stride, bits.Height);
diff --git a/NW4RTools/Util/NVDXT.cs b/NW4RTools/Util/NVDXT.cs
index fd7d50a..a972344 100644
--- a/NW4RTools/Util/NVDXT.cs
+++ b/NW4RTools/Util/NVDXT.cs
@@ -729,7 +729,7 @@ namespace NW4RTools.Util {
CMPRBlock block = new CMPRBlock();
uint indices = 0xAAAAAAAA;
-
+
block.Color0 = (ushort)((OMatch5[c.R, 0] << 11) | (OMatch6[c.G, 0] << 5) | OMatch5[c.B, 0]);
block.Color1 = (ushort)((OMatch5[c.R, 1] << 11) | (OMatch6[c.G, 1] << 5) | OMatch5[c.B, 1]);
diff --git a/NW4RTools/bin/Debug/NW4RTools.dll b/NW4RTools/bin/Debug/NW4RTools.dll
index aa6b86f..1204c1d 100755
--- a/NW4RTools/bin/Debug/NW4RTools.dll
+++ b/NW4RTools/bin/Debug/NW4RTools.dll
Binary files differ
diff --git a/NW4RTools/bin/Debug/NW4RTools.dll.mdb b/NW4RTools/bin/Debug/NW4RTools.dll.mdb
index ea4c02e..badfeb7 100644
--- a/NW4RTools/bin/Debug/NW4RTools.dll.mdb
+++ b/NW4RTools/bin/Debug/NW4RTools.dll.mdb
Binary files differ