From c9bfbf84244c8aa4e26ece994ef6b93386c708e3 Mon Sep 17 00:00:00 2001 From: Treeki Date: Sun, 23 Sep 2012 17:43:33 +0200 Subject: tons of junk I'll clean up eventually --- NW4RTools/BrresReader.cs | 33 +- NW4RTools/BrresWriter.cs | 548 +++++++++++++++------------- NW4RTools/ColladaExporter.cs | 22 +- NW4RTools/Models/Animation/CharacterAnim.cs | 6 +- NW4RTools/Models/ByteCode.cs | 2 +- NW4RTools/ObjExporter.cs | 19 + NW4RTools/ObjImporter.cs | 53 +-- NW4RTools/Texture.cs | 6 +- TestApp/Main.cs | 312 +++++++++++++++- 9 files changed, 679 insertions(+), 322 deletions(-) diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs index 9616b5b..fbf2e2a 100755 --- a/NW4RTools/BrresReader.cs +++ b/NW4RTools/BrresReader.cs @@ -354,7 +354,7 @@ namespace NW4RTools { UInt16 kfCount = ins.ReadUInt16(); ins.Skip(2); - OffsetMap[kfPos] = String.Format("Element Keyframe Data for {0:X} [{1} frames]", savePos, kfCount); + OffsetMap[kfPos] = String.Format("Element Keyframe Data for {0:X} [{1} frames] format: {2}", savePos, kfCount, format); float invKeyFrameRange = ins.ReadFloat(); elem.Keyframes = new Keyframe[kfCount]; @@ -390,8 +390,8 @@ namespace NW4RTools { ins.Seek(savePos + 4); } else if ( - format == CharacterAnim.RotateFormatType.DataUnk4 || - format == CharacterAnim.RotateFormatType.DataUnk5) { + format == CharacterAnim.RotateFormatType.DataFrm8 || + format == CharacterAnim.RotateFormatType.DataFrm16) { // SOMETHING ELSE int savePos = ins.Position; ins.Seek(startPos + ins.ReadInt32()); @@ -401,14 +401,14 @@ namespace NW4RTools { elem.Multiplier = ins.ReadFloat(); elem.BaseValue = ins.ReadFloat(); - elem.Values = new int[CHR_ANIM_HACK.FrameCount]; + elem.Values = new int[CHR_ANIM_HACK.FrameCount + 1]; - for (int i = 0; i < CHR_ANIM_HACK.FrameCount; i++) { + for (int i = 0; i < (CHR_ANIM_HACK.FrameCount + 1); i++) { switch (format) { - case CharacterAnim.RotateFormatType.DataUnk4: + case CharacterAnim.RotateFormatType.DataFrm8: elem.Values[i] = ins.ReadByte(); break; - case CharacterAnim.RotateFormatType.DataUnk5: + case CharacterAnim.RotateFormatType.DataFrm16: elem.Values[i] = ins.ReadUInt16(); break; } @@ -417,18 +417,17 @@ namespace NW4RTools { ins.Seek(savePos + 4); } else if ( - format == CharacterAnim.RotateFormatType.DataUnk6) { + format == CharacterAnim.RotateFormatType.DataFrm32) { // WHAT THE FUCK int savePos = ins.Position; ins.Seek(startPos + ins.ReadInt32()); int kfPos = ins.Position; - OffsetMap[kfPos] = String.Format("Element Type 6 Frame Data for {0:X}", savePos); + OffsetMap[kfPos] = String.Format("Element Constant Frame Data for {0:X}", savePos); - elem.BaseValue = ins.ReadFloat(); - elem.FloatValues = new float[CHR_ANIM_HACK.FrameCount]; + elem.FloatValues = new float[CHR_ANIM_HACK.FrameCount + 1]; - for (int i = 0; i < CHR_ANIM_HACK.FrameCount; i++) { + for (int i = 0; i < (CHR_ANIM_HACK.FrameCount + 1); i++) { elem.FloatValues[i] = ins.ReadFloat(); } @@ -516,7 +515,7 @@ namespace NW4RTools { if (elem.IsConstant) { elem.ConstValue = ins.ReadColor(); - } else { + } else { int savePos = ins.Position; ins.Seek(savePos + ins.ReadInt32()); @@ -608,8 +607,8 @@ namespace NW4RTools { var elem = new TextureSRTAnim.Element(); int savePos = ins.Position; - ins.Seek(startPos + ins.ReadInt32()); - + ins.Seek(startPos + ins.ReadInt32()); + elem.Exists = true; OffsetMap.Add(ins.Position, String.Format("Element")); @@ -876,8 +875,8 @@ namespace NW4RTools { var insn4 = new ByteCode.DrawShapeInstruction(); insn4.MaterialID = ins.ReadUInt16(); insn4.ShapeID = ins.ReadUInt16(); - insn4.NodeID = ins.ReadUInt16(); - insn4.Unk = ins.ReadByte(); + insn4.NodeID = ins.ReadUInt16(); + insn4.Unk = ins.ReadByte(); bc.Instructions.Add(insn4); break; diff --git a/NW4RTools/BrresWriter.cs b/NW4RTools/BrresWriter.cs index 033007e..0b81a65 100755 --- a/NW4RTools/BrresWriter.cs +++ b/NW4RTools/BrresWriter.cs @@ -80,12 +80,12 @@ namespace NW4RTools { public int PairingLookupByTexture; public int BlockSize; // Duplicate shader structs are removed from the model, so they require special tracking - public List UniqueShaders; - public Dictionary MaterialSizes; - - public ModelCalcInfo() { - UniqueShaders = new List(); - MaterialSizes = new Dictionary(); + public List UniqueShaders; + public Dictionary MaterialSizes; + + public ModelCalcInfo() { + UniqueShaders = new List(); + MaterialSizes = new Dictionary(); } } private Dictionary ModelCalcInfos; @@ -133,11 +133,11 @@ namespace NW4RTools { #region Texture SRT Animation Data Storage private class TexSRTAnmNodeInfo { public int[] TextureOffsets; - public int[] IndirectTextureOffsets; - - public TexSRTAnmNodeInfo() { - TextureOffsets = new int[8]; - IndirectTextureOffsets = new int[3]; + public int[] IndirectTextureOffsets; + + public TexSRTAnmNodeInfo() { + TextureOffsets = new int[8]; + IndirectTextureOffsets = new int[3]; } } @@ -478,8 +478,8 @@ namespace NW4RTools { MaterialDLOffsets[kv.Value] = CurrentPos; CurrentPos += 0x20 + 0x80 + 0x40 + 0xA0; - // Sized! - int size = CurrentPos - MaterialOffsets[kv.Value]; + // Sized! + int size = CurrentPos - MaterialOffsets[kv.Value]; ModelCalcInfos[m].MaterialSizes[kv.Value] = size; } } @@ -830,14 +830,14 @@ namespace NW4RTools { case CharacterAnim.RotateFormatType.Data96: CurrentPos += 8 + (kf.Keyframes.Length * 12); break; - case CharacterAnim.RotateFormatType.DataUnk4: + case CharacterAnim.RotateFormatType.DataFrm8: CurrentPos += 8 + (kf.Values.Length); break; - case CharacterAnim.RotateFormatType.DataUnk5: + case CharacterAnim.RotateFormatType.DataFrm16: CurrentPos += 8 + (kf.Values.Length * 2); break; - case CharacterAnim.RotateFormatType.DataUnk6: - CurrentPos += 4 + (kf.FloatValues.Length * 4); + case CharacterAnim.RotateFormatType.DataFrm32: + CurrentPos += (kf.FloatValues.Length * 4); break; default: throw new NotImplementedException(); @@ -942,8 +942,8 @@ namespace NW4RTools { CurrentPos += GetSizeForResDict(anim.Nodes.Count); var anminfo = new TexSRTAnmInfo(); - TexSRTAnmInfos.Add(anim, anminfo); - + TexSRTAnmInfos.Add(anim, anminfo); + // Now size each node foreach (var nodepair in anim.Nodes) { AddString(nodepair.Key); @@ -954,29 +954,29 @@ namespace NW4RTools { LogPosition("TextureSRT Anim Node: " + nodepair.Key); CurrentPos += 12; - var nodeinfo = new TexSRTAnmNodeInfo(); - anminfo.NodeInfo.Add(node, nodeinfo); - - // Figure out what anims we're using - for (int i = 0; i < 8; i++) - if (node.Textures[i].Exists) - CurrentPos += 4; - for (int i = 0; i < 3; i++) - if (node.IndirectTextures[i].Exists) - CurrentPos += 4; - - for (int i = 0; i < 8; i++) { - if (node.Textures[i].Exists) { - nodeinfo.TextureOffsets[i] = CurrentPos; - CalculateAnmTexSrtElement(anminfo.KFInfo, node.Textures[i]); - } - } - - for (int i = 0; i < 3; i++) { - if (node.IndirectTextures[i].Exists) { - nodeinfo.IndirectTextureOffsets[i] = CurrentPos; - CalculateAnmTexSrtElement(anminfo.KFInfo, node.IndirectTextures[i]); - } + var nodeinfo = new TexSRTAnmNodeInfo(); + anminfo.NodeInfo.Add(node, nodeinfo); + + // Figure out what anims we're using + for (int i = 0; i < 8; i++) + if (node.Textures[i].Exists) + CurrentPos += 4; + for (int i = 0; i < 3; i++) + if (node.IndirectTextures[i].Exists) + CurrentPos += 4; + + for (int i = 0; i < 8; i++) { + if (node.Textures[i].Exists) { + nodeinfo.TextureOffsets[i] = CurrentPos; + CalculateAnmTexSrtElement(anminfo.KFInfo, node.Textures[i]); + } + } + + for (int i = 0; i < 3; i++) { + if (node.IndirectTextures[i].Exists) { + nodeinfo.IndirectTextureOffsets[i] = CurrentPos; + CalculateAnmTexSrtElement(anminfo.KFInfo, node.IndirectTextures[i]); + } } } @@ -984,51 +984,51 @@ namespace NW4RTools { anminfo.BlockSize = CurrentPos - TexSRTAnmOffsets[anim]; } - } - - private void CalculateAnmTexSrtElement(KeyframeWriterInfo kfinfo, TextureSRTAnim.Element elem) { - CurrentPos += 4; - - if (!(elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1))) { - // Scale is not all 1 - kfinfo.Add(elem.ScaleS); - CurrentPos += 4; - - if (elem.ScaleT != elem.ScaleS) { - // Not uniform - kfinfo.Add(elem.ScaleT); - CurrentPos += 4; - } - } - - if (!elem.Rotate.IsConstWith(0)) { - // Rotate is not zero - kfinfo.Add(elem.Rotate); - CurrentPos += 4; - } - - if (!(elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0))) { - // Translate is not all zero - kfinfo.Add(elem.TransS); - kfinfo.Add(elem.TransT); - CurrentPos += 8; - } } - #endregion - - #region Keyframe Animation Calculation - private void CalculateKeyframeData(KeyframeWriterInfo kfinfo) { - foreach (var pair in kfinfo.KeyframeAnims) { - var anim = pair.two; - - kfinfo.KeyframeAnimOffsets[pair] = CurrentPos; - - CurrentPos += 8 + (anim.Keyframes.Length * 12); - } - } - #endregion - - + + private void CalculateAnmTexSrtElement(KeyframeWriterInfo kfinfo, TextureSRTAnim.Element elem) { + CurrentPos += 4; + + if (!(elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1))) { + // Scale is not all 1 + kfinfo.Add(elem.ScaleS); + CurrentPos += 4; + + if (elem.ScaleT != elem.ScaleS) { + // Not uniform + kfinfo.Add(elem.ScaleT); + CurrentPos += 4; + } + } + + if (!elem.Rotate.IsConstWith(0)) { + // Rotate is not zero + kfinfo.Add(elem.Rotate); + CurrentPos += 4; + } + + if (!(elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0))) { + // Translate is not all zero + kfinfo.Add(elem.TransS); + kfinfo.Add(elem.TransT); + CurrentPos += 8; + } + } + #endregion + + #region Keyframe Animation Calculation + private void CalculateKeyframeData(KeyframeWriterInfo kfinfo) { + foreach (var pair in kfinfo.KeyframeAnims) { + var anim = pair.two; + + kfinfo.KeyframeAnimOffsets[pair] = CurrentPos; + + CurrentPos += 8 + (anim.Keyframes.Length * 12); + } + } + #endregion + + private void AlignCalcPos(int alignTo) { if ((CurrentPos & (alignTo - 1)) == 0) return; @@ -1277,7 +1277,7 @@ namespace NW4RTools { var op4 = insn as ByteCode.DrawShapeInstruction; Output.WriteUInt16(op4.MaterialID); Output.WriteUInt16(op4.ShapeID); - Output.WriteUInt16(op4.NodeID); + Output.WriteUInt16(op4.NodeID); Output.WriteByte(op4.Unk); break; @@ -1346,7 +1346,7 @@ namespace NW4RTools { Material mat = kv.Value; int startPos = Output.Position; - // size, model offset, name offset, index + // size, model offset, name offset, index var modelInfo = ModelCalcInfos[m]; Output.WriteUInt32((UInt32)modelInfo.MaterialSizes[mat]); Output.WriteInt32(ModelOffsets[m] - startPos); @@ -1907,6 +1907,19 @@ namespace NW4RTools { Output.WriteFloat(invKeyFrameRange); } + if (format == CharacterAnim.RotateFormatType.DataFrm8 || + format == CharacterAnim.RotateFormatType.DataFrm16) { + // Header for CV 8 and 16 + Output.WriteFloat(kf.Multiplier); + Output.WriteFloat(kf.BaseValue); + Misc.Assert(kf.Values.Length == (anm.FrameCount + 1)); + } + + if (format == CharacterAnim.RotateFormatType.DataFrm32) { + // Assert this + Misc.Assert(kf.FloatValues.Length == (anm.FrameCount + 1)); + } + switch (format) { case CharacterAnim.RotateFormatType.Data32: for (int i = 0; i < kf.Keyframes.Length; i++) { @@ -1930,8 +1943,23 @@ namespace NW4RTools { Output.WriteFloat(kf.Keyframes[i].Slope); } break; + case CharacterAnim.RotateFormatType.DataFrm8: + for (int i = 0; i < (anm.FrameCount + 1); i++) { + Output.WriteByte((byte)kf.Values[i]); + } + break; + case CharacterAnim.RotateFormatType.DataFrm16: + for (int i = 0; i < (anm.FrameCount + 1); i++) { + Output.WriteUInt16((ushort)kf.Values[i]); + } + break; + case CharacterAnim.RotateFormatType.DataFrm32: + for (int i = 0; i < (anm.FrameCount + 1); i++) { + Output.WriteFloat(kf.FloatValues[i]); + } + break; default: - throw new NotImplementedException(); + throw new NotImplementedException(String.Format("Unknown format: {0}", format)); } Output.AlignTo(4); @@ -1999,7 +2027,7 @@ namespace NW4RTools { Output.WriteUInt32(flags); for (int i = 0; i < node.Elements.Length; i++) { - if (node.Elements[i].Exists) { + if (node.Elements[i].Exists) { Output.WriteUInt32(node.Elements[i].Mask); if (node.Elements[i].IsConstant) { Output.WriteColor(node.Elements[i].ConstValue); @@ -2041,176 +2069,176 @@ namespace NW4RTools { i++; } } - #endregion - - #region Texture SRT Animation Writing - private void WriteAnmTexSrt() { - foreach (var kv in File.GetGroup("AnmTexSrt(NW4R)")) { - using (var c = Debug.Push("TextureSRT Animation: {0}", kv.Key)) { - Output.AlignTo(0x20); - - int startPos = Output.Position; - TextureSRTAnim anm = kv.Value; - var info = TexSRTAnmInfos[anm]; - - Misc.Assert(startPos == TexSRTAnmOffsets[anm]); - - // Base struct: magic 'SRT0', block size, version[currently 5], resfile offset - Output.WriteUInt32(0x53525430); - Output.WriteUInt32((UInt32)info.BlockSize); - Output.WriteUInt32(5); - Output.WriteInt32(-startPos); - - // Data offset, name offset, other crap - Output.WriteInt32(0x2C); - Output.WriteUInt32(0); - Output.WriteInt32(StringPositions[kv.Key] - startPos); - Output.WriteUInt32(0); - - Output.WriteUInt16((UInt16)anm.FrameCount); - Output.WriteUInt16((UInt16)anm.Nodes.Count); - - Output.WriteUInt32((UInt32)anm.MatrixMode); - Output.WriteUInt32(anm.Loop ? 1U : 0U); - - WriteResDict(anm.Nodes, info.NodeOffsets); - - WriteAnmTexSrtNodes(anm); - WriteKeyframeData(info.KFInfo); - } - } - } - - private void WriteAnmTexSrtNodes(TextureSRTAnim anm) { - var info = TexSRTAnmInfos[anm]; - - foreach (var kv in anm.Nodes) { - using (var c = Debug.Push("Element: {0}", kv.Key)) { - int startPos = Output.Position; - TextureSRTAnim.Node node = kv.Value; - TexSRTAnmNodeInfo nodeinfo = info.NodeInfo[node]; - - Misc.Assert(startPos == info.NodeOffsets[node]); - - Output.WriteInt32(StringPositions[kv.Key] - startPos); - - UInt32 flags = 0, indFlags = 0; - - for (int i = 0; i < 8; i++) - if (node.Textures[i].Exists) - flags |= (UInt32)(1 << i); - for (int i = 0; i < 3; i++) - if (node.IndirectTextures[i].Exists) - indFlags |= (UInt32)(1 << i); - - Output.WriteUInt32(flags); - Output.WriteUInt32(indFlags); - - var elemsToAddLater = new List(); - - for (int i = 0; i < 8; i++) - if (node.Textures[i].Exists) { - Output.WriteInt32(nodeinfo.TextureOffsets[i] - startPos); - elemsToAddLater.Add(node.Textures[i]); - } - - for (int i = 0; i < 3; i++) - if (node.IndirectTextures[i].Exists) { - Output.WriteInt32(nodeinfo.IndirectTextureOffsets[i] - startPos); - elemsToAddLater.Add(node.IndirectTextures[i]); - } - - foreach (var elem in elemsToAddLater) - WriteAnmTexSrtElement(elem, info.KFInfo); - } - } - } - - private void WriteAnmTexSrtElement(TextureSRTAnim.Element elem, KeyframeWriterInfo kfinfo) { - UInt32 flags = 1; - - int flagPos = Output.Position; - Output.WriteUInt32(0); // placeholder! - - if (elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1)) { - flags |= 2 | 0x10 | 0x20 | 0x40; - } else { - WriteKeyframeAnim(elem.ScaleS, kfinfo); - if (elem.ScaleS.IsConstant) - flags |= 0x20; - - if (elem.ScaleS == elem.ScaleT) { - flags |= 0x10; - if (elem.ScaleS.IsConstant) - flags |= 0x40; - } else { - WriteKeyframeAnim(elem.ScaleT, kfinfo); - if (elem.ScaleT.IsConstant) - flags |= 0x40; - } - } - - if (elem.Rotate.IsConstWith(0)) { - flags |= 4 | 0x80; - } else { - WriteKeyframeAnim(elem.Rotate, kfinfo); - if (elem.Rotate.IsConstant) - flags |= 0x80; - } - - if (elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0)) { - flags |= 8 | 0x100 | 0x200; - } else { - WriteKeyframeAnim(elem.TransS, kfinfo); - WriteKeyframeAnim(elem.TransT, kfinfo); - - if (elem.TransS.IsConstant) - flags |= 0x100; - if (elem.TransT.IsConstant) - flags |= 0x200; - } - - // Replace the flags - int savePos = Output.Position; - Output.Seek(flagPos); - Output.WriteUInt32(flags); - Output.Seek(savePos); - } - #endregion - - #region Keyframe Data Writing - private void WriteKeyframeAnim(KeyframeAnim anim, KeyframeWriterInfo kfinfo) { - if (anim.IsConstant) - Output.WriteFloat(anim.ConstValue); - else - Output.WriteInt32(kfinfo.GetPosition(anim) - Output.Position); - } - - private void WriteKeyframeData(KeyframeWriterInfo kfinfo) { - foreach (var pair in kfinfo.KeyframeAnims) { - var anim = pair.two; - var kf = pair.two; - var expectedOffset = kfinfo.KeyframeAnimOffsets[pair]; - - Misc.Assert(expectedOffset == Output.Position); - - Output.WriteUInt16((UInt16)kf.Keyframes.Length); - Output.AddPadding(2); - - float delta = kf.Keyframes[kf.Keyframes.Length - 1].Frame - kf.Keyframes[0].Frame; - float invKeyFrameRange = (1.0f / delta); - Output.WriteFloat(invKeyFrameRange); - - for (int i = 0; i < kf.Keyframes.Length; i++) { - Output.WriteFloat(kf.Keyframes[i].Frame); - Output.WriteFloat(kf.Keyframes[i].Value); - Output.WriteFloat(kf.Keyframes[i].Slope); - } - } - } - #endregion - - #region ResDicts + #endregion + + #region Texture SRT Animation Writing + private void WriteAnmTexSrt() { + foreach (var kv in File.GetGroup("AnmTexSrt(NW4R)")) { + using (var c = Debug.Push("TextureSRT Animation: {0}", kv.Key)) { + Output.AlignTo(0x20); + + int startPos = Output.Position; + TextureSRTAnim anm = kv.Value; + var info = TexSRTAnmInfos[anm]; + + Misc.Assert(startPos == TexSRTAnmOffsets[anm]); + + // Base struct: magic 'SRT0', block size, version[currently 5], resfile offset + Output.WriteUInt32(0x53525430); + Output.WriteUInt32((UInt32)info.BlockSize); + Output.WriteUInt32(5); + Output.WriteInt32(-startPos); + + // Data offset, name offset, other crap + Output.WriteInt32(0x2C); + Output.WriteUInt32(0); + Output.WriteInt32(StringPositions[kv.Key] - startPos); + Output.WriteUInt32(0); + + Output.WriteUInt16((UInt16)anm.FrameCount); + Output.WriteUInt16((UInt16)anm.Nodes.Count); + + Output.WriteUInt32((UInt32)anm.MatrixMode); + Output.WriteUInt32(anm.Loop ? 1U : 0U); + + WriteResDict(anm.Nodes, info.NodeOffsets); + + WriteAnmTexSrtNodes(anm); + WriteKeyframeData(info.KFInfo); + } + } + } + + private void WriteAnmTexSrtNodes(TextureSRTAnim anm) { + var info = TexSRTAnmInfos[anm]; + + foreach (var kv in anm.Nodes) { + using (var c = Debug.Push("Element: {0}", kv.Key)) { + int startPos = Output.Position; + TextureSRTAnim.Node node = kv.Value; + TexSRTAnmNodeInfo nodeinfo = info.NodeInfo[node]; + + Misc.Assert(startPos == info.NodeOffsets[node]); + + Output.WriteInt32(StringPositions[kv.Key] - startPos); + + UInt32 flags = 0, indFlags = 0; + + for (int i = 0; i < 8; i++) + if (node.Textures[i].Exists) + flags |= (UInt32)(1 << i); + for (int i = 0; i < 3; i++) + if (node.IndirectTextures[i].Exists) + indFlags |= (UInt32)(1 << i); + + Output.WriteUInt32(flags); + Output.WriteUInt32(indFlags); + + var elemsToAddLater = new List(); + + for (int i = 0; i < 8; i++) + if (node.Textures[i].Exists) { + Output.WriteInt32(nodeinfo.TextureOffsets[i] - startPos); + elemsToAddLater.Add(node.Textures[i]); + } + + for (int i = 0; i < 3; i++) + if (node.IndirectTextures[i].Exists) { + Output.WriteInt32(nodeinfo.IndirectTextureOffsets[i] - startPos); + elemsToAddLater.Add(node.IndirectTextures[i]); + } + + foreach (var elem in elemsToAddLater) + WriteAnmTexSrtElement(elem, info.KFInfo); + } + } + } + + private void WriteAnmTexSrtElement(TextureSRTAnim.Element elem, KeyframeWriterInfo kfinfo) { + UInt32 flags = 1; + + int flagPos = Output.Position; + Output.WriteUInt32(0); // placeholder! + + if (elem.ScaleS.IsConstWith(1) && elem.ScaleT.IsConstWith(1)) { + flags |= 2 | 0x10 | 0x20 | 0x40; + } else { + WriteKeyframeAnim(elem.ScaleS, kfinfo); + if (elem.ScaleS.IsConstant) + flags |= 0x20; + + if (elem.ScaleS == elem.ScaleT) { + flags |= 0x10; + if (elem.ScaleS.IsConstant) + flags |= 0x40; + } else { + WriteKeyframeAnim(elem.ScaleT, kfinfo); + if (elem.ScaleT.IsConstant) + flags |= 0x40; + } + } + + if (elem.Rotate.IsConstWith(0)) { + flags |= 4 | 0x80; + } else { + WriteKeyframeAnim(elem.Rotate, kfinfo); + if (elem.Rotate.IsConstant) + flags |= 0x80; + } + + if (elem.TransS.IsConstWith(0) && elem.TransT.IsConstWith(0)) { + flags |= 8 | 0x100 | 0x200; + } else { + WriteKeyframeAnim(elem.TransS, kfinfo); + WriteKeyframeAnim(elem.TransT, kfinfo); + + if (elem.TransS.IsConstant) + flags |= 0x100; + if (elem.TransT.IsConstant) + flags |= 0x200; + } + + // Replace the flags + int savePos = Output.Position; + Output.Seek(flagPos); + Output.WriteUInt32(flags); + Output.Seek(savePos); + } + #endregion + + #region Keyframe Data Writing + private void WriteKeyframeAnim(KeyframeAnim anim, KeyframeWriterInfo kfinfo) { + if (anim.IsConstant) + Output.WriteFloat(anim.ConstValue); + else + Output.WriteInt32(kfinfo.GetPosition(anim) - Output.Position); + } + + private void WriteKeyframeData(KeyframeWriterInfo kfinfo) { + foreach (var pair in kfinfo.KeyframeAnims) { + var anim = pair.two; + var kf = pair.two; + var expectedOffset = kfinfo.KeyframeAnimOffsets[pair]; + + Misc.Assert(expectedOffset == Output.Position); + + Output.WriteUInt16((UInt16)kf.Keyframes.Length); + Output.AddPadding(2); + + float delta = kf.Keyframes[kf.Keyframes.Length - 1].Frame - kf.Keyframes[0].Frame; + float invKeyFrameRange = (1.0f / delta); + Output.WriteFloat(invKeyFrameRange); + + for (int i = 0; i < kf.Keyframes.Length; i++) { + Output.WriteFloat(kf.Keyframes[i].Frame); + Output.WriteFloat(kf.Keyframes[i].Value); + Output.WriteFloat(kf.Keyframes[i].Slope); + } + } + } + #endregion + + #region ResDicts private struct RawDictEntry { public ushort Ref; public ushort Unk; diff --git a/NW4RTools/ColladaExporter.cs b/NW4RTools/ColladaExporter.cs index 645b8dc..8ee111e 100644 --- a/NW4RTools/ColladaExporter.cs +++ b/NW4RTools/ColladaExporter.cs @@ -643,6 +643,7 @@ namespace NW4RTools { List triStrips = new List(); List lineStrips = new List(); + File.WriteAllBytes("DL.bin", shape.DisplayList2); // Now go through the display list while (true) { @@ -651,7 +652,26 @@ namespace NW4RTools { byte cmd = dl.ReadByte(); if (cmd == 0) - break; + continue; + + // These can contain other stuff. Who knew. + + if ((cmd & 0x80) == 0) { + GXCommand castCmd = (GXCommand)cmd; + + switch (castCmd) { + case GXCommand.LoadPosMtxFromArray: + case GXCommand.LoadNrmMtxFromArray: + case GXCommand.LoadTexCoordMtxFromArray: + case GXCommand.LoadLightFromArray: + dl.Skip(4); + break; + default: + Console.WriteLine("UNIMPLEMENTED GX COMMAND: {0}", castCmd); + break; + } + continue; // don't process it as a primitive + } PrimitiveType prim = (PrimitiveType)((cmd >> 3) & 7); int vtxCount = dl.ReadUInt16(); diff --git a/NW4RTools/Models/Animation/CharacterAnim.cs b/NW4RTools/Models/Animation/CharacterAnim.cs index 72efd56..2fab9f9 100644 --- a/NW4RTools/Models/Animation/CharacterAnim.cs +++ b/NW4RTools/Models/Animation/CharacterAnim.cs @@ -58,9 +58,9 @@ namespace NW4RTools.Models.Animation { Data32 = 1, Data48 = 2, Data96 = 3, - DataUnk4 = 4, - DataUnk5 = 5, - DataUnk6 = 6, + DataFrm8 = 4, + DataFrm16 = 5, + DataFrm32 = 6, Zero = 7, } diff --git a/NW4RTools/Models/ByteCode.cs b/NW4RTools/Models/ByteCode.cs index 62968e9..ed16d10 100755 --- a/NW4RTools/Models/ByteCode.cs +++ b/NW4RTools/Models/ByteCode.cs @@ -50,7 +50,7 @@ namespace NW4RTools.Models { public UInt16 MaterialID; public UInt16 ShapeID; - public UInt16 NodeID; + public UInt16 NodeID; public byte Unk; } diff --git a/NW4RTools/ObjExporter.cs b/NW4RTools/ObjExporter.cs index 7b1afef..8cb9088 100644 --- a/NW4RTools/ObjExporter.cs +++ b/NW4RTools/ObjExporter.cs @@ -138,6 +138,25 @@ namespace NW4RTools { if (cmd == 0) break; + // These can contain other stuff. Who knew. + + if ((cmd & 0x80) == 0) { + GXCommand castCmd = (GXCommand)cmd; + + switch (castCmd) { + case GXCommand.LoadPosMtxFromArray: + case GXCommand.LoadNrmMtxFromArray: + case GXCommand.LoadTexCoordMtxFromArray: + case GXCommand.LoadLightFromArray: + dl.Skip(4); + break; + default: + Console.WriteLine("UNIMPLEMENTED GX COMMAND: {0}", castCmd); + break; + } + continue; // don't process it as a primitive + } + PrimitiveType prim = (PrimitiveType)((cmd >> 3) & 7); int vtxCount = dl.ReadUInt16(); Output.WriteLine("# Primitive: {0} ({1} vertices)", prim, vtxCount); diff --git a/NW4RTools/ObjImporter.cs b/NW4RTools/ObjImporter.cs index 384fb66..cdc9fcd 100755 --- a/NW4RTools/ObjImporter.cs +++ b/NW4RTools/ObjImporter.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Collections; using System.Collections.Generic; +using System.Globalization; using NW4RTools.Models; namespace NW4RTools { @@ -271,7 +272,7 @@ namespace NW4RTools { var output = new float[count]; for (int i = 0; i < count; i++) { - output[i] = float.Parse(src[i + index]); + output[i] = float.Parse(src[i + index], CultureInfo.InvariantCulture); } return output; @@ -293,13 +294,13 @@ namespace NW4RTools { if (line.Length == 0 || line[0] == '#') continue; - var parsed = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - - switch (parsed[0]) { - case "newmtl": - // Create a new material and initialise everything - // Default settings taken from Test_Lift material - currentMaterial = new Material(); + var parsed = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + switch (parsed[0]) { + case "newmtl": + // Create a new material and initialise everything + // Default settings taken from Test_Lift material + currentMaterial = new Material(); currentMaterial.ChanCtrls[0] = new ChanCtrl(); currentMaterial.ChanCtrls[0].Flags = 0x3F; @@ -315,20 +316,20 @@ namespace NW4RTools { currentMaterial.ChanCtrls[1].FlagC = 0; currentMaterial.ChanCtrls[1].FlagA = 0; - if (Lightmap == ObjImporter.LightmapType.None) { - currentMaterial.TexCoordGenCount = 1; - currentMaterial.ChanCount = 1; - currentMaterial.TevStageCount = 2; - currentMaterial.IndStageCount = 0; - - // This might need changing - currentMaterial.CullMode = 2; + if (Lightmap == ObjImporter.LightmapType.None) { + currentMaterial.TexCoordGenCount = 1; + currentMaterial.ChanCount = 1; + currentMaterial.TevStageCount = 2; + currentMaterial.IndStageCount = 0; + + // This might need changing + currentMaterial.CullMode = 2; currentMaterial.LightSetID = 1; - currentMaterial.SRTSettings[0] = new SRTSettingInfo(); - - // Default display lists, taken from test_lift + currentMaterial.SRTSettings[0] = new SRTSettingInfo(); + + // Default display lists, taken from test_lift var pixDL = new DisplayListWriter(); pixDL.LoadBPReg(0xF31EFF80); pixDL.LoadBPReg(0x40000017); @@ -469,10 +470,10 @@ namespace NW4RTools { Shader thisShader = CreateShader(); CurrentModel.Shaders.Add(parsed[1], thisShader); - currentMaterial.ShaderRef = thisShader; - - break; - + currentMaterial.ShaderRef = thisShader; + + break; + case "map_Kd": var rawTexName = string.Join(" ", parsed, 1, parsed.Length - 1); // TODO: fix this to use the mtllib path @@ -512,9 +513,9 @@ namespace NW4RTools { currentMaterial.TextureInfos.Add(lm01); currentMaterial.TextureInfos.Add(lm02); } - - break; - + + break; + } } diff --git a/NW4RTools/Texture.cs b/NW4RTools/Texture.cs index f2cf231..249af79 100644 --- a/NW4RTools/Texture.cs +++ b/NW4RTools/Texture.cs @@ -437,8 +437,10 @@ namespace NW4RTools { byte val = data.ReadByte(); for (int inX = 0; inX < 4; inX++) { - uint *pPixel = (uint*)pRow + x + (iBlockX * 4) + inX; - *pPixel = clrArray[(val >> 6) & 3]; + if ((y + (iBlockY * 4) + inY) < height && (x + (iBlockX * 4) + inX) < width) { + uint *pPixel = (uint*)pRow + x + (iBlockX * 4) + inX; + *pPixel = clrArray[(val >> 6) & 3]; + } val <<= 2; } } diff --git a/TestApp/Main.cs b/TestApp/Main.cs index a61577f..76329b7 100755 --- a/TestApp/Main.cs +++ b/TestApp/Main.cs @@ -2,6 +2,8 @@ using System; using System.IO; using System.Collections.Generic; using NW4RTools; +using NW4RTools.Models; +using NW4RTools.Models.Animation; using OpenTK; using OpenTK.Graphics; using OpenTK.Graphics.OpenGL; @@ -9,6 +11,21 @@ using OpenTK.Graphics.OpenGL; namespace TestApp { class MainClass { public static void Main(string[] args) { + ResFile shits = BrresReader.LoadFile(File.ReadAllBytes("Z:/stuff/Games/Newer/bros.brres")); + ColladaExporter.WriteModel(File.OpenWrite("Z:/stuff/Games/Newer/bros.dae"), shits, "bros"); + //ResFile shits = BrresReader.LoadFile(File.ReadAllBytes("/home/me/Games/Newer/bros.brres")); + //ColladaExporter.WriteModel(File.OpenWrite("/home/me/Games/Newer/bros.dae"), shits, "bros"); + return; + + striphammer(); + return; + + fuckskawo(); + return; + + ewater(); + return; + blah(args); return; @@ -23,13 +40,13 @@ namespace TestApp { //string filename = "crapmap", resmdlname = "CrapMap"; /*string filename = "SMGoldwood"; - ResFile rf = new ResFile(); + ResFile rf = new ResFile(); - ObjImporter.ImportModel(mdlPath, File.OpenText(mdlPath + "GoldwoodBase.obj"), rf, "GoldwoodBase", ObjImporter.LightmapType.Map); - ObjImporter.ImportModel(mdlPath, File.OpenText(mdlPath + "BrownTree.obj"), rf, "BrownTree", ObjImporter.LightmapType.MapObj); - ObjImporter.ImportModel(mdlPath, File.OpenText(mdlPath + "RedTree.obj"), rf, "RedTree", ObjImporter.LightmapType.MapObj); + ObjImporter.ImportModel(mdlPath, File.OpenText(mdlPath + "GoldwoodBase.obj"), rf, "GoldwoodBase", ObjImporter.LightmapType.Map); + ObjImporter.ImportModel(mdlPath, File.OpenText(mdlPath + "BrownTree.obj"), rf, "BrownTree", ObjImporter.LightmapType.MapObj); + ObjImporter.ImportModel(mdlPath, File.OpenText(mdlPath + "RedTree.obj"), rf, "RedTree", ObjImporter.LightmapType.MapObj); - File.WriteAllBytes(mdlPath + "SMGoldwood.brres", BrresWriter.WriteFile(rf));*/ + File.WriteAllBytes(mdlPath + "SMGoldwood.brres", BrresWriter.WriteFile(rf));*/ string filename = "DemoMap"; @@ -58,7 +75,7 @@ namespace TestApp { /*var texs = rf.GetGroup("Textures(NW4R)"); // wtf C#?! foreach (var kv in (IEnumerable>)texs) { - kv.Value.BaseImage.Save(mdlPath + "images/" + kv.Key + ".png"); + kv.Value.BaseImage.Save(mdlPath + "images/" + kv.Key + ".png"); } //var objFile = File.Open(mdlPath + mdlName + ".obj", FileMode.OpenOrCreate); @@ -69,6 +86,38 @@ namespace TestApp { objFile.Close();*/ } + private static void striphammer() { + string mdlPath = "/home/me/Games/Newer/ISO/files/Object/hammerM/g3d/"; + + ResFile rf = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "bros.brres")); + + var mgrp = rf.GetModelGroup(); + var mremove = new List(); + foreach (var k in mgrp.Keys) + if (k != "bros_hammer") + mremove.Add(k); + foreach (var k in mremove) + mgrp.Remove(k); + + var tgrp = rf.GetTextureGroup(); + var texremove = new List(); + var texkeep = new Dictionary(); + texkeep.Add("bros_hammer", true); + texkeep.Add("bros_hammer_env", true); + texkeep.Add("bros_hammer_handle", true); + texkeep.Add("lm_01e", true); + texkeep.Add("lm_02e", true); + foreach (var k in tgrp.Keys) + if (!texkeep.ContainsKey(k)) + texremove.Add(k); + foreach (var k in texremove) + tgrp.Remove(k); + + rf.Remove("AnmChr(NW4R)"); + + File.WriteAllBytes(mdlPath + "hammer.brres", BrresWriter.WriteFile(rf)); + + } private static void skawo() { string mdlPath = "/home/me/Games/Newer/ModelRev/"; @@ -113,7 +162,7 @@ namespace TestApp { /*var texs = rf.GetGroup("Textures(NW4R)"); // wtf C#?! foreach (var kv in (IEnumerable>)texs) { - kv.Value.Images[0].Save(mdlPath + "images/" + kv.Key + ".png"); + kv.Value.Images[0].Save(mdlPath + "images/" + kv.Key + ".png"); }*/ //var objFile = File.Open(mdlPath + "lift_han_wood_M.dae", FileMode.OpenOrCreate); @@ -123,11 +172,7 @@ namespace TestApp { private static void blah(string[] args) { - string mdlPath; - if (Directory.Exists("/home/me")) - mdlPath = "/home/me/Games/Newer/WaterL/"; - else - mdlPath = "Z:/stuff/Games/Newer/WaterL/"; + string mdlPath = getPath(); ResFile rf = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "CS_W4.brres")); File.WriteAllBytes(mdlPath + "CS_W4a.brres", BrresWriter.WriteFile(rf)); @@ -143,6 +188,249 @@ namespace TestApp { File.WriteAllBytes(mdlPath + "CS_W4e.brres", BrresWriter.WriteFile(rf5)); } } + + private static void ewater() { + string mdlPath = getPath(); + + ResFile rf = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "CS_W4.brres")); + Model m = rf.GetModelGroup()["CS_W4"]; + + foreach (var kv in m.Shapes) { + var s = kv.Value; + Console.WriteLine("{0} : {1}", + kv.Key, + m.VtxPosData.GetKeyForValue(s.PosData)); + } + + var tgrp = rf.GetTextureGroup(); + var texremove = new List(); + var texkeep = new Dictionary(); + texkeep.Add("W3a_water01", true); + texkeep.Add("water_wave1mm", true); + texkeep.Add("water01", true); + texkeep.Add("texMask", true); + foreach (var k in tgrp.Keys) + if (!texkeep.ContainsKey(k)) + texremove.Add(k); + foreach (var k in texremove) + tgrp.Remove(k); + + rf.Remove("AnmClr(NW4R)"); + rf.Remove("AnmChr(NW4R)"); + + var tsanmg = rf.GetGroup("AnmTexSrt(NW4R)"); + var tsanm = tsanmg["CS_W4"]; + tsanmg.RemoveAt(0); + tsanmg.Add("KpBG", tsanm); + + tsanm.Nodes.Remove("mtCloudA"); + tsanm.Nodes.Remove("mtCloudB"); + tsanm.Nodes.Remove("mtWater_nami"); + + rf.GetModelGroup().RemoveAt(0); + rf.GetModelGroup()["KpBG"] = m; + + // Make bytecode + m.MatrixIDtoNodeID = new int[1]; + m.MatrixIDtoNodeID[0] = 0; + + // Now put together the NodeTree + var nodeTreeInsn = new ByteCode.AssignNodeToParentMtxInstruction(); + nodeTreeInsn.NodeID = 0; + nodeTreeInsn.ParentMatrixID = 0; + + var done = new ByteCode.DoneInstruction(); + + var nodeTree = new ByteCode(); + nodeTree.Instructions.Add(nodeTreeInsn); + nodeTree.Instructions.Add(done); + + m.Bytecode["NodeTree"] = nodeTree; + + // Also, DrawOpa + var drawOpa = new ByteCode(); + var shapeInsn = new ByteCode.DrawShapeInstruction(); + shapeInsn.ShapeID = 0; + shapeInsn.NodeID = 0; + shapeInsn.MaterialID = 0; + drawOpa.Instructions.Add(shapeInsn); + drawOpa.Instructions.Add(done); + m.Bytecode["DrawOpa"] = drawOpa; + + var drawXlu = new ByteCode(); + drawXlu.Instructions.Add(done); + m.Bytecode["DrawXlu"] = drawXlu; + + + // Clean up other shit + var seashape = m.Shapes["polygon106"]; + var posd = seashape.PosData; + var nrmd = seashape.NrmData; + var clrd = seashape.ClrData; + var tcd = seashape.TexCoordData; + + var nnod = new ResDict(); + var nshp = new ResDict(); + var nshd = new ResDict(); + var nmat = new ResDict(); + var npos = new ResDict(); + var nnrm = new ResDict(); + var nclr = new ResDict(); + var ntc = new ResDict(); + + Node rn = m.Nodes["CS_W4"]; + rn.FirstChild = null; + nnod["RootNode"] = rn; + nshp["waterpoly"] = seashape; + nshd["mtWater"] = m.Shaders["mtWater"]; + nmat["mtWater"] = m.Materials["mtWater"]; + + if (posd != null) { + npos[m.VtxPosData.GetKeyForValue(posd)] = posd; + } + + if (nrmd != null) { + nnrm[m.VtxNrmData.GetKeyForValue(nrmd)] = nrmd; + } + + foreach (var t in clrd) { + if (t != null) { + nclr[m.VtxClrData.GetKeyForValue(t)] = t; + } + } + + foreach (var t in tcd) { + if (t != null) { + ntc[m.VtxTexCoordData.GetKeyForValue(t)] = t; + } + } + + m.Nodes = nnod; + m.Shapes = nshp; + m.Shaders = nshd; + m.Materials = nmat; + m.VtxPosData = npos; + m.VtxNrmData = nnrm; + m.VtxClrData = nclr; + m.VtxTexCoordData = ntc; + + m.PairingLookupByTexture = new ResDict>(); + m.PairingLookupByPalette = new ResDict>(); + + // NEW + var dl = new DisplayListWriter(); + dl.BeginPrimitives(PrimitiveType.Quads, 0, 4); + dl.WriteByte(0); + dl.WriteByte(0); + dl.WriteByte(1); + dl.WriteByte(1); + dl.WriteByte(2); + dl.WriteByte(2); + dl.WriteByte(3); + dl.WriteByte(3); + dl.End(); + seashape.DisplayList2 = dl.GetBuffer(); + seashape.DLBufferSize2 = (uint)seashape.DisplayList2.Length; + var shape = seashape; + + float w = 200000, h = 100000; + float sw = 600, sh = 600; + float ow = -480, oh = 480; + + posd.EntryCount = 4; + posd.Data = new float[][] { + new float[] { ow, (-h) + oh, -40 }, + new float[] { ow, oh, -40 }, + new float[] { ow + w, oh, -40 }, + new float[] { ow + w, (-h) + oh, -40 } + }; + posd.EntrySize = 12; + posd.Fraction = 0; + posd.ComponentType = VertexSettings.CompType.Float32; + posd.Save(); + + tcd[0].EntryCount = 4; + tcd[0].Fraction = 0; + tcd[0].Data = new float[][] { + new float[] { 0, 0 }, + new float[] { sw, 0 }, + new float[] { sw, sh }, + new float[] { 0, sh } + }; + tcd[0].Save(); + /* posd.Data = new float[][] { + new float[] { 0, -40, h }, + new float[] { 0, -40, 0 }, + new float[] { w, -40, 0 }, + new float[] { w, -40, h } + };*/ + + var vs = new VertexSettings(); + vs.PositionDesc = VertexSettings.DescType.Index8; + vs.PositionFormat = VertexSettings.CompType.Float32; + vs.PositionCount = VertexSettings.CompCount.Position3; + + vs.TexCoordDesc[0] = VertexSettings.DescType.Index8; + vs.TexCoordFormat[0] = VertexSettings.CompType.Int16; + vs.TexCoordCount[0] = VertexSettings.CompCount.TexCoord2; + vs.TexCoordFrac[0] = 0; + + uint vd1, vd2, vat1, vat2, vat3; + vs.GetDesc(out vd1, out vd2); + vs.GetAttrFmt(out vat1, out vat2, out vat3); + + byte vtxSpecs = 1 << 4; + // setting up Unk in the hopes it will work, I think this is what it's for.. + var unkStream = new OutputStream(); + unkStream.WriteUInt32(vd1); + unkStream.WriteUInt32(vd2); + unkStream.WriteUInt32(vtxSpecs); + shape.Unk = unkStream.GetBuffer(); + + // now make the display list + var dl1 = new DisplayListWriter(); + dl1.AddPadding(10); + dl1.LoadCPReg(0x50, vd1); + dl1.LoadCPReg(0x60, vd2); + dl1.LoadXFReg(0x1008, new byte[] { 0x00, 0x00, 0x00, vtxSpecs }); + dl1.Nop(); + dl1.LoadCPReg(0x70, vat1); + dl1.LoadCPReg(0x80, vat2); + dl1.LoadCPReg(0x90, vat3); + + // extend it + // should it be bigger if more texcoords are used? maybe... + dl1.PadToSize(0x80); + dl1.End(); + + shape.DLBufferSize1 = 0xE0; + shape.DisplayList1 = dl1.GetBuffer(); + + + + var texmask = tgrp["texMask"].Images[0]; + var g = System.Drawing.Graphics.FromImage(texmask); + g.FillRectangle(new System.Drawing.SolidBrush(System.Drawing.Color.White), 0, 0, texmask.Width, texmask.Height); + g.Dispose(); + + + // END NEW + + File.WriteAllBytes(mdlPath + "Water.brres", BrresWriter.WriteFile(rf)); + } + + + private static string getPath() { + if (Directory.Exists("/home/me")) + return "/home/me/Games/Newer/WaterL/"; + else + return "Z:/stuff/Games/Newer/WaterL/"; + } + + private static void fuckskawo() { + var rf = BrresReader.LoadFile(File.ReadAllBytes("/home/me/Games/Newer/Pakku/pakkun/g3d/pakkun.brres")); + File.WriteAllBytes("/home/me/Games/Newer/Pakku/pakkun/g3d/pakkuRewrite.brres", BrresWriter.WriteFile(rf)); + } } } -- cgit v1.2.3