From a5b6dc1789f2e06a26fe6a0510aee04aeccdc70b Mon Sep 17 00:00:00 2001 From: Treeki Date: Thu, 3 Mar 2011 22:01:38 +0100 Subject: tidying up a bit, also it compiles now --- NW4RTools/BrresReader.cs | 3 - NW4RTools/ColladaExporter.cs | 732 ++++++++++++++++++++++++++++++++++ NW4RTools/Models/Material.cs | 5 + NW4RTools/Models/Model.cs | 11 + NW4RTools/Models/Shape.cs | 3 + NW4RTools/NW4RTools.csproj | 2 +- NW4RTools/Texture.cs | 2 +- NW4RTools/Util/NVDXT.cs | 2 +- NW4RTools/bin/Debug/NW4RTools.dll | Bin 221696 -> 222208 bytes NW4RTools/bin/Debug/NW4RTools.dll.mdb | Bin 109673 -> 109819 bytes 10 files changed, 754 insertions(+), 6 deletions(-) create mode 100644 NW4RTools/ColladaExporter.cs 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(); } else { mdl.VtxNrmData = ReadAndConvertDict(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(); } else { mdl.VtxClrData = ReadAndConvertDict(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(); } else { mdl.VtxTexCoordData = ReadAndConvertDict(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("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(); + + // First up, before anything else: images and materials + // Compile a list of every texture we use in the model + var usedTextures = new List(); + + 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(); + var TexGroup = CurrentFile.GetGroup("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(); + + 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(); + 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(); + 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(); + + // OK, so here's what's up: first off, we must create a definition for every node + var NodeDefs = new Dictionary(); + + 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(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(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(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(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 meshItems = new List(); + + // 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 triStrips = new List(); + + + // 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(); } } } 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(); + Nodes = new ResDict(); + VtxPosData = new ResDict(); + VtxNrmData = new ResDict(); + VtxClrData = new ResDict(); + VtxTexCoordData = new ResDict(); + Materials = new ResDict(); + Shaders = new ResDict(); + Shapes = new ResDict(); + PairingLookupByTexture = new ResDict>(); + PairingLookupByPalette = new ResDict>(); } } } 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 @@ - @@ -76,6 +75,7 @@ + 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 Binary files a/NW4RTools/bin/Debug/NW4RTools.dll and b/NW4RTools/bin/Debug/NW4RTools.dll differ diff --git a/NW4RTools/bin/Debug/NW4RTools.dll.mdb b/NW4RTools/bin/Debug/NW4RTools.dll.mdb index ea4c02e..badfeb7 100644 Binary files a/NW4RTools/bin/Debug/NW4RTools.dll.mdb and b/NW4RTools/bin/Debug/NW4RTools.dll.mdb differ -- cgit v1.2.3