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 | |
parent | a5b6dc1789f2e06a26fe6a0510aee04aeccdc70b (diff) | |
download | nw4rtools-e962fd89af865d6e01522e9752f5fbd855ce128a.tar.gz nw4rtools-e962fd89af865d6e01522e9752f5fbd855ce128a.zip |
partially working obj importer. still untested in-game
24 files changed, 1270 insertions, 62 deletions
diff --git a/NW4RTools.sln b/NW4RTools.sln index eb42975..224f5e3 100644 --- a/NW4RTools.sln +++ b/NW4RTools.sln @@ -38,11 +38,12 @@ Global $3.MethodBraceStyle = EndOfLine
$3.ConstructorBraceStyle = EndOfLine
$3.DestructorBraceStyle = EndOfLine
- $3.BeforeMethodCallParentheses = False
$3.BeforeMethodDeclarationParentheses = False
+ $3.BeforeMethodCallParentheses = False
$3.BeforeConstructorDeclarationParentheses = False
$3.BeforeDelegateDeclarationParentheses = False
$3.NewParentheses = False
+ $3.SpacesBeforeBrackets = False
$3.inheritsSet = Mono
$3.inheritsScope = text/x-csharp
$3.scope = text/x-csharp
diff --git a/NW4RTools.suo b/NW4RTools.suo Binary files differnew file mode 100755 index 0000000..372e67d --- /dev/null +++ b/NW4RTools.suo diff --git a/NW4RTools.userprefs b/NW4RTools.userprefs index 59f34b8..d7da209 100644 --- a/NW4RTools.userprefs +++ b/NW4RTools.userprefs @@ -1,19 +1,22 @@ <Properties> <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug" /> - <MonoDevelop.Ide.Workbench ActiveDocument="NW4RTools/BrresWriter.cs"> + <MonoDevelop.Ide.Workbench ActiveDocument="NW4RTools/ObjImporter.cs"> <Files> - <File FileName="NW4RTools/BrresReader.cs" Line="115" Column="34" /> - <File FileName="NW4RTools/BrresWriter.cs" Line="582" Column="69" /> - <File FileName="NW4RTools/Texture.cs" Line="193" Column="6" /> - <File FileName="TestApp/Main.cs" Line="18" Column="24" /> - <File FileName="NW4RTools/InputStream.cs" Line="136" Column="1" /> - <File FileName="NW4RTools/Models/Shape.cs" Line="24" Column="29" /> - <File FileName="NW4RTools/Models/OpenGL/GLTexture.cs" Line="37" Column="18" /> - <File FileName="NW4RTools/OutputStream.cs" Line="105" Column="36" /> - <File FileName="NW4RTools/Models/Model.cs" Line="33" Column="44" /> - <File FileName="NW4RTools/Models/Material.cs" Line="39" Column="3" /> - <File FileName="NW4RTools/Models/TextureInfo.cs" Line="1" Column="1" /> - <File FileName="TestApp/AssemblyInfo.cs" Line="1" Column="1" /> + <File FileName="NW4RTools/ObjImporter.cs" Line="635" Column="18" /> + <File FileName="NW4RTools/ObjExporter.cs" Line="105" Column="25" /> + <File FileName="NW4RTools/BrresReader.cs" Line="784" Column="11" /> + <File FileName="NW4RTools/BrresWriter.cs" Line="648" Column="21" /> + <File FileName="NW4RTools/Models/Model.cs" Line="1" Column="1" /> + <File FileName="NW4RTools/Models/ByteCode.cs" Line="1" Column="1" /> + <File FileName="TestApp/Main.cs" Line="19" Column="1" /> + <File FileName="NW4RTools/Types.cs" Line="1" Column="1" /> + <File FileName="NW4RTools/Models/Material.cs" Line="1" Column="1" /> + <File FileName="NW4RTools/VertexSettings.cs" Line="184" Column="46" /> + <File FileName="NW4RTools/Models/VertexData.cs" Line="238" Column="31" /> + <File FileName="NW4RTools/Enums.cs" Line="23" Column="27" /> + <File FileName="NW4RTools/Models/OpenGL/GLModel.cs" Line="302" Column="1" /> + <File FileName="NW4RTools/InputStream.cs" Line="66" Column="1" /> + <File FileName="TestApp/RenderWindow.cs" Line="19" Column="1" /> </Files> </MonoDevelop.Ide.Workbench> <MonoDevelop.Ide.DebuggingService.Breakpoints> diff --git a/NW4RTools/BrresWriter.cs b/NW4RTools/BrresWriter.cs index b93a71d..cf48cdd 100644 --- a/NW4RTools/BrresWriter.cs +++ b/NW4RTools/BrresWriter.cs @@ -42,6 +42,12 @@ namespace NW4RTools { return Output.GetBuffer(); } + + private void LogPosition(string text) { + Debug.Send("0x{0:X} : {1}", CurrentPos, text); + OffsetMap.Add(CurrentPos, text); + } + // OK, here's how I'm going to code this: kinda like how BrawlLib works, first I'll calculate the size of // each written element, and use that to build up a list of offsets. Then, I'll actually write out the data. // This code will also handle building the string table. @@ -106,7 +112,7 @@ namespace NW4RTools { // Now do each ResDict in the File foreach (var kv in File) { - OffsetMap.Add(CurrentPos, "ResDict: " + kv.Key); + LogPosition("ResDict: " + kv.Key); RootDictOffsets[kv.Value] = CurrentPos; CurrentPos += GetSizeForResDict((kv.Value as ICollection).Count); @@ -132,7 +138,7 @@ namespace NW4RTools { // ... and done with that. Build the string table and go! AlignCalcPos(4); - OffsetMap.Add(CurrentPos, "String Table"); + LogPosition("String Table"); CalculateStringTable(); } @@ -169,61 +175,61 @@ namespace NW4RTools { // This is used later to calculate the MDL0 block size easily int startPos = CurrentPos; - OffsetMap.Add(CurrentPos, "Model: " + kv.Key); + LogPosition("Model: " + kv.Key); ModelOffsets.Add(model, CurrentPos); CurrentPos += 0x4C; - OffsetMap.Add(CurrentPos, "Model Info Struct for: " + kv.Key); + LogPosition("Model Info Struct for: " + kv.Key); CurrentPos += 0x40; - OffsetMap.Add(CurrentPos, "Matrix ID to Node ID Data for: " + kv.Key); + LogPosition("Matrix ID to Node ID Data for: " + kv.Key); calcInfo.MatrixIDtoNodeID = CurrentPos; CurrentPos += 4 + (model.MatrixIDtoNodeID.Length * 4); - OffsetMap.Add(CurrentPos, "ResDict: ByteCode"); + LogPosition("ResDict: ByteCode"); calcInfo.Bytecode = CurrentPos; CurrentPos += GetSizeForResDict(model.Bytecode.Count); - OffsetMap.Add(CurrentPos, "ResDict: Nodes"); + LogPosition("ResDict: Nodes"); calcInfo.Nodes = CurrentPos; CurrentPos += GetSizeForResDict(model.Nodes.Count); - OffsetMap.Add(CurrentPos, "ResDict: VertexPosData"); + LogPosition("ResDict: VertexPosData"); calcInfo.VtxPosData = CurrentPos; CurrentPos += GetSizeForResDict(model.VtxPosData.Count); if (model.VtxNrmData.Count > 0) { - OffsetMap.Add(CurrentPos, "ResDict: VertexNrmData"); + LogPosition("ResDict: VertexNrmData"); calcInfo.VtxNrmData = CurrentPos; CurrentPos += GetSizeForResDict(model.VtxNrmData.Count); } if (model.VtxClrData.Count > 0) { - OffsetMap.Add(CurrentPos, "ResDict: VertexClrData"); + LogPosition("ResDict: VertexClrData"); calcInfo.VtxClrData = CurrentPos; CurrentPos += GetSizeForResDict(model.VtxClrData.Count); } if (model.VtxTexCoordData.Count > 0) { - OffsetMap.Add(CurrentPos, "ResDict: VertexTexCoordData"); + LogPosition("ResDict: VertexTexCoordData"); calcInfo.VtxTexCoordData = CurrentPos; CurrentPos += GetSizeForResDict(model.VtxTexCoordData.Count); } - OffsetMap.Add(CurrentPos, "ResDict: Materials"); + LogPosition("ResDict: Materials"); calcInfo.Materials = CurrentPos; CurrentPos += GetSizeForResDict(model.Materials.Count); - OffsetMap.Add(CurrentPos, "ResDict: Shaders"); + LogPosition("ResDict: Shaders"); calcInfo.Shaders = CurrentPos; CurrentPos += GetSizeForResDict(model.Shaders.Count); - OffsetMap.Add(CurrentPos, "ResDict: Shapes"); + LogPosition("ResDict: Shapes"); calcInfo.Shapes = CurrentPos; CurrentPos += GetSizeForResDict(model.Shapes.Count); if (model.PairingLookupByTexture.Count > 0) { - OffsetMap.Add(CurrentPos, "ResDict: Texture Lookup"); + LogPosition("ResDict: Texture Lookup"); calcInfo.PairingLookupByTexture = CurrentPos; CurrentPos += GetSizeForResDict(model.PairingLookupByTexture.Count); } @@ -250,7 +256,7 @@ namespace NW4RTools { private void CalculatePairings(Model m, ResDict<List<TexMatPairing>> dict) { foreach (var kv in dict) { - OffsetMap.Add(CurrentPos, "Texture/Material Pairing List for: " + kv.Key); + LogPosition("Texture/Material Pairing List for: " + kv.Key); PairingOffsets.Add(kv.Value, CurrentPos); CurrentPos += 4 + (kv.Value.Count * 8); @@ -262,7 +268,7 @@ namespace NW4RTools { AddString(kv.Key); ByteCode bc = kv.Value; - OffsetMap.Add(CurrentPos, "ByteCode: " + kv.Key); + LogPosition("ByteCode: " + kv.Key); BytecodeOffsets.Add(kv.Value, CurrentPos); foreach (var insn in bc.Instructions) { @@ -296,7 +302,7 @@ namespace NW4RTools { foreach (var kv in m.Nodes) { AddString(kv.Key); - OffsetMap.Add(CurrentPos, "Node: " + kv.Key); + LogPosition("Node: " + kv.Key); NodeOffsets.Add(kv.Value, CurrentPos); CurrentPos += 0xD0; @@ -307,7 +313,7 @@ namespace NW4RTools { foreach (var kv in m.Materials) { AddString(kv.Key); - OffsetMap.Add(CurrentPos, "Material: " + kv.Key); + LogPosition("Material: " + kv.Key); MaterialOffsets.Add(kv.Value, CurrentPos); // Base material struct @@ -336,7 +342,7 @@ namespace NW4RTools { // Texture Infos if (kv.Value.TextureInfos.Count > 0) - OffsetMap.Add(CurrentPos, "Material Texture Infos: " + kv.Key); + LogPosition("Material Texture Infos: " + kv.Key); for (int i = 0; i < kv.Value.TextureInfos.Count; i++) { TextureInfoOffsets[kv.Value.TextureInfos[i]] = CurrentPos; @@ -345,7 +351,7 @@ namespace NW4RTools { // Display Lists AlignCalcPos(0x20); - OffsetMap.Add(CurrentPos, "Material Display Lists: " + kv.Key); + LogPosition("Material Display Lists: " + kv.Key); MaterialDLOffsets[kv.Value] = CurrentPos; CurrentPos += 0x20 + 0x80 + 0x40 + 0xA0; } @@ -391,7 +397,7 @@ namespace NW4RTools { // This is a new shader, add it! calcInfo.UniqueShaders.Add(kv.Value); - OffsetMap.Add(CurrentPos, "Shader: " + kv.Key); + LogPosition("Shader: " + kv.Key); ShaderOffsets.Add(kv.Value, CurrentPos); CurrentPos += 0x200; @@ -403,7 +409,7 @@ namespace NW4RTools { foreach (var kv in m.Shapes) { AddString(kv.Key); - OffsetMap.Add(CurrentPos, "Shape: " + kv.Key); + LogPosition("Shape: " + kv.Key); ShapeOffsets.Add(kv.Value, CurrentPos); CurrentPos += 0x68; @@ -416,12 +422,12 @@ namespace NW4RTools { AlignCalcPos(0x20); ShapeDL1Offsets.Add(kv.Value, CurrentPos); - OffsetMap.Add(CurrentPos, "Shape DL 1: " + kv.Key); + LogPosition("Shape DL 1: " + kv.Key); CurrentPos += (int)kv.Value.DLBufferSize1; AlignCalcPos(0x20); ShapeDL2Offsets.Add(kv.Value, CurrentPos); - OffsetMap.Add(CurrentPos, "Shape DL 2: " + kv.Key); + LogPosition("Shape DL 2: " + kv.Key); CurrentPos += (int)kv.Value.DLBufferSize2; // Should this line be after the final alignment? @@ -436,7 +442,7 @@ namespace NW4RTools { foreach (var kv in m.VtxPosData) { AddString(kv.Key); - OffsetMap.Add(CurrentPos, "VertexPosData: " + kv.Key); + LogPosition("VertexPosData: " + kv.Key); VtxPosOffsets.Add(kv.Value, CurrentPos); // Main data @@ -446,7 +452,7 @@ namespace NW4RTools { CurrentPos += 0x18; AlignCalcPos(0x20); - OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + LogPosition("Data: " + kv.Key); CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; AlignCalcPos(0x20); } @@ -456,14 +462,14 @@ namespace NW4RTools { foreach (var kv in m.VtxNrmData) { AddString(kv.Key); - OffsetMap.Add(CurrentPos, "VertexNrmData: " + kv.Key); + LogPosition("VertexNrmData: " + kv.Key); VtxNrmOffsets.Add(kv.Value, CurrentPos); // Main data CurrentPos += 0x20; AlignCalcPos(0x20); - OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + LogPosition("Data: " + kv.Key); CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; AlignCalcPos(0x20); } @@ -473,14 +479,14 @@ namespace NW4RTools { foreach (var kv in m.VtxClrData) { AddString(kv.Key); - OffsetMap.Add(CurrentPos, "VertexClrData: " + kv.Key); + LogPosition("VertexClrData: " + kv.Key); VtxClrOffsets.Add(kv.Value, CurrentPos); // Main data CurrentPos += 0x20; AlignCalcPos(0x20); - OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + LogPosition("Data: " + kv.Key); CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; AlignCalcPos(0x20); } @@ -490,7 +496,7 @@ namespace NW4RTools { foreach (var kv in m.VtxTexCoordData) { AddString(kv.Key); - OffsetMap.Add(CurrentPos, "VertexTexCoordData: " + kv.Key); + LogPosition("VertexTexCoordData: " + kv.Key); VtxTexCoordOffsets.Add(kv.Value, CurrentPos); // Main data @@ -500,7 +506,7 @@ namespace NW4RTools { CurrentPos += 0x10; AlignCalcPos(0x20); - OffsetMap.Add(CurrentPos, "Data: " + kv.Key); + LogPosition("Data: " + kv.Key); CurrentPos += kv.Value.EntryCount * kv.Value.EntrySize; AlignCalcPos(0x20); } @@ -523,14 +529,14 @@ namespace NW4RTools { Debug.Send("Current: {0}", kv.Key); - OffsetMap.Add(CurrentPos, "Texture: " + kv.Key); + LogPosition("Texture: " + kv.Key); TextureOffsets.Add(kv.Value, CurrentPos); CurrentPos += 0x30; AlignCalcPos(0x20); - OffsetMap.Add(CurrentPos, "Texture Data for: " + kv.Key); + LogPosition("Texture Data for: " + kv.Key); TextureDataOffsets.Add(kv.Value, CurrentPos); CurrentPos += texture.GetDataSize(); @@ -650,7 +656,7 @@ namespace NW4RTools { Output.WriteInt32(calcInfo.Materials - startPos); Output.WriteInt32(calcInfo.Shaders - startPos); Output.WriteInt32(calcInfo.Shapes - startPos); - Output.WriteInt32(calcInfo.PairingLookupByTexture - startPos); + Output.WriteInt32((calcInfo.PairingLookupByTexture == 0) ? 0 : (calcInfo.PairingLookupByTexture - startPos)); // Pairing lookup by palette, unhandled atm Output.WriteInt32(0); // Unknown extra data @@ -673,8 +679,7 @@ namespace NW4RTools { Output.WriteUInt32((uint)model.MatrixIDtoNodeID.Length); Output.WriteByte(model.UsesNrmMtxArray ? (byte)1 : (byte)0); Output.WriteByte(model.UsesTexMtxArray ? (byte)1 : (byte)0); - // Padding - Output.WriteInt16(0); + Output.AddPadding(2); Output.WriteInt32(calcInfo.MatrixIDtoNodeID - infoStructPos); Output.WriteVec3(model.Minimum); Output.WriteVec3(model.Maximum); @@ -700,7 +705,8 @@ namespace NW4RTools { WriteResDict<Material>(model.Materials, MaterialOffsets); WriteResDict<Shader>(model.Shaders, ShaderOffsets); WriteResDict<Shape>(model.Shapes, ShapeOffsets); - WriteResDict<List<TexMatPairing>>(model.PairingLookupByTexture, PairingOffsets); + if (model.PairingLookupByTexture.Count > 0) + WriteResDict<List<TexMatPairing>>(model.PairingLookupByTexture, PairingOffsets); // TODO: Palette pairing lookups @@ -735,6 +741,7 @@ namespace NW4RTools { private void WriteBytecode(Model m) { foreach (var kv in m.Bytecode) { + Debug.Send("Writing bytecode {0} @ offset {1:X}", kv.Key, Output.Position); ByteCode bc = kv.Value; foreach (var insn in bc.Instructions) { @@ -787,6 +794,7 @@ namespace NW4RTools { int currentIndex = 0; foreach (var kv in m.Nodes) { + Debug.Send("Writing node {0} @ offset {1:X}", kv.Key, Output.Position); Node node = kv.Value; int startPos = Output.Position; @@ -831,6 +839,7 @@ namespace NW4RTools { int currentIndex = 0; foreach (var kv in m.Materials) { + Debug.Send("Writing material {0} @ offset {1:X}", kv.Key, Output.Position); Material mat = kv.Value; int startPos = Output.Position; @@ -897,8 +906,13 @@ namespace NW4RTools { // ResTlutObj Output.WriteUInt32(mat.TlutFlag); - Output.WriteBytes(mat.TlutObj); - Output.AddPadding(0x60 - mat.TlutObj.Length); + + if (mat.TlutObj == null) { + Output.AddPadding(0x60); + } else { + Output.WriteBytes(mat.TlutObj); + Output.AddPadding(0x60 - mat.TlutObj.Length); + } // ResTexSrt // this one is a bit of a pain @@ -1032,6 +1046,7 @@ namespace NW4RTools { int currentIndex = 0; foreach (var shader in ModelCalcInfos[m].UniqueShaders) { + Debug.Send("Writing shader @ offset {0:X}", Output.Position); int startPos = Output.Position; // Size, model offset, index @@ -1058,6 +1073,7 @@ namespace NW4RTools { int currentIndex = 0; foreach (var kv in m.Shapes) { + Debug.Send("Writing shape {0} @ offset {1:X}", kv.Key, Output.Position); Shape shape = kv.Value; int startPos = Output.Position; @@ -1142,6 +1158,7 @@ namespace NW4RTools { int currentIndex = 0; foreach (var kv in dict) { + Debug.Send("Writing vtxdata {0} @ offset {1:X}", kv.Key, Output.Position); int startPos = Output.Position; int structSize = 0x20; diff --git a/NW4RTools/Enums.cs b/NW4RTools/Enums.cs index 734eb9b..1ee089f 100644 --- a/NW4RTools/Enums.cs +++ b/NW4RTools/Enums.cs @@ -18,7 +18,9 @@ namespace NW4RTools { LoadNrmMtxFromArray = 0x28, LoadTexCoordMtxFromArray = 0x30, LoadLightFromArray = 0x38, - DrawPrimitiveMask = 0x80 + DrawPrimitiveMask = 0x80, + // TODO: Figure out some better way to store this + PrimitiveShiftAmount = 3 } diff --git a/NW4RTools/Models/Material.cs b/NW4RTools/Models/Material.cs index 0bf0acc..519790c 100644 --- a/NW4RTools/Models/Material.cs +++ b/NW4RTools/Models/Material.cs @@ -18,6 +18,17 @@ namespace NW4RTools.Models { public byte MapType; public byte Flags; public Matrix TexMatrix; + + public SRTSettingInfo() { + ScaleX = 1.0f; + ScaleY = 1.0f; + + CameraID = 0xFF; + LightID = 0xFf; + Flags = 1; + + TexMatrix.Identity(); + } } @@ -62,6 +73,8 @@ namespace NW4RTools.Models { IndirectTexMtxCalcMethod2 = new byte[4]; TexObj = new byte[8][]; // todo + SRTSettings = new SRTSettingInfo[8]; + ChanCtrls = new ChanCtrl[2]; TextureInfos = new List<TextureInfo>(); } } diff --git a/NW4RTools/Models/OpenGL/GLModel.cs b/NW4RTools/Models/OpenGL/GLModel.cs index 8ca3f31..072ea00 100644 --- a/NW4RTools/Models/OpenGL/GLModel.cs +++ b/NW4RTools/Models/OpenGL/GLModel.cs @@ -257,7 +257,7 @@ namespace NW4RTools.Models.OpenGL { break; if ((cmd & (int)GXCommand.DrawPrimitiveMask) != 0) { - PrimitiveType prim = (PrimitiveType)((cmd >> 3) & 7); + PrimitiveType prim = (PrimitiveType)((cmd >> (byte)GXCommand.PrimitiveShiftAmount) & 7); int vtxCount = dl.ReadUInt16(); if ((cmd & 7) != 0) diff --git a/NW4RTools/Models/VertexData.cs b/NW4RTools/Models/VertexData.cs index 187b9aa..feb8eb6 100644 --- a/NW4RTools/Models/VertexData.cs +++ b/NW4RTools/Models/VertexData.cs @@ -60,6 +60,54 @@ namespace NW4RTools.Models { } } + public virtual void Save() { + int elementCount = Data[0].Length; + float scale = 1.0f / (1 << Fraction); + var output = new OutputStream(); + + switch (ComponentType) { + case VertexSettings.CompType.UInt8: + foreach (var array in Data) { + for (int i = 0; i < elementCount; i++) { + output.WriteByte((byte)(array[i] / scale)); + } + } + break; + case VertexSettings.CompType.Int8: + foreach (var array in Data) { + for (int i = 0; i < elementCount; i++) { + output.WriteSByte((sbyte)(array[i] / scale)); + } + } + break; + case VertexSettings.CompType.UInt16: + foreach (var array in Data) { + for (int i = 0; i < elementCount; i++) { + output.WriteUInt16((ushort)(array[i] / scale)); + } + } + break; + case VertexSettings.CompType.Int16: + foreach (var array in Data) { + for (int i = 0; i < elementCount; i++) { + output.WriteInt16((short)(array[i] / scale)); + } + } + break; + case VertexSettings.CompType.Float32: + foreach (var array in Data) { + for (int i = 0; i < elementCount; i++) { + output.WriteFloat(array[i]); + } + } + break; + default: + throw new NotImplementedException(String.Format("unimplemented type {0}", (int)ComponentType)); + } + + RawData = output.GetBuffer(); + } + public abstract int GetRealCount(); } @@ -186,6 +234,10 @@ namespace NW4RTools.Models { return ret; } + + public override void Save() { + throw new NotImplementedException(); + } } diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj index 80115cd..07fec58 100644 --- a/NW4RTools/NW4RTools.csproj +++ b/NW4RTools/NW4RTools.csproj @@ -59,7 +59,6 @@ <Compile Include="Models\Shader.cs" /> <Compile Include="Models\Shape.cs" /> <Compile Include="Models\TextureInfo.cs" /> - <Compile Include="ObjWriter.cs" /> <Compile Include="Enums.cs" /> <Compile Include="DisplayList.cs" /> <Compile Include="VertexSettings.cs" /> @@ -76,6 +75,8 @@ <Compile Include="Models\Animation\TextureSRTAnim.cs" /> <Compile Include="Util\NVDXT.cs" /> <Compile Include="ColladaExporter.cs" /> + <Compile Include="ObjExporter.cs" /> + <Compile Include="ObjImporter.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/NW4RTools/NW4RTools.pidb b/NW4RTools/NW4RTools.pidb Binary files differindex 5dbaf53..757d6c2 100644 --- a/NW4RTools/NW4RTools.pidb +++ b/NW4RTools/NW4RTools.pidb 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; + } + } + } + } +} + diff --git a/NW4RTools/ObjImporter.cs b/NW4RTools/ObjImporter.cs new file mode 100755 index 0000000..41b9ea4 --- /dev/null +++ b/NW4RTools/ObjImporter.cs @@ -0,0 +1,759 @@ +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using NW4RTools.Models; + +namespace NW4RTools { + public class ObjImporter { + public static void ImportModel(string basePath, TextReader tr, ResFile file, string modelName) { + new ObjImporter(basePath, file, tr).ImportModel(modelName); + } + + + + string BasePath; + ResFile CurrentFile; + Models.Model CurrentModel; + TextReader Input; + + List<float[]> Positions; + List<float[]> Normals; + List<float[]> TexCoords; + + Vec3 CurrentMinimum; + Vec3 CurrentMaximum; + + private ObjImporter(string basePath, ResFile file, TextReader tr) { + BasePath = basePath; + CurrentFile = file; + Input = tr; + } + + public void ImportModel(string modelName) { + var modelGroup = CurrentFile.CreateModelGroup(); + var texGroup = CurrentFile.CreateTextureGroup(); + + // OK, so here's what I'm going to do: + // I'll read the file into an internal array and process commands as they come. + + // Vertex position/normal/texcoord (v, vn, vt): + // -- These will be added into a list. + + // Faces (f): + // -- These will be added into a list for the appropriate group. + + // Set group (g): + // -- A shape will be created for this group. It will be added to DrawOpa. + // -- TODO: Add DrawXlu handling. + + // Load material library (mtllib): + // -- The specified file will be loaded. + // -- All materials in it (and their associated textures) will be added to the model. + + // Set current material (usemtl): + // -- A different material will be assigned for the current shape. + + CurrentModel = new Model(); + modelGroup.Add(modelName, CurrentModel); + + // Before we start reading the OBJ file, prepare the model + CurrentModel.ScaleMode = Model.ScaleModeType.Standard; + CurrentModel.TexMatrixMode = Model.TexMatrixModeType.Maya; + CurrentModel.UsesNrmMtxArray = true; + // why? + + // Todo: vertex count, triangle count + // Minimum, Maximum will be calc'd later + CurrentMinimum = new Vec3(0, 0, 0); + CurrentMaximum = new Vec3(0, 0, 0); + + // Create one node + // Default settings from test_lift.brres + var newNode = new Node(); + newNode.Flags = 0x31F; + newNode.Scale = new Vec3(1, 1, 1); + newNode.BoxMin = new Vec3(-16.0f, -16.0f, 0.0f); + newNode.BoxMax = new Vec3(16.0f, 16.0f, 0.0f); + + newNode.NodeMatrix.v00 = 1; + newNode.NodeMatrix.v01 = 0; + newNode.NodeMatrix.v02 = 0; + newNode.NodeMatrix.v03 = 0; + newNode.NodeMatrix.v10 = 0; + newNode.NodeMatrix.v11 = 1; + newNode.NodeMatrix.v12 = 0; + newNode.NodeMatrix.v13 = 0; + newNode.NodeMatrix.v20 = 0; + newNode.NodeMatrix.v21 = 0; + newNode.NodeMatrix.v22 = 1; + newNode.NodeMatrix.v23 = 0; + + newNode.NodeInvMatrix.v00 = 1; + newNode.NodeInvMatrix.v01 = -0; + newNode.NodeInvMatrix.v02 = 0; + newNode.NodeInvMatrix.v03 = 0; + newNode.NodeInvMatrix.v10 = -0; + newNode.NodeInvMatrix.v11 = 1; + newNode.NodeInvMatrix.v12 = -0; + newNode.NodeInvMatrix.v13 = 0; + newNode.NodeInvMatrix.v20 = 0; + newNode.NodeInvMatrix.v21 = -0; + newNode.NodeInvMatrix.v22 = 1; + newNode.NodeInvMatrix.v23 = 0; + + CurrentModel.Nodes.Add("RootNode", newNode); + + // Map it correctly + CurrentModel.MatrixIDtoNodeID = new int[1]; + CurrentModel.MatrixIDtoNodeID[0] = 0; + + // Now put together the NodeTree + var nodeTreeInsn = new ByteCode.AssignNodeToParentMtxInstruction(); + nodeTreeInsn.NodeID = 0; + nodeTreeInsn.ParentMatrixID = 0; + + var nodeTreeEndInsn = new ByteCode.DoneInstruction(); + + var nodeTree = new ByteCode(); + nodeTree.Instructions.Add(nodeTreeInsn); + nodeTree.Instructions.Add(nodeTreeEndInsn); + + CurrentModel.Bytecode.Add("NodeTree", nodeTree); + + // Also, DrawOpa + var drawOpa = new ByteCode(); + CurrentModel.Bytecode.Add("DrawOpa", drawOpa); + + // Initial setup is done, let's go! + Positions = new List<float[]>(); + Normals = new List<float[]>(); + TexCoords = new List<float[]>(); + + string line; + + while ((line = Input.ReadLine()) != null) { + line = line.Trim(); + + if (line.Length == 0 || line[0] == '#') + continue; + + var parsed = line.Split(' '); + + switch (parsed[0]) { + case "mtllib": + LoadMaterialLibrary(string.Join(" ", parsed, 1, parsed.Length - 1)); + break; + case "v": + Positions.Add(ParseFloatArray(parsed, 1)); + break; + case "vn": + Normals.Add(ParseFloatArray(parsed, 1)); + break; + case "vt": + TexCoords.Add(ParseFloatArray(parsed, 1)); + break; + case "f": + AddFace(parsed); + break; + case "g": + Console.WriteLine("Beginning shape {0}", parsed[1]); + BeginShape(parsed[1]); + break; + case "usemtl": + Console.WriteLine("Setting material {0}", parsed[1]); + SetMaterial(CurrentModel.Materials[parsed[1]]); + break; + default: + Console.WriteLine("Unhandled OBJ command: {0}", parsed[0]); + break; + } + } + + EndShape(); + + // Parsing is finished. Let's finish up DrawOpa + drawOpa.Instructions.Add(new ByteCode.DoneInstruction()); + } + + + private float[] ParseFloatArray(string[] src, int index) { + var output = new float[src.Length - index]; + + for (int i = index; i < src.Length; i++) { + output[i - index] = float.Parse(src[i]); + } + + return output; + } + + + #region Materials + private void LoadMaterialLibrary(string libPath) { + string realPath = Path.Combine(BasePath, libPath); + Console.WriteLine("Loading material library from {0}", realPath); + + var reader = File.OpenText(realPath); + string line; + Material currentMaterial = null; + + while ((line = reader.ReadLine()) != null) { + line = line.Trim(); + + if (line.Length == 0 || line[0] == '#') + continue; + + var parsed = line.Split(' ');
+
+ switch (parsed[0]) {
+ case "newmtl":
+ // Create a new material and initialise everything
+ // Default settings taken from Test_Lift material
+ currentMaterial = new Material();
+
+ currentMaterial.TexCoordGenCount = 1;
+ currentMaterial.ChanCount = 1;
+ currentMaterial.TevStageCount = 2;
+ currentMaterial.IndStageCount = 0;
+
+// This might need changing
+ currentMaterial.CullMode = 2;
+
+ currentMaterial.SRTSettings[0] = new SRTSettingInfo();
+
+ currentMaterial.ChanCtrls[0] = new ChanCtrl();
+ currentMaterial.ChanCtrls[0].Flags = 0x3F;
+ currentMaterial.ChanCtrls[0].MatColor.Rgba = 0xFFFFFFFF;
+ currentMaterial.ChanCtrls[0].AmbColor.Rgba = 0xFFFFFFFF;
+ currentMaterial.ChanCtrls[0].FlagC = 0x702;
+ currentMaterial.ChanCtrls[0].FlagA = 0x700;
+
+ currentMaterial.ChanCtrls[1] = new ChanCtrl();
+ currentMaterial.ChanCtrls[1].Flags = 0xF;
+ currentMaterial.ChanCtrls[1].MatColor.Rgba = 0x000000FF;
+ currentMaterial.ChanCtrls[1].AmbColor.Rgba = 0x00000000;
+ currentMaterial.ChanCtrls[1].FlagC = 0;
+ currentMaterial.ChanCtrls[1].FlagA = 0;
+
+// Default display lists, taken from test_lift
+ // The current version of MonoDevelop likes to make an awful mess + // of the indentation. There's nothing I can do about it. :/ + currentMaterial.PixDL = new byte[] { + 0x61, 0xF3, 0x1E, 0xFF, 0x80, + 0x61, 0x40, 0x00, 0x00, 0x17, + 0x61, 0xFE, 0x00, 0xFF, 0xE3, + 0x61, 0x41, 0x00, 0x34, 0xA0, + 0x61, 0x42, 0x00, 0x00, 0x00, +/* Padding */ + 0, 0, 0, 0, 0, + 0, 0 + }; + + currentMaterial.TevColorDL = new byte[] { + 0x61, 0xE2, 0x00, 0x00, 0xFF, + 0x61, 0xE3, 0x0F, 0xF0, 0xFF, + 0x61, 0xE3, 0x0F, 0xF0, 0xFF, + 0x61, 0xE3, 0x0F, 0xF0, 0xFF, + 0x61, 0xE4, 0x00, 0x00, 0x00, + 0x61, 0xE5, 0x00, 0x00, 0x00, + 0x61, 0xE5, 0x00, 0x00, 0x00, + 0x61, 0xE5, 0x00, 0x00, 0x00, + 0x61, 0xE6, 0x00, 0x00, 0x00, + 0x61, 0xE7, 0x00, 0x00, 0x00, + 0x61, 0xE7, 0x00, 0x00, 0x00, + 0x61, 0xE7, 0x00, 0x00, 0x00, + /* Padding */ + 0, 0, 0, 0, + 0x61, 0xE0, 0x80, 0x00, 0x00, + 0x61, + 0xE1, 0x80, 0x00, 0x00, + 0x61, 0xE2, 0x80, 0x00, 0x00, + 0x61, + 0xE3, 0x80, 0x00, 0x00, + 0x61, 0xE4, 0x80, 0x00, 0x00, + 0x61, + 0xE5, 0x80, 0x00, 0x00, + 0x61, 0xE6, 0x80, 0x00, 0x00, + 0x61, + 0xE7, 0x80, 0x00, 0x00, +/* More padding */ + 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + + currentMaterial.IndMtxAndScaleDL = new byte[] { + 0x61, 0x25, 0x00, 0x00, 0x00, + 0x61, 0x26, 0x00, 0x00, 0x00, + /* Padding */ + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 + }; + + currentMaterial.TexCoordGenDL = new byte[] { + 0x10, 0x00, 0x00, 0x10, 0x40, 0x00, 0x00, 0x52, + 0x80, 0x10, + 0x00, 0x00, 0x10, 0x50, 0x00, 0x00, +/* Padding? */ + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, +0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +CurrentModel.Materials.Add(parsed[1], currentMaterial); + + +Shader thisShader = CreateShader(); + CurrentModel.Shaders.Add(parsed[1], thisShader); + currentMaterial.ShaderRef = thisShader;
+
+ break;
+
+ case "map_Kd": + var rawTexName = string.Join(" ", parsed, 1, parsed.Length - 1); +// TODO: fix this to use the mtllib path + var texPath = Path.Combine(BasePath, rawTexName); + var texName = Path.GetFileNameWithoutExtension(texPath); + + if (!CurrentFile.GetTextureGroup().ContainsKey(texName)) + AddTexture(texName, texPath); + + var texInfo = new TextureInfo(); + texInfo.TextureName = texName; + + texInfo.WrapS = TextureWrapType.REPEAT; + texInfo.WrapT = TextureWrapType.REPEAT; + + texInfo.MinFilt = 1; + texInfo.MagFilt = 1; + + currentMaterial.TextureInfos.Add(texInfo); +
+ break;
+
+ + } + } + } + + + private void AddTexture(string texName, string texPath) { + var newTexture = new Texture(); + + newTexture.Images = new System.Drawing.Bitmap[1]; + newTexture.Images[0] = new System.Drawing.Bitmap(texPath); + + newTexture.Format = TextureFormat.RGB5A3; + + CurrentFile.GetTextureGroup().Add(texName, newTexture); + + // TODO: Texture/Material pairing lookups + } + + + private Shader CreateShader() { + var shader = new Shader(); + + shader.TevStageCount = 2; + shader.Unk1 = 0x00FFFFFF; + shader.Unk2 = 0xFFFFFFFF; + shader.DisplayList = new byte[] { + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xF6, 0x00, 0x00, 0x04, + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xF7, 0x00, 0x00, 0x0E, + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xF8, 0x00, 0x00, 0x00, + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xF9, 0x00, 0x00, 0x0C, + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xFA, 0x00, 0x00, 0x05, + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xFB, 0x00, 0x00, 0x0D, + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xFC, 0x00, 0x00, 0x0A, + 0x61, 0xFE, 0x00, 0x00, 0x0F, + 0x61, 0xFD, 0x00, 0x00, 0x0E, + 0x61, 0x27, 0xFF, 0xFF, 0xFF, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0x61, 0xFE, 0xFF, 0xFF, + 0xF0, + 0x61, 0xF6, 0xE3, 0x38, 0xC0, + 0x61, 0x28, 0x03, 0xF0, + 0x40, + 0x61, 0xC0, 0x08, 0xF8, 0xAF, + 0x61, 0xC2, 0x08, 0xF2, + 0x0F, + 0x61, 0xC1, 0x08, 0xF2, 0xF0, + 0x61, 0xC3, 0x08, 0x1F, + 0xF0, +0x61, 0x10, 0x00, 0x00, 0x00, +0x61, 0x11, 0x00, 0x00, + 0x00 +}; + + Array.Resize<byte>(ref shader.DisplayList, 0x1E0); + + return shader; + } + #endregion + + + + #region Shapes + Shape CurrentShape; + Material CurrentShapeMaterial; + + List<ushort[]> Quads; + List<ushort[]> Triangles; + + BitArray UsedVertices; + BitArray UsedNormals; + BitArray UsedTexCoords; + + private void BeginShape(string name) { + if (CurrentShape != null) + EndShape(); + + CurrentShape = new Shape(); + CurrentShape.Unk = new byte[] { 0, 0, 0x14, 0, 0, 0, 0, 0x02, 0, + 0, 0, 0x14 }; + CurrentShape.DataFlags = 0x2600; + CurrentShape.ExtraData = new ushort[0]; + + CurrentModel.Shapes.Add(name, CurrentShape); + + Quads = new List<ushort[]>(); + Triangles = new List<ushort[]>(); + + UsedVertices = new BitArray(Positions.Count); + UsedTexCoords = new BitArray(TexCoords.Count); + UsedNormals = new BitArray(Normals.Count); + } + + + private void SetMaterial(Material mat) { + CurrentShapeMaterial = mat; + } + + + private void AddFace(string[] cmd) { + int vtxCount = cmd.Length - 1; + if (vtxCount != 3 && vtxCount != 4) { + throw new NotSupportedException(string.Format("cannot deal with a {0} vertices primitive", vtxCount)); + } + + var output = new ushort[vtxCount * 3]; + + for (int i = 0; i < vtxCount; i++) { + var s = cmd[i + 1]; + + int pos1 = s.IndexOf('/'); + int pos2 = s.IndexOf('/', pos1 + 1); + + int vnum = int.Parse(s.Substring(0, pos1)) - 1; + int tcnum = int.Parse(s.Substring(pos1 + 1, pos2 - pos1 - 1)) - 1; + int nnum = int.Parse(s.Substring(pos2 + 1)) - 1; + + UsedVertices[vnum] = true; + UsedTexCoords[tcnum] = true; + UsedNormals[nnum] = true; + + output[i * 3] = (ushort)vnum; + output[i * 3 + 1] = (ushort)nnum; + output[i * 3 + 2] = (ushort)tcnum; + } + + if (vtxCount == 3) + Triangles.Add(output); + else + Quads.Add(output); + } + + + private void EndShape() { + // Let's assemble the display lists. + + // ** Reverse Engineering DL 1 ** + // 0x0A : CP command: Vertex Descriptor part 1 + // 0x10 : CP command: Vertex Descriptor part 2 + // 0x16 : XF command: Address 0x1008 ["XFMEM_VTXSPECS"], Transfer: 0 + // 0x1F : Padding: NOP + // 0x20 : CP command: Vertex Attribute Format part 1 + // 0x26 : CP command: Vertex Attribute Format part 2 + // 0x2C : CP command: Vertex Attribute Format part 3 + // 0x32 : Dynamic CP command: Position Array Pointer + // 0x38 : Dynamic CP command: Position Array Stride + // 0x3E : Dynamic CP command: Normal Array Pointer + // 0x44 : Dynamic CP command: Normal Array Pointer + // 0x4A : Dynamic CP command: Colour 0 Array Pointer + // 0x50 : Dynamic CP command: Colour 0 Array Stride + // 0x56 : Dynamic CP command: Colour 1 Array Pointer + // 0x5C : Dynamic CP command: Colour 1 Array Stride + // 0x62 : Dynamic CP command: TexCoord 0 Array Pointer + // 0x68 : Dynamic CP command: TexCoord 0 Array Stride + // 0x6E : Dynamic CP command: TexCoord 1 Array Pointer + // 0x74 : Dynamic CP command: TexCoord 1 Array Stride + // 0x7A : Dynamic CP command: TexCoord 2 Array Pointer + // 0x80 : Dynamic CP command: TexCoord 2 Array Stride + // 0x86 : Dynamic CP command: TexCoord 3 Array Pointer + // 0x8C : Dynamic CP command: TexCoord 3 Array Stride + // 0x92 : Dynamic CP command: TexCoord 4 Array Pointer + // 0x98 : Dynamic CP command: TexCoord 4 Array Stride + // 0x9E : Dynamic CP command: TexCoord 5 Array Pointer + // 0xA4 : Dynamic CP command: TexCoord 5 Array Stride + // 0xAA : Dynamic CP command: TexCoord 6 Array Pointer + // 0xB0 : Dynamic CP command: TexCoord 6 Array Stride + // 0xB6 : Dynamic CP command: TexCoord 7 Array Pointer + // 0xBC : Dynamic CP command: TexCoord 7 Array Stride + + // Now create vertex settings + var vs = new VertexSettings(); + vs.PositionDesc = VertexSettings.DescType.Index16; + vs.PositionFormat = VertexSettings.CompType.Float32; + vs.PositionCount = VertexSettings.CompCount.Position3; + + vs.TexCoordDesc[0] = VertexSettings.DescType.Index16; + vs.TexCoordFormat[0] = VertexSettings.CompType.Float32; + vs.TexCoordCount[0] = VertexSettings.CompCount.TexCoord2; + + vs.NormalDesc = VertexSettings.DescType.Index16; + vs.NormalFormat = VertexSettings.CompType.Float32; + vs.NormalCount = VertexSettings.CompCount.Normal3; + + uint vd1, vd2, vat1, vat2, vat3; + vs.GetDesc(out vd1, out vd2); + vs.GetAttrFmt(out vat1, out vat2, out vat3); + + + // Todo: a Display List generator class + + CurrentShape.DLBufferSize1 = 0xE0; + CurrentShape.DisplayList1 = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0x08, 0x50, + (byte)((vd1 & 0xFF000000) >> 24), + (byte)((vd1 & 0x00FF0000) >> 16), + (byte)((vd1 & 0x0000FF00) >> 8), + (byte)((vd1 & 0x000000FF)), + 0x08, 0x60, + (byte)((vd2 & 0xFF000000) >> 24), + (byte)((vd2 & 0x00FF0000) >> 16), + (byte)((vd2 & 0x0000FF00) >> 8), + (byte)((vd2 & 0x000000FF)), + 0x10, 0x00, 0x00, 0x10, 0x08, 0x00, 0x00, 0x00, + 0x14, 0, + 0x08, 0x70, + (byte)((vat1 & 0xFF000000) >> 24), + (byte)((vat1 & 0x00FF0000) >> 16), + (byte)((vat1 & 0x0000FF00) >> 8), + (byte)((vat1 & 0x000000FF)), + 0x08, 0x80, + (byte)((vat2 & 0xFF000000) >> 24), + (byte)((vat2 & 0x00FF0000) >> 16), + (byte)((vat2 & 0x0000FF00) >> 8), + (byte)((vat2 & 0x000000FF)), + 0x08, 0x90, + (byte)((vat3 & 0xFF000000) >> 24), + (byte)((vat3 & 0x00FF0000) >> 16), + (byte)((vat3 & 0x0000FF00) >> 8), + (byte)((vat3 & 0x000000FF)) + }; + + + // I might need to create XFMEM_VTXSPECS... + // test_lift uses 0x14. According to Dolphin's src, this means: + // numcolors = 0, numnormals = 1 (just normal), numtextures = 1. Makes sense. + byte vtxSpecs = 0 | (1 << 2) | (1 << 4); + CurrentShape.DisplayList1[0x1E] = vtxSpecs; + + // extend it + // should it be bigger if more texcoords are used? maybe... + Array.Resize<byte>(ref CurrentShape.DisplayList1, 0x80); + + +// Display List 2 is where all the primitive-related fun happens + // However, before we do that, let's compute the vertex data arrays + ushort[] posIndexes, texCoordIndexes, normalIndexes; + + var posDataArray = ComputeVertexDataArray(Positions, UsedVertices, out posIndexes); + var tcDataArray = ComputeVertexDataArray(TexCoords, UsedTexCoords, out texCoordIndexes); + var nrmDataArray = ComputeVertexDataArray(Normals, UsedNormals, out normalIndexes); + + // todo: better names + var posData = new VertexPosData(); + posData.ComponentCount = VertexSettings.CompCount.Position3; + posData.ComponentType = VertexSettings.CompType.Float32; + posData.EntryCount = (ushort)posDataArray.Length; + posData.EntrySize = 12; + posData.Fraction = 0; + posData.Data = posDataArray; + posData.Save(); + + CurrentModel.VtxPosData.Add("Pos" + CurrentModel.VtxPosData.Count.ToString(), posData); + + var tcData = new VertexTexCoordData(); + tcData.ComponentCount = VertexSettings.CompCount.TexCoord2; + tcData.ComponentType = VertexSettings.CompType.Float32; + tcData.EntryCount = (ushort)tcDataArray.Length; + tcData.EntrySize = 8; + tcData.Fraction = 0; + tcData.Data = tcDataArray; + tcData.Save(); + + CurrentModel.VtxTexCoordData.Add("TexCoord" + CurrentModel.VtxTexCoordData.Count.ToString(), tcData); + + // TODO: Flip texcoords + + var nrmData = new VertexNrmData(); + nrmData.ComponentCount = VertexSettings.CompCount.Normal3; + nrmData.ComponentType = VertexSettings.CompType.Float32; + nrmData.EntryCount = (ushort)nrmDataArray.Length; + nrmData.EntrySize = 12; + nrmData.Fraction = 0; + nrmData.Data = nrmDataArray; + nrmData.Save(); + + CurrentModel.VtxNrmData.Add("Nrm" + CurrentModel.VtxNrmData.Count.ToString(), nrmData); + + CurrentShape.PosData = posData; + CurrentShape.TexCoordData[0] = tcData; + CurrentShape.NrmData = nrmData; + + var dl = new OutputStream(); + + if (Triangles.Count > 0) { + dl.WriteByte((byte)GXCommand.DrawPrimitiveMask | ((byte)PrimitiveType.Triangles << (byte)GXCommand.PrimitiveShiftAmount)); + dl.WriteUInt16((ushort)(Triangles.Count * 3)); + + foreach (var tri in Triangles) { + dl.WriteUInt16(posIndexes[tri[0]]); + dl.WriteUInt16(normalIndexes[tri[1]]); + dl.WriteUInt16(texCoordIndexes[tri[2]]); + dl.WriteUInt16(posIndexes[tri[3]]); + dl.WriteUInt16(normalIndexes[tri[4]]); + dl.WriteUInt16(texCoordIndexes[tri[5]]); + dl.WriteUInt16(posIndexes[tri[6]]); + dl.WriteUInt16(normalIndexes[tri[7]]); + dl.WriteUInt16(texCoordIndexes[tri[8]]); + } + } + + if (Quads.Count > 0) { + dl.WriteByte((byte)GXCommand.DrawPrimitiveMask | ((byte)PrimitiveType.Quads << (byte)GXCommand.PrimitiveShiftAmount)); + dl.WriteUInt16((ushort)(Quads.Count * 3)); + + foreach (var quad in Quads) { + dl.WriteUInt16(posIndexes[quad[0]]); + dl.WriteUInt16(normalIndexes[quad[1]]); + dl.WriteUInt16(texCoordIndexes[quad[2]]); + dl.WriteUInt16(posIndexes[quad[3]]); + dl.WriteUInt16(normalIndexes[quad[4]]); + dl.WriteUInt16(texCoordIndexes[quad[5]]); + dl.WriteUInt16(posIndexes[quad[6]]); + dl.WriteUInt16(normalIndexes[quad[7]]); + dl.WriteUInt16(texCoordIndexes[quad[8]]); + dl.WriteUInt16(posIndexes[quad[9]]); + dl.WriteUInt16(normalIndexes[quad[10]]); + dl.WriteUInt16(texCoordIndexes[quad[11]]); + } + } + + dl.AlignTo(0x20); + + CurrentShape.DisplayList2 = dl.GetBuffer(); + CurrentShape.DLBufferSize2 = (uint)CurrentShape.DisplayList2.Length; + + // now add it to DrawOpa + var newInsn = new ByteCode.DrawShapeInstruction(); + newInsn.NodeID = 0; + newInsn.MaterialID = (ushort)CurrentModel.Materials.GetIndexForValue(CurrentShapeMaterial); + newInsn.ShapeID = (ushort)CurrentModel.Shapes.GetIndexForValue(CurrentShape); + + CurrentModel.Bytecode["DrawOpa"].Instructions.Add(newInsn); + } + + + + private float[][] ComputeVertexDataArray(List<float[]> objData, BitArray usedFields, out ushort[] indexes) { + indexes = new ushort[usedFields.Count]; + + var output = new List<float[]>(); + + // How this will work: + // I'll loop through every used vertex index in the BitArray. + // If it's used, I will compare it to the ones already in the output list. + + // If the vertex is already in the output list, then I'll take the index of it + // and write it to the "indexes" array. If not, then I'll add it to the output list and do that. + + // The "indexes" array matches input vertex indexes to the indexes in the optimised array. + + int vertexCount = usedFields.Count; + int elementCount = objData[0].Length; + + for (int i = 0; i < usedFields.Count; i++) { + if (usedFields[i]) { + // this one is used, let's try to find it + var thisVtx = objData[i]; + bool foundIt = false; + int j; + + for (j = 0; j < output.Count; j++) { + var check = output[j]; + bool isEqual = true; + for (int k = 0; k < elementCount; k++) + isEqual &= (thisVtx[k] == check[k]); + + if (isEqual) { + foundIt = true; + break; + } + } + + if (foundIt) { + // it already exists, just add it to the indexes list + indexes[i] = (ushort)j; + } else { + // nope, add it to the computed list + indexes[i] = (ushort)output.Count; + output.Add(thisVtx); + } + } + } + + return output.ToArray(); + } + #endregion + } +} + diff --git a/NW4RTools/ResFile.cs b/NW4RTools/ResFile.cs index 02919e5..4f4a1b8 100644 --- a/NW4RTools/ResFile.cs +++ b/NW4RTools/ResFile.cs @@ -2,11 +2,46 @@ using System; namespace NW4RTools { public class ResFile : ResDict<object> { + public const string ModelGroupName = "3DModels(NW4R)"; + public const string TextureGroupName = "Textures(NW4R)"; + + public UInt16 Version; + public ResDict<TValue> CreateGroup<TValue>(string name) { + if (ContainsKey(name)) { + return this[name] as ResDict<TValue>; + } else { + var newDict = new ResDict<TValue>(); + this[name] = newDict; + return newDict; + } + } + public ResDict<TValue> GetGroup<TValue>(string name) { return this[name] as ResDict<TValue>; } + + + #region Specific Group Creators + public ResDict<Models.Model> CreateModelGroup() { + return CreateGroup<Models.Model>(ModelGroupName); + } + + public ResDict<Texture> CreateTextureGroup() { + return CreateGroup<Texture>(TextureGroupName); + } + #endregion + + #region Specific Group Getters + public ResDict<Models.Model> GetModelGroup() { + return GetGroup<Models.Model>(ModelGroupName); + } + + public ResDict<Texture> GetTextureGroup() { + return GetGroup<Texture>(TextureGroupName); + } + #endregion } } diff --git a/NW4RTools/Types.cs b/NW4RTools/Types.cs index 78723dc..619303e 100644 --- a/NW4RTools/Types.cs +++ b/NW4RTools/Types.cs @@ -9,19 +9,65 @@ namespace NW4RTools { public struct Color { public byte r, g, b, a; + + public uint Rgba { + get { + return ((uint)r << 24) | ((uint)g << 16) | ((uint)b << 8) | (uint)a; + } + set { + r = (byte)((value & 0xFF000000) >> 24); + g = (byte)((value & 0x00FF0000) >> 16); + b = (byte)((value & 0x0000FF00) >> 8); + a = (byte)(value & 0x000000FF); + } + } + + public Color(byte _r, byte _g, byte _b, byte _a) { + r = _r; + g = _g; + b = _b; + a = _a; + } } public struct Vec3 { public float x, y, z; + + public Vec3(float _x, float _y, float _z) { + x = _x; + y = _y; + z = _z; + } } public struct Vec2 { public float x, y; + + public Vec2(float _x, float _y) { + x = _x; + y = _y; + } } + // TODO: Convert this to a packed struct, and change this[,] to use pointers public struct Matrix { public float v00, v01, v02, v03, v10, v11, v12, v13, v20, v21, v22, v23; + public void Identity() { + v00 = 1.0f; + v01 = 0.0f; + v02 = 0.0f; + v03 = 0.0f; + v10 = 0.0f; + v11 = 1.0f; + v12 = 0.0f; + v13 = 0.0f; + v20 = 0.0f; + v21 = 0.0f; + v22 = 1.0f; + v23 = 0.0f; + } + public float this[int x, int y] { get { switch (y) { diff --git a/NW4RTools/VertexSettings.cs b/NW4RTools/VertexSettings.cs index 2a0d3ac..4f05fd7 100644 --- a/NW4RTools/VertexSettings.cs +++ b/NW4RTools/VertexSettings.cs @@ -179,6 +179,23 @@ namespace NW4RTools { } + public void GetDesc(out UInt32 val1, out UInt32 val2) { + val1 = 0; + val1 |= (uint)(PNMatrixIndexExists ? 1 : 0); + + for (int i = 0; i < 8; i++) + val1 |= (TexCoordMatrixIndexExists[i] ? (uint)(2 << i) : 0); + + val1 |= ((uint)PositionDesc) << 9; + val1 |= ((uint)NormalDesc) << 11; + val1 |= ((uint)ColorDesc[0]) << 13; + val1 |= ((uint)ColorDesc[1]) << 15; + + val2 = 0; + for (int i = 0; i < 8; i++) + val2 |= ((uint)TexCoordDesc[i]) << (i * 2); + } + public void SetDesc(UInt32 val1, UInt32 val2) { PNMatrixIndexExists = ((val1 & 1) != 0); @@ -194,9 +211,69 @@ namespace NW4RTools { TexCoordDesc[i] = (DescType)((val2 & (3 << (i * 2))) >> (i * 2)); } + public void GetAttrFmt(out UInt32 val1, out UInt32 val2, out UInt32 val3) { + val1 = 0; + val2 = 0; + val3 = 0; + + val1 |= (uint)PositionCount; + val1 |= ((uint)PositionFormat) << 1; + val1 |= ((uint)PositionFrac) << 4; + if (PositionFrac > 0) + val1 |= 0x40000000; + + val1 |= ((uint)NormalFormat) << 10; + if (NormalCount == VertexSettings.CompCount.NBT || NormalCount == VertexSettings.CompCount.NBT3) + val1 |= 0x200; + if (NormalCount == VertexSettings.CompCount.NBT3) + val1 |= 0x80000000; + + val1 |= ((uint)ColorCount[0]) << 13; + val1 |= ((uint)ColorFormat[0]) << 14; + val1 |= ((uint)ColorCount[1]) << 17; + val1 |= ((uint)ColorFormat[1]) << 18; + + for (int i = 0; i < 8; i++) { + if (TexCoordFrac[i] > 0) + val1 |= 0x40000000; + } + + val1 |= ((uint)TexCoordCount[0]) << 21; + val1 |= ((uint)TexCoordFormat[0]) << 22; + val1 |= ((uint)TexCoordFrac[0]) << 25; + + val2 |= ((uint)TexCoordCount[1]); + val2 |= ((uint)TexCoordFormat[1]) << 1; + val2 |= ((uint)TexCoordFrac[1]) << 4; + + val2 |= ((uint)TexCoordCount[2]) << 9; + val2 |= ((uint)TexCoordFormat[2]) << 10; + val2 |= ((uint)TexCoordFrac[2]) << 13; + + val2 |= ((uint)TexCoordCount[3]) << 18; + val2 |= ((uint)TexCoordFormat[3]) << 19; + val2 |= ((uint)TexCoordFrac[3]) << 22; + + val2 |= ((uint)TexCoordCount[4]) << 27; + val2 |= ((uint)TexCoordFormat[4]) << 28; + val3 |= ((uint)TexCoordFrac[4]); + + val3 |= ((uint)TexCoordCount[5]) << 5; + val3 |= ((uint)TexCoordFormat[5]) << 6; + val3 |= ((uint)TexCoordFrac[5]) << 9; + + val3 |= ((uint)TexCoordCount[6]) << 14; + val3 |= ((uint)TexCoordFormat[6]) << 15; + val3 |= ((uint)TexCoordFrac[6]) << 18; + + val3 |= ((uint)TexCoordCount[7]) << 23; + val3 |= ((uint)TexCoordFormat[7]) << 24; + val3 |= ((uint)TexCoordFrac[7]) << 27; + } + public void SetAttrFmt(UInt32 val1, UInt32 val2, UInt32 val3) { PositionCount = (CompCount)(val1 & 1); - PositionFormat = (CompType)((val1 & 0xE) >> 3); + PositionFormat = (CompType)((val1 & 0xE) >> 1); PositionFrac = (byte)((val1 & 0x1F0) >> 4); // note: this field is special diff --git a/NW4RTools/bin/Debug/NW4RTools.dll b/NW4RTools/bin/Debug/NW4RTools.dll Binary files differindex 1204c1d..e4c2ea1 100755 --- a/NW4RTools/bin/Debug/NW4RTools.dll +++ b/NW4RTools/bin/Debug/NW4RTools.dll diff --git a/NW4RTools/bin/Debug/NW4RTools.dll.mdb b/NW4RTools/bin/Debug/NW4RTools.dll.mdb Binary files differindex badfeb7..62014b8 100644 --- a/NW4RTools/bin/Debug/NW4RTools.dll.mdb +++ b/NW4RTools/bin/Debug/NW4RTools.dll.mdb diff --git a/TestApp/Main.cs b/TestApp/Main.cs index b9a8f13..a5db889 100644 --- a/TestApp/Main.cs +++ b/TestApp/Main.cs @@ -11,7 +11,7 @@ namespace TestApp { public static void Main(string[] args) { string mdlPath = "/home/me/Games/Newer/ModelRev/"; - //string mdlName = "CS_W1"; + /*//string mdlName = "CS_W1"; //string mdlName = "bgB_4502"; //string mdlName = "cobKoopaCastle"; string mdlName = "waterPlate_W4boss"; @@ -22,7 +22,22 @@ namespace TestApp { byte[] file = File.ReadAllBytes(mdlPath + mdlName + ".brres"); ResFile rf = BrresReader.LoadFile(file); - File.WriteAllBytes(mdlPath + mdlName + "_rewritten.brres", BrresWriter.WriteFile(rf)); + File.WriteAllBytes(mdlPath + mdlName + "_rewritten.brres", BrresWriter.WriteFile(rf));*/ + + // Going to create a model! + ResFile rf = new ResFile(); + + ObjImporter.ImportModel(mdlPath, File.OpenText(mdlPath + "crapmap.obj"), rf, "CrapMap"); + + File.WriteAllBytes(mdlPath + "crapmap.brres", BrresWriter.WriteFile(rf)); + + + ResFile rf2 = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "crapmap.brres")); + using (var gw = new RenderWindow()) { + gw.Title = "crapmap"; + gw.SetModel(rf2, "CrapMap"); + gw.Run(1, 1); + } /*using (var gw = new RenderWindow()) { gw.Title = mdlName; diff --git a/TestApp/RenderWindow.cs b/TestApp/RenderWindow.cs index 3215190..2f29d0f 100644 --- a/TestApp/RenderWindow.cs +++ b/TestApp/RenderWindow.cs @@ -46,7 +46,7 @@ namespace TestApp { GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); //Matrix4 modelview = Matrix4.LookAt(new Vector3(1000, 600, 1000), new Vector3(1000, 0, 0), Vector3.UnitY); - Matrix4 modelview = Matrix4.LookAt(new Vector3(0, 0, 1000), new Vector3(0, 0, 0), Vector3.UnitY); + Matrix4 modelview = Matrix4.LookAt(new Vector3(0, 2, 4), new Vector3(0, 0, 0), Vector3.UnitY); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref modelview); diff --git a/TestApp/TestApp.pidb b/TestApp/TestApp.pidb Binary files differindex ad64e77..bbc694a 100644 --- a/TestApp/TestApp.pidb +++ b/TestApp/TestApp.pidb diff --git a/TestApp/bin/Debug/NW4RTools.dll b/TestApp/bin/Debug/NW4RTools.dll Binary files differindex aa8cfdd..e4c2ea1 100755 --- a/TestApp/bin/Debug/NW4RTools.dll +++ b/TestApp/bin/Debug/NW4RTools.dll diff --git a/TestApp/bin/Debug/NW4RTools.dll.mdb b/TestApp/bin/Debug/NW4RTools.dll.mdb Binary files differindex 02e5217..62014b8 100644 --- a/TestApp/bin/Debug/NW4RTools.dll.mdb +++ b/TestApp/bin/Debug/NW4RTools.dll.mdb diff --git a/TestApp/bin/Debug/TestApp.exe b/TestApp/bin/Debug/TestApp.exe Binary files differindex d0238aa..a5251b0 100755 --- a/TestApp/bin/Debug/TestApp.exe +++ b/TestApp/bin/Debug/TestApp.exe diff --git a/TestApp/bin/Debug/TestApp.exe.mdb b/TestApp/bin/Debug/TestApp.exe.mdb Binary files differindex 2998706..7508ce0 100644 --- a/TestApp/bin/Debug/TestApp.exe.mdb +++ b/TestApp/bin/Debug/TestApp.exe.mdb |