diff options
-rw-r--r-- | ConsoleApp/ConsoleApp.csproj | 3 | ||||
-rw-r--r-- | NW4RTools.sln | 4 | ||||
-rwxr-xr-x | NW4RTools.suo | bin | 16896 -> 16896 bytes | |||
-rwxr-xr-x[-rw-r--r--] | NW4RTools/BrresReader.cs | 33 | ||||
-rwxr-xr-x[-rw-r--r--] | NW4RTools/BrresWriter.cs | 967 | ||||
-rw-r--r-- | NW4RTools/Misc.cs | 40 | ||||
-rw-r--r-- | NW4RTools/Models/Animation/CharacterAnim.cs | 2 | ||||
-rw-r--r-- | NW4RTools/Models/Animation/Shared.cs | 40 | ||||
-rwxr-xr-x[-rw-r--r--] | NW4RTools/Models/ByteCode.cs | 3 | ||||
-rw-r--r-- | NW4RTools/NW4RTools.csproj | 183 | ||||
-rw-r--r-- | NW4RTools/Texture.cs | 15 | ||||
-rw-r--r-- | NW4RTools/Types.cs | 10 | ||||
-rwxr-xr-x[-rw-r--r--] | TestApp/Main.cs | 29 | ||||
-rw-r--r-- | TestApp/TestApp.csproj | 102 |
14 files changed, 1256 insertions, 175 deletions
diff --git a/ConsoleApp/ConsoleApp.csproj b/ConsoleApp/ConsoleApp.csproj index 727c77e..2e07d89 100644 --- a/ConsoleApp/ConsoleApp.csproj +++ b/ConsoleApp/ConsoleApp.csproj @@ -32,8 +32,7 @@ <ItemGroup> <Reference Include="System" /> <Reference Include="System.Drawing" /> - <Reference Include="OpenTK, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4"> - <SpecificVersion>False</SpecificVersion> + <Reference Include="OpenTK"> <HintPath>..\ExternalLibs\OpenTK.dll</HintPath> </Reference> </ItemGroup> diff --git a/NW4RTools.sln b/NW4RTools.sln index 15f8760..0423c6d 100644 --- a/NW4RTools.sln +++ b/NW4RTools.sln @@ -27,7 +27,7 @@ Global {A9C9FABD-0A5F-4DAB-979D-9F288F96866F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(MonoDevelopProperties) = preSolution
- StartupItem = ConsoleApp\ConsoleApp.csproj
+ StartupItem = TestApp\TestApp.csproj
Policies = $0
$0.DotNetNamingPolicy = $1
$1.DirectoryNamespaceAssociation = None
@@ -44,8 +44,8 @@ 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
diff --git a/NW4RTools.suo b/NW4RTools.suo Binary files differindex 372e67d..5e67343 100755 --- a/NW4RTools.suo +++ b/NW4RTools.suo diff --git a/NW4RTools/BrresReader.cs b/NW4RTools/BrresReader.cs index f120943..9616b5b 100644..100755 --- a/NW4RTools/BrresReader.cs +++ b/NW4RTools/BrresReader.cs @@ -153,8 +153,15 @@ namespace NW4RTools { // handle mipmaps tex.Images = new System.Drawing.Bitmap[mipmapCount]; +#if DEV_DONT_MODIFY_TEXTURE + tex.ImageData = new byte[mipmapCount][]; +#endif for (int i = 0; i < mipmapCount; i++) { - tex.ImportData(i, ins.ReadBytes(Texture.GetDataSize(width, height, format)), width, height, format); + byte[] data = ins.ReadBytes(Texture.GetDataSize(width, height, format)); +#if DEV_DONT_MODIFY_TEXTURE + tex.ImageData[i] = data; +#endif + tex.ImportData(i, data, width, height, format); width /= 2; height /= 2; } @@ -223,6 +230,10 @@ namespace NW4RTools { node.Flags = flags & CharacterAnim.Flags.NonComputableFlags; + node.ScaleUseModel = ((flags & CharacterAnim.Flags.ScaleUseModel) != 0); + node.RotateUseModel = ((flags & CharacterAnim.Flags.RotateUseModel) != 0); + node.TranslateUseModel = ((flags & CharacterAnim.Flags.TranslateUseModel) != 0); + // SCALES bool needScaleYZ = true; @@ -482,7 +493,7 @@ namespace NW4RTools { node.Elements = new ColorAnim.Element[(int)ColorAnim.TargetType.Count]; for (int i = 0; i < node.Elements.Length; i++) { - node.Elements[i] = ReadAnmClrElement(ins, startPos, flags); + node.Elements[i] = ReadAnmClrElement(ins, flags); flags >>= 2; } @@ -490,7 +501,7 @@ namespace NW4RTools { } } - private ColorAnim.Element ReadAnmClrElement(InputStream ins, int startPos, UInt32 flags) { + private ColorAnim.Element ReadAnmClrElement(InputStream ins, UInt32 flags) { var elem = new ColorAnim.Element(); elem.Exists = (flags & 1) != 0; @@ -498,16 +509,16 @@ namespace NW4RTools { OffsetMap[ins.Position] = String.Format("Element [{0}]", elem.IsConstant ? "Constant" : "Colours"); - elem.Mask = ins.ReadUInt32(); - if (!elem.Exists) return elem; + elem.Mask = ins.ReadUInt32(); + if (elem.IsConstant) { elem.ConstValue = ins.ReadColor(); - } else { + } else {
int savePos = ins.Position; - ins.Seek(startPos + ins.ReadInt32()); + ins.Seek(savePos + ins.ReadInt32()); elem.Colors = new Color[COLOR_ANIM_HACK.FrameCount + 1]; @@ -597,7 +608,9 @@ 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")); @@ -863,8 +876,8 @@ namespace NW4RTools { var insn4 = new ByteCode.DrawShapeInstruction(); insn4.MaterialID = ins.ReadUInt16(); insn4.ShapeID = ins.ReadUInt16(); - insn4.NodeID = ins.ReadUInt16(); - ins.Skip(1); + insn4.NodeID = ins.ReadUInt16();
+ insn4.Unk = ins.ReadByte();
bc.Instructions.Add(insn4); break; diff --git a/NW4RTools/BrresWriter.cs b/NW4RTools/BrresWriter.cs index 04750de..033007e 100644..100755 --- a/NW4RTools/BrresWriter.cs +++ b/NW4RTools/BrresWriter.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using NW4RTools.Models; +using NW4RTools.Models.Animation; namespace NW4RTools { public class BrresWriter { @@ -53,6 +54,7 @@ namespace NW4RTools { // This code will also handle building the string table. #region Offset Data Storage + #region Model Data Storage private Dictionary<Model, int> ModelOffsets; private Dictionary<ByteCode, int> BytecodeOffsets; private Dictionary<Node, int> NodeOffsets; @@ -78,9 +80,118 @@ namespace NW4RTools { public int PairingLookupByTexture; public int BlockSize; // Duplicate shader structs are removed from the model, so they require special tracking - public List<Shader> UniqueShaders; + public List<Shader> UniqueShaders;
+ public Dictionary<Material, int> MaterialSizes;
+
+ public ModelCalcInfo() {
+ UniqueShaders = new List<Shader>();
+ MaterialSizes = new Dictionary<Material,int>();
+ } } private Dictionary<Model, ModelCalcInfo> ModelCalcInfos; + #endregion + + #region Character Animation Data Storage + private class ChrAnmNodeInfo { + public CharacterAnim.Flags Flags; + } + + private class ChrAnmInfo { + public KeyframeWriterInfo KFInfo; + public Dictionary<CharacterAnim.Node, ChrAnmNodeInfo> NodeInfo; + public Dictionary<CharacterAnim.Node, int> NodeOffsets; + public int BlockSize; + + public ChrAnmInfo() { + KFInfo = new KeyframeWriterInfo(); + NodeInfo = new Dictionary<CharacterAnim.Node, ChrAnmNodeInfo>(); + NodeOffsets = new Dictionary<CharacterAnim.Node, int>(); + } + } + private Dictionary<CharacterAnim, ChrAnmInfo> ChrAnmInfos; + private Dictionary<CharacterAnim, int> ChrAnmOffsets; + #endregion + + #region Color Animation Data Storage + private class ClrAnmInfo { + public Dictionary<ColorAnim.Node, int> NodeOffsets; + public int BlockSize; + + public List<Color[]> UsedArrays; + public List<int> UsedArrayPositions; + + public ClrAnmInfo() { + NodeOffsets = new Dictionary<ColorAnim.Node, int>(); + UsedArrays = new List<Color[]>(); + UsedArrayPositions = new List<int>(); + } + } + private Dictionary<ColorAnim, ClrAnmInfo> ClrAnmInfos; + private Dictionary<ColorAnim, int> ClrAnmOffsets; + #endregion + + #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];
+ } + } + + private class TexSRTAnmInfo { + public KeyframeWriterInfo KFInfo; + public Dictionary<TextureSRTAnim.Node, TexSRTAnmNodeInfo> NodeInfo; + public Dictionary<TextureSRTAnim.Node, int> NodeOffsets; + public int BlockSize; + + public TexSRTAnmInfo() { + KFInfo = new KeyframeWriterInfo(); + NodeInfo = new Dictionary<TextureSRTAnim.Node,TexSRTAnmNodeInfo>(); + NodeOffsets = new Dictionary<TextureSRTAnim.Node, int>(); + } + } + private Dictionary<TextureSRTAnim, TexSRTAnmInfo> TexSRTAnmInfos; + private Dictionary<TextureSRTAnim, int> TexSRTAnmOffsets; + #endregion + + private class KeyframeWriterInfo { + public Dictionary<Pair<object, KeyframeAnim>, int> KeyframeAnimOffsets; + public List<Pair<object, KeyframeAnim>> KeyframeAnims; + + public KeyframeWriterInfo() { + KeyframeAnimOffsets = new Dictionary<Pair<object, KeyframeAnim>, int>(); + KeyframeAnims = new List<Pair<object, KeyframeAnim>>(); + } + + public void Add(KeyframeAnim anim) { + Add(new Pair<object, KeyframeAnim>(null, anim)); + } + + public void Add(object obj, KeyframeAnim anim) { + Add(new Pair<object, KeyframeAnim>(obj, anim)); + } + + public void Add(Pair<object, KeyframeAnim> pair) { + if (pair.two.IsConstant) + return; + if (KeyframeAnimOffsets.ContainsKey(pair)) + return; + + KeyframeAnims.Add(pair); + KeyframeAnimOffsets.Add(pair, -1); + } + + public int GetPosition(KeyframeAnim anim) { + return KeyframeAnimOffsets[new Pair<object, KeyframeAnim>(null, anim)]; + } + + public int GetPosition(object obj, KeyframeAnim anim) { + return KeyframeAnimOffsets[new Pair<object, KeyframeAnim>(obj, anim)]; + } + } private Dictionary<Texture, int> TextureOffsets; private Dictionary<Texture, int> TextureDataOffsets; @@ -130,6 +241,15 @@ namespace NW4RTools { case "Textures(NW4R)": CalculateTextures(); break; + case "AnmChr(NW4R)": + CalculateAnmChr(); + break; + case "AnmClr(NW4R)": + CalculateAnmClr(); + break; + case "AnmTexSrt(NW4R)": + CalculateAnmTexSrt(); + break; default: Debug.Send("[[ UNIMPLEMENTED {0} ]]", name); break; @@ -241,7 +361,6 @@ namespace NW4RTools { CalculateBytecode(model); CalculateNodes(model); CalculateMaterials(model); - calcInfo.UniqueShaders = new List<Shader>(); CalculateShaders(model); CalculateShapes(model); CalculateVtxPosData(model); @@ -358,6 +477,10 @@ namespace NW4RTools { LogPosition("Material Display Lists: " + kv.Key); MaterialDLOffsets[kv.Value] = CurrentPos; CurrentPos += 0x20 + 0x80 + 0x40 + 0xA0; + + // Sized!
+ int size = CurrentPos - MaterialOffsets[kv.Value];
+ ModelCalcInfos[m].MaterialSizes[kv.Value] = size; } } @@ -549,8 +672,363 @@ namespace NW4RTools { } #endregion + #region Character Animation Calculation + private void CalculateAnmChr() { + ChrAnmOffsets = new Dictionary<CharacterAnim, int>(); + ChrAnmInfos = new Dictionary<CharacterAnim, ChrAnmInfo>(); + + var chrAnmDict = File.GetGroup<CharacterAnim>("AnmChr(NW4R)"); + + foreach (var kv in chrAnmDict) { + AddString(kv.Key); + AlignCalcPos(0x20); + CharacterAnim anim = kv.Value; + + LogPosition("Character Anim: " + kv.Key); + ChrAnmOffsets.Add(anim, CurrentPos); + + CurrentPos += 0x20; + LogPosition("Character Anim Info Struct: " + kv.Key); + + CurrentPos += 0xC; + LogPosition("Character Anim Node Dictionary: " + kv.Key); + CurrentPos += GetSizeForResDict(anim.Nodes.Count); + + var anminfo = new ChrAnmInfo(); + ChrAnmInfos.Add(anim, anminfo); + + // Now size each node + foreach (var nodepair in anim.Nodes) { + AddString(nodepair.Key); + CharacterAnim.Node node = nodepair.Value; + + anminfo.NodeOffsets.Add(node, CurrentPos); + + LogPosition("Character Anim Node: " + nodepair.Key); + CurrentPos += 8; + + var nodeinfo = new ChrAnmNodeInfo(); + anminfo.NodeInfo.Add(node, nodeinfo); + + nodeinfo.Flags = CharacterAnim.Flags.AlwaysSet | (node.Flags & CharacterAnim.Flags.NonComputableFlags); + + if (node.ScaleUseModel) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleUseModel; + if (node.RotateUseModel) + nodeinfo.Flags |= CharacterAnim.Flags.RotateUseModel; + if (node.TranslateUseModel) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateUseModel; + + // Scale + if (node.ScaleX.IsConstWith(1) && node.ScaleY.IsConstWith(1) && node.ScaleZ.IsConstWith(1)) { + nodeinfo.Flags |= CharacterAnim.Flags.ScaleOne; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleUniform; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleXConstant; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; + } else { + nodeinfo.Flags |= CharacterAnim.Flags.CalcScale; + + anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleX); + + if (node.ScaleX.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleXConstant; + CurrentPos += 4; + + if (node.ScaleX.Equals(node.ScaleY) && node.ScaleX.Equals(node.ScaleZ)) { + nodeinfo.Flags |= CharacterAnim.Flags.ScaleUniform; + if (node.ScaleX.IsConstant) { + nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; + } + } else { + anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleY); + anminfo.KFInfo.Add(node.ScaleFormat, node.ScaleZ); + + if (node.ScaleY.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleYConstant; + if (node.ScaleZ.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.ScaleZConstant; + CurrentPos += 8; + } + } + + // Rotation + if (node.RotateX.IsConstWith(0) && node.RotateY.IsConstWith(0) && node.RotateZ.IsConstWith(0)) { + nodeinfo.Flags |= CharacterAnim.Flags.RotateZero; + nodeinfo.Flags |= CharacterAnim.Flags.RotateXConstant; + nodeinfo.Flags |= CharacterAnim.Flags.RotateYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.RotateZConstant; + } else { + nodeinfo.Flags |= CharacterAnim.Flags.CalcRotate; + + anminfo.KFInfo.Add(node.RotateFormat, node.RotateX); + anminfo.KFInfo.Add(node.RotateFormat, node.RotateY); + anminfo.KFInfo.Add(node.RotateFormat, node.RotateZ); + + if (node.RotateX.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.RotateXConstant; + if (node.RotateY.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.RotateYConstant; + if (node.RotateZ.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.RotateZConstant; + CurrentPos += 12; + } + + // Translation + if (node.TranslateX.IsConstWith(0) && node.TranslateY.IsConstWith(0) && node.TranslateZ.IsConstWith(0)) { + nodeinfo.Flags |= CharacterAnim.Flags.TranslateZero; + nodeinfo.Flags |= CharacterAnim.Flags.TranslateXConstant; + nodeinfo.Flags |= CharacterAnim.Flags.TranslateYConstant; + nodeinfo.Flags |= CharacterAnim.Flags.TranslateZConstant; + } else { + nodeinfo.Flags |= CharacterAnim.Flags.CalcTranslate; + + anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateX); + anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateY); + anminfo.KFInfo.Add(node.TranslateFormat, node.TranslateZ); + + if (node.TranslateX.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateXConstant; + if (node.TranslateY.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateYConstant; + if (node.TranslateZ.IsConstant) + nodeinfo.Flags |= CharacterAnim.Flags.TranslateZConstant; + CurrentPos += 12; + } + + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateZero) != 0 && + (nodeinfo.Flags & CharacterAnim.Flags.TranslateZero) != 0) + nodeinfo.Flags |= CharacterAnim.Flags.RtZero; + + if ((nodeinfo.Flags & CharacterAnim.Flags.RtZero) != 0 && + (nodeinfo.Flags & CharacterAnim.Flags.ScaleOne) != 0) + nodeinfo.Flags |= CharacterAnim.Flags.Identity; + + nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.ScaleFormat << 25); + nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.RotateFormat << 27); + nodeinfo.Flags |= (CharacterAnim.Flags)((uint)node.TranslateFormat << 30); + } + + + // And do all keyframes + foreach (var pair in anminfo.KFInfo.KeyframeAnims) { + CharacterAnim.RotateFormatType format = (CharacterAnim.RotateFormatType)pair.one; + KeyframeAnim kf = pair.two; + + LogPosition("Keyframes"); + anminfo.KFInfo.KeyframeAnimOffsets[pair] = CurrentPos; + + switch (format) { + case CharacterAnim.RotateFormatType.Data32: + CurrentPos += 8 + (kf.Keyframes.Length * 4); + break; + case CharacterAnim.RotateFormatType.Data48: + CurrentPos += 8 + (kf.Keyframes.Length * 6); + break; + case CharacterAnim.RotateFormatType.Data96: + CurrentPos += 8 + (kf.Keyframes.Length * 12); + break; + case CharacterAnim.RotateFormatType.DataUnk4: + CurrentPos += 8 + (kf.Values.Length); + break; + case CharacterAnim.RotateFormatType.DataUnk5: + CurrentPos += 8 + (kf.Values.Length * 2); + break; + case CharacterAnim.RotateFormatType.DataUnk6: + CurrentPos += 4 + (kf.FloatValues.Length * 4); + break; + default: + throw new NotImplementedException(); + } + + AlignCalcPos(4); + } + + + anminfo.BlockSize = CurrentPos - ChrAnmOffsets[anim]; + } + } + #endregion + + #region Color Animation Calculation + private void CalculateAnmClr() { + ClrAnmOffsets = new Dictionary<ColorAnim, int>(); + ClrAnmInfos = new Dictionary<ColorAnim, ClrAnmInfo>(); + + var clrAnmDict = File.GetGroup<ColorAnim>("AnmClr(NW4R)"); + + foreach (var kv in clrAnmDict) { + AddString(kv.Key); + ColorAnim anim = kv.Value; + + LogPosition("Color Anim: " + kv.Key); + ClrAnmOffsets.Add(anim, CurrentPos); + + CurrentPos += 0x20; + LogPosition("Color Anim Info Struct: " + kv.Key); + + CurrentPos += 0x8; + LogPosition("Color Anim Node Dictionary: " + kv.Key); + CurrentPos += GetSizeForResDict(anim.Nodes.Count); + + var anminfo = new ClrAnmInfo(); + ClrAnmInfos.Add(anim, anminfo); + + // Now size each node + foreach (var nodepair in anim.Nodes) { + AddString(nodepair.Key); + ColorAnim.Node node = nodepair.Value; + + anminfo.NodeOffsets.Add(node, CurrentPos); + + LogPosition("Color Anim Node: " + nodepair.Key); + CurrentPos += 8; + + for (int i = 0; i < node.Elements.Length; i++) { + if (node.Elements[i].Exists) { + CurrentPos += 8; + if (!node.Elements[i].IsConstant) { + // Add the array + // But check to make sure it's not already there + bool wasFound = false; + + foreach (var arr in anminfo.UsedArrays) { + if (Misc.ArrayCompare(node.Elements[i].Colors, arr)) { + wasFound = true; + break; + } + } + + if (!wasFound) + anminfo.UsedArrays.Add(node.Elements[i].Colors); + } + } + } + } + + // Finally, add every array + foreach (var arr in anminfo.UsedArrays) { + anminfo.UsedArrayPositions.Add(CurrentPos); + CurrentPos += (arr.Length * 4); + } + + anminfo.BlockSize = CurrentPos - ClrAnmOffsets[anim]; + } + } + #endregion + + #region Texture SRT Animation Calculation + private void CalculateAnmTexSrt() { + TexSRTAnmOffsets = new Dictionary<TextureSRTAnim, int>(); + TexSRTAnmInfos = new Dictionary<TextureSRTAnim, TexSRTAnmInfo>(); + + var chrAnmDict = File.GetGroup<TextureSRTAnim>("AnmTexSrt(NW4R)"); + + foreach (var kv in chrAnmDict) { + AddString(kv.Key); + AlignCalcPos(0x20); + TextureSRTAnim anim = kv.Value; + + LogPosition("TextureSRT Anim: " + kv.Key); + TexSRTAnmOffsets.Add(anim, CurrentPos); + + CurrentPos += 0x20; + LogPosition("TextureSRT Anim Info Struct: " + kv.Key); + + CurrentPos += 0xC; + LogPosition("TextureSRT Anim Node Dictionary: " + kv.Key); + CurrentPos += GetSizeForResDict(anim.Nodes.Count); + + var anminfo = new TexSRTAnmInfo(); + TexSRTAnmInfos.Add(anim, anminfo);
+
+ // Now size each node + foreach (var nodepair in anim.Nodes) { + AddString(nodepair.Key); + TextureSRTAnim.Node node = nodepair.Value; + + anminfo.NodeOffsets.Add(node, CurrentPos); + + 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]);
+ }
+ } + } + CalculateKeyframeData(anminfo.KFInfo); + 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 AlignCalcPos(int alignTo) { if ((CurrentPos & (alignTo - 1)) == 0) return; @@ -594,6 +1072,15 @@ namespace NW4RTools { case "Textures(NW4R)": WriteResDict<Texture>(kv.Value as ResDict<Texture>, TextureOffsets); break; + case "AnmChr(NW4R)": + WriteResDict<CharacterAnim>(kv.Value as ResDict<CharacterAnim>, ChrAnmOffsets); + break; + case "AnmClr(NW4R)": + WriteResDict<ColorAnim>(kv.Value as ResDict<ColorAnim>, ClrAnmOffsets); + break; + case "AnmTexSrt(NW4R)": + WriteResDict<TextureSRTAnim>(kv.Value as ResDict<TextureSRTAnim>, TexSRTAnmOffsets); + break; default: // for testing, boo Output.AddPadding(GetSizeForResDict((kv.Value as ICollection).Count)); @@ -613,6 +1100,15 @@ namespace NW4RTools { case "Textures(NW4R)": WriteTextures(); break; + case "AnmChr(NW4R)": + WriteAnmChr(); + break; + case "AnmClr(NW4R)": + WriteAnmClr(); + break; + case "AnmTexSrt(NW4R)": + WriteAnmTexSrt(); + break; default: Debug.Send("UNHANDLED RESOURCE TYPE: {0}", kv.Key); break; @@ -637,7 +1133,9 @@ namespace NW4RTools { Model model = kv.Value; var calcInfo = ModelCalcInfos[model]; - // Base struct: magic 'MDL0', block size, version [currently 11], resfile offset + Misc.Assert(startPos == ModelOffsets[model]); + + // Base struct: magic 'MDL0', block size, version[currently 11], resfile offset Output.WriteUInt32(0x4D444C30); Output.WriteUInt32((uint)calcInfo.BlockSize); Output.WriteUInt32(11); @@ -680,6 +1178,7 @@ namespace NW4RTools { Output.WriteUInt32((uint)model.VertexCount); Output.WriteUInt32((uint)model.TriangleCount); Output.WriteUInt32(0); + // TODO: This is not right, fix it Output.WriteUInt32((uint)model.MatrixIDtoNodeID.Length); Output.WriteByte(model.UsesNrmMtxArray ? (byte)1 : (byte)0); Output.WriteByte(model.UsesTexMtxArray ? (byte)1 : (byte)0); @@ -778,8 +1277,8 @@ namespace NW4RTools { var op4 = insn as ByteCode.DrawShapeInstruction; Output.WriteUInt16(op4.MaterialID); Output.WriteUInt16(op4.ShapeID); - Output.WriteUInt16(op4.NodeID); - Output.WriteByte(0); + Output.WriteUInt16(op4.NodeID);
+ Output.WriteByte(op4.Unk); break; case ByteCode.OpType.AssignMtxToNode: @@ -847,9 +1346,9 @@ namespace NW4RTools { Material mat = kv.Value; int startPos = Output.Position; - // size, model offset, name offset, index - // is size static, or does it change? must verify - Output.WriteUInt32(0x5E8); + // size, model offset, name offset, index
+ var modelInfo = ModelCalcInfos[m]; + Output.WriteUInt32((UInt32)modelInfo.MaterialSizes[mat]); Output.WriteInt32(ModelOffsets[m] - startPos); Output.WriteInt32(StringPositions[kv.Key] - startPos); Output.WriteUInt32((uint)currentIndex); @@ -1233,7 +1732,9 @@ namespace NW4RTools { int startPos = Output.Position; Texture tex = kv.Value; - // Base struct: magic 'TEX0', block size, version [currently 11], resfile offset + Misc.Assert(startPos == TextureOffsets[tex]); + + // Base struct: magic 'TEX0', block size, version[currently 3], resfile offset Output.WriteUInt32(0x54455830); Output.WriteUInt32((uint)(0x40 + tex.GetDataSize())); Output.WriteUInt32(3); @@ -1257,13 +1758,459 @@ namespace NW4RTools { Output.AlignTo(0x20); for (int i = 0; i < tex.Images.Length; i++) +#if DEV_DONT_MODIFY_TEXTURE + Output.WriteBytes(tex.ImageData[i]); +#else Output.WriteBytes(tex.ExportData(i)); +#endif + } + } + } + #endregion + + #region Character Animation Writing + private void WriteAnmChr() { + foreach (var kv in File.GetGroup<CharacterAnim>("AnmChr(NW4R)")) { + using (var c = Debug.Push("Character Animation: {0}", kv.Key)) { + Output.AlignTo(0x20); + + int startPos = Output.Position; + CharacterAnim anm = kv.Value; + var info = ChrAnmInfos[anm]; + + Misc.Assert(startPos == ChrAnmOffsets[anm]); + + // Base struct: magic 'CHR0', block size, version[currently 5], resfile offset + Output.WriteUInt32(0x43485230); + 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(anm.Loop ? 1U : 0U); + Output.WriteUInt32((UInt32)anm.ScaleMode); + + WriteResDict<CharacterAnim.Node>(anm.Nodes, info.NodeOffsets); + + WriteAnmChrNodes(anm); + WriteAnmChrKeyframes(anm); + } + } + } + + private void WriteAnmChrNodes(CharacterAnim anm) { + var info = ChrAnmInfos[anm]; + + foreach (var kv in anm.Nodes) { + using (var c = Debug.Push("Element: {0}", kv.Key)) { + int startPos = Output.Position; + CharacterAnim.Node node = kv.Value; + ChrAnmNodeInfo nodeinfo = info.NodeInfo[node]; + + Misc.Assert(startPos == info.NodeOffsets[node]); + + Output.WriteInt32(StringPositions[kv.Key] - startPos); + Output.WriteUInt32((UInt32)nodeinfo.Flags); + + if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleXNotExist) == 0) { + if (node.ScaleX.IsConstant) + Output.WriteFloat(node.ScaleX.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleX) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleYNotExist) == 0) { + if (node.ScaleY.IsConstant) + Output.WriteFloat(node.ScaleY.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleY) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.ScaleZNotExist) == 0) { + if (node.ScaleZ.IsConstant) + Output.WriteFloat(node.ScaleZ.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.ScaleFormat, node.ScaleZ) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateXNotExist) == 0) { + if (node.RotateX.IsConstant) + Output.WriteFloat(node.RotateX.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateX) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateYNotExist) == 0) { + if (node.RotateY.IsConstant) + Output.WriteFloat(node.RotateY.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateY) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.RotateZNotExist) == 0) { + if (node.RotateZ.IsConstant) + Output.WriteFloat(node.RotateZ.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.RotateFormat, node.RotateZ) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateXNotExist) == 0) { + if (node.TranslateX.IsConstant) + Output.WriteFloat(node.TranslateX.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateX) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateYNotExist) == 0) { + if (node.TranslateY.IsConstant) + Output.WriteFloat(node.TranslateY.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateY) - startPos); + } + + if ((nodeinfo.Flags & CharacterAnim.Flags.TranslateZNotExist) == 0) { + if (node.TranslateZ.IsConstant) + Output.WriteFloat(node.TranslateZ.ConstValue); + else + Output.WriteInt32(info.KFInfo.GetPosition(node.TranslateFormat, node.TranslateZ) - startPos); + } + } + } + } + + private void WriteAnmChrKeyframes(CharacterAnim anm) { + var anminfo = ChrAnmInfos[anm]; + foreach (var kv in anminfo.KFInfo.KeyframeAnimOffsets) { + var format = (CharacterAnim.RotateFormatType)kv.Key.one; + var kf = kv.Key.two; + var expectedOffset = kv.Value; + + Misc.Assert(Output.Position == expectedOffset); + + if (format == CharacterAnim.RotateFormatType.Data32 || + format == CharacterAnim.RotateFormatType.Data48 || + format == CharacterAnim.RotateFormatType.Data96) { + // Header for FVS + 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); } + + switch (format) { + case CharacterAnim.RotateFormatType.Data32: + for (int i = 0; i < kf.Keyframes.Length; i++) { + Output.WriteUInt32( + (((UInt32)kf.Keyframes[i].Frame) << 24) | + ((((UInt32)kf.Keyframes[i].Value) & 0xFFF) << 12) | + (((UInt32)(kf.Keyframes[i].Slope * 32.0f)) & 0xFFF)); + } + break; + case CharacterAnim.RotateFormatType.Data48: + for (int i = 0; i < kf.Keyframes.Length; i++) { + Output.WriteInt16((Int16)(kf.Keyframes[i].Frame * 32.0f)); + Output.WriteUInt16((UInt16)(kf.Keyframes[i].Value / (360.0f / 0x10000))); + Output.WriteInt16((Int16)(kf.Keyframes[i].Slope * 256.0f)); + } + break; + case CharacterAnim.RotateFormatType.Data96: + 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); + } + break; + default: + throw new NotImplementedException(); + } + + Output.AlignTo(4); } } #endregion - #region ResDicts + #region Color Animation Writing + private void WriteAnmClr() { + foreach (var kv in File.GetGroup<ColorAnim>("AnmClr(NW4R)")) { + using (var c = Debug.Push("Color Animation: {0}", kv.Key)) { + int startPos = Output.Position; + ColorAnim anm = kv.Value; + var info = ClrAnmInfos[anm]; + + Misc.Assert(startPos == ClrAnmOffsets[anm]); + + // Base struct: magic 'CLR0', block size, version[currently 5], resfile offset + Output.WriteUInt32(0x434C5230); + Output.WriteUInt32((UInt32)info.BlockSize); + Output.WriteUInt32(4); + Output.WriteInt32(-startPos); + + // Data offset, name offset, other crap + Output.WriteInt32(0x28); + 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(anm.Loop ? 1U : 0U); + + WriteResDict<ColorAnim.Node>(anm.Nodes, info.NodeOffsets); + + WriteAnmClrNodes(anm); + WriteAnmClrArrays(anm); + } + } + } + + private void WriteAnmClrNodes(ColorAnim anm) { + ClrAnmInfo info = ClrAnmInfos[anm]; + + foreach (var kv in anm.Nodes) { + using (var c = Debug.Push("Element: {0}", kv.Key)) { + int startPos = Output.Position; + ColorAnim.Node node = kv.Value; + + Misc.Assert(startPos == info.NodeOffsets[node]); + + Output.WriteInt32(StringPositions[kv.Key] - startPos); + + UInt32 flags = 0; + + for (int i = node.Elements.Length - 1; i >= 0; i--) { + flags <<= 2; + if (node.Elements[i].Exists) + flags |= 1; + if (node.Elements[i].IsConstant) + flags |= 2; + } + + Output.WriteUInt32(flags); + + for (int i = 0; i < node.Elements.Length; i++) { + if (node.Elements[i].Exists) {
+ Output.WriteUInt32(node.Elements[i].Mask); + if (node.Elements[i].IsConstant) { + Output.WriteColor(node.Elements[i].ConstValue); + } else { + // Search for the matching array + int find = 0; + bool wasFound = false; + foreach (var arr in info.UsedArrays) { + if (Misc.ArrayCompare(node.Elements[i].Colors, arr)) { + wasFound = true; + break; + } + find++; + } + + if (!wasFound) + throw new Exception(); + + int arrayPos = info.UsedArrayPositions[find]; + Output.WriteInt32(arrayPos - Output.Position); + } + } + } + + } + } + } + + private void WriteAnmClrArrays(ColorAnim anm) { + var info = ClrAnmInfos[anm]; + + int i = 0; + foreach (var arr in info.UsedArrays) { + Misc.Assert(info.UsedArrayPositions[i] == Output.Position); + + for (int j = 0; j < arr.Length; j++) + Output.WriteColor(arr[j]); + + i++; + } + } + #endregion
+
+ #region Texture SRT Animation Writing
+ private void WriteAnmTexSrt() {
+ foreach (var kv in File.GetGroup<TextureSRTAnim>("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<TextureSRTAnim.Node>(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<TextureSRTAnim.Element>();
+
+ 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/Misc.cs b/NW4RTools/Misc.cs index c42b185..118f164 100644 --- a/NW4RTools/Misc.cs +++ b/NW4RTools/Misc.cs @@ -16,6 +16,46 @@ namespace NW4RTools { public static uint AlignDown(uint val, uint to) { return val & ~(to - 1); } + + + public static void Assert(bool condition) { + if (!condition) + throw new Exception("Assert failed"); + } + public static void Assert(bool condition, string format, params object[] args) { + if (!condition) + throw new Exception(String.Format(format, args)); + } + + + public static bool ArrayCompare<T>(T[] one, T[] two) { + if (one == two) + return true; + if (one == null || two == null) + return false; + if (one.Length != two.Length) + return false; + + for (int i = 0; i < one.Length; i++) { + if (!one[i].Equals(two[i])) + return false; + } + + return true; + } + + + public static int ArrayHash<T>(T[] arr) { + if (arr == null) + return 0xEA7BEEF; + + int hash = 0; + + for (int i = 0; i < arr.Length; i++) + hash ^= arr[i].GetHashCode(); + + return hash; + } } } diff --git a/NW4RTools/Models/Animation/CharacterAnim.cs b/NW4RTools/Models/Animation/CharacterAnim.cs index dd496f6..72efd56 100644 --- a/NW4RTools/Models/Animation/CharacterAnim.cs +++ b/NW4RTools/Models/Animation/CharacterAnim.cs @@ -81,6 +81,8 @@ namespace NW4RTools.Models.Animation { public STFormatType ScaleFormat; public STFormatType TranslateFormat; public RotateFormatType RotateFormat; + + public bool ScaleUseModel, RotateUseModel, TranslateUseModel; } public CharacterAnim() { diff --git a/NW4RTools/Models/Animation/Shared.cs b/NW4RTools/Models/Animation/Shared.cs index f87bea6..62cca94 100644 --- a/NW4RTools/Models/Animation/Shared.cs +++ b/NW4RTools/Models/Animation/Shared.cs @@ -28,5 +28,45 @@ namespace NW4RTools.Models.Animation { Values = null; FloatValues = null; } + + + public bool IsConstWith(float cv) { + return (IsConstant && (ConstValue == cv)); + } + + + public override bool Equals(object obj) { + return obj is KeyframeAnim && this == (KeyframeAnim)obj; + } + + public override int GetHashCode() { + return + IsConstant.GetHashCode() ^ + ConstValue.GetHashCode() ^ + BaseValue.GetHashCode() ^ + Multiplier.GetHashCode() ^ + Misc.ArrayHash(Keyframes) ^ + Misc.ArrayHash(Values) ^ + Misc.ArrayHash(FloatValues); + } + + public static bool operator ==(KeyframeAnim x, KeyframeAnim y) { + return + (x.IsConstant == y.IsConstant) && + (x.ConstValue == y.ConstValue) && + (x.BaseValue == y.BaseValue) && + (x.Multiplier == y.Multiplier) && + Misc.ArrayCompare(x.Keyframes, y.Keyframes) && + Misc.ArrayCompare(x.Values, y.Values) && + Misc.ArrayCompare(x.FloatValues, y.FloatValues); + } + + public static bool operator !=(KeyframeAnim x, KeyframeAnim y) { + return !(x == y); + } + + public void Dump() { + Console.WriteLine("IsC:{0} CV:{1} KF:{2} BV:{3} M:{4} V:{5} FV:{6}", IsConstant, ConstValue, Keyframes, BaseValue, Multiplier, Values, FloatValues); + } } } diff --git a/NW4RTools/Models/ByteCode.cs b/NW4RTools/Models/ByteCode.cs index 6df6dee..62968e9 100644..100755 --- a/NW4RTools/Models/ByteCode.cs +++ b/NW4RTools/Models/ByteCode.cs @@ -50,7 +50,8 @@ namespace NW4RTools.Models { public UInt16 MaterialID; public UInt16 ShapeID; - public UInt16 NodeID; + public UInt16 NodeID;
+ public byte Unk; } // According to BrawlLib: in NodeMix, this is for "primary influences" diff --git a/NW4RTools/NW4RTools.csproj b/NW4RTools/NW4RTools.csproj index bcdc413..50b4320 100644 --- a/NW4RTools/NW4RTools.csproj +++ b/NW4RTools/NW4RTools.csproj @@ -1,94 +1,89 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProductVersion>9.0.21022</ProductVersion> - <SchemaVersion>2.0</SchemaVersion> - <ProjectGuid>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</ProjectGuid> - <OutputType>Library</OutputType> - <RootNamespace>NW4RTools</RootNamespace> - <AssemblyName>NW4RTools</AssemblyName> - <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug</OutputPath> - <DefineConstants>DEBUG</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>none</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Release</OutputPath> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <ConsolePause>false</ConsolePause> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - </PropertyGroup> - <ItemGroup> - <Reference Include="System" /> - <Reference Include="System.Xml" /> - <Reference Include="System.Drawing" /> - <Reference Include="System.Core" /> - <Reference Include="OpenTK"> - <HintPath>..\ExternalLibs\OpenTK.dll</HintPath> - </Reference> - </ItemGroup> - <ItemGroup> - <Compile Include="AssemblyInfo.cs" /> - <Compile Include="Logger.cs" /> - <Compile Include="InputStream.cs" /> - <Compile Include="Types.cs" /> - <Compile Include="ResDict.cs" /> - <Compile Include="BrresReader.cs" /> - <Compile Include="ResFile.cs" /> - <Compile Include="Models\Model.cs" /> - <Compile Include="Models\ByteCode.cs" /> - <Compile Include="Models\Node.cs" /> - <Compile Include="Util\IOrderedDictionary.cs" /> - <Compile Include="Util\OrderedDictionary.cs" /> - <Compile Include="Models\VertexData.cs" /> - <Compile Include="Models\Material.cs" /> - <Compile Include="Models\Shader.cs" /> - <Compile Include="Models\Shape.cs" /> - <Compile Include="Models\TextureInfo.cs" /> - <Compile Include="Enums.cs" /> - <Compile Include="DisplayList.cs" /> - <Compile Include="VertexSettings.cs" /> - <Compile Include="Util\collada_schema_1_4.cs" /> - <Compile Include="Texture.cs" /> - <Compile Include="Misc.cs" /> - <Compile Include="Models\OpenGL\GLModel.cs" /> - <Compile Include="Models\OpenGL\GLTexture.cs" /> - <Compile Include="Models\OpenGL\GLDisplayList.cs" /> - <Compile Include="BrresWriter.cs" /> - <Compile Include="OutputStream.cs" /> - <Compile Include="Models\Animation\ColorAnim.cs" /> - <Compile Include="Models\Animation\CharacterAnim.cs" /> - <Compile Include="Models\Animation\TextureSRTAnim.cs" /> - <Compile Include="Util\NVDXT.cs" /> - <Compile Include="ColladaExporter.cs" /> - <Compile Include="ObjExporter.cs" /> - <Compile Include="ObjImporter.cs" /> - <Compile Include="DisplayListWriter.cs" /> - <Compile Include="Models\Animation\Shared.cs" /> - </ItemGroup> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <ItemGroup> - <Folder Include="Models\" /> - <Folder Include="Util\" /> - <Folder Include="Models\OpenGL\" /> - <Folder Include="Models\Animation\" /> - </ItemGroup> - <ItemGroup> - <None Include="OpenTK.dll.config"> - <CopyToOutputDirectory>Always</CopyToOutputDirectory> - </None> - </ItemGroup> -</Project> +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <RootNamespace>NW4RTools</RootNamespace>
+ <AssemblyName>NW4RTools</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG DEV_DONT_MODIFY_TEXTURE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <ConsolePause>false</ConsolePause>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <DefineConstants>DEV_DONT_MODIFY_TEXTURE</DefineConstants>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Drawing" />
+ <Reference Include="System.Core" />
+ <Reference Include="OpenTK">
+ <HintPath>..\ExternalLibs\OpenTK.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="AssemblyInfo.cs" />
+ <Compile Include="Logger.cs" />
+ <Compile Include="InputStream.cs" />
+ <Compile Include="Types.cs" />
+ <Compile Include="ResDict.cs" />
+ <Compile Include="BrresReader.cs" />
+ <Compile Include="ResFile.cs" />
+ <Compile Include="Models\Model.cs" />
+ <Compile Include="Models\ByteCode.cs" />
+ <Compile Include="Models\Node.cs" />
+ <Compile Include="Util\IOrderedDictionary.cs" />
+ <Compile Include="Util\OrderedDictionary.cs" />
+ <Compile Include="Models\VertexData.cs" />
+ <Compile Include="Models\Material.cs" />
+ <Compile Include="Models\Shader.cs" />
+ <Compile Include="Models\Shape.cs" />
+ <Compile Include="Models\TextureInfo.cs" />
+ <Compile Include="Enums.cs" />
+ <Compile Include="DisplayList.cs" />
+ <Compile Include="VertexSettings.cs" />
+ <Compile Include="Util\collada_schema_1_4.cs" />
+ <Compile Include="Texture.cs" />
+ <Compile Include="Misc.cs" />
+ <Compile Include="Models\OpenGL\GLModel.cs" />
+ <Compile Include="Models\OpenGL\GLTexture.cs" />
+ <Compile Include="Models\OpenGL\GLDisplayList.cs" />
+ <Compile Include="BrresWriter.cs" />
+ <Compile Include="OutputStream.cs" />
+ <Compile Include="Models\Animation\ColorAnim.cs" />
+ <Compile Include="Models\Animation\CharacterAnim.cs" />
+ <Compile Include="Models\Animation\TextureSRTAnim.cs" />
+ <Compile Include="Util\NVDXT.cs" />
+ <Compile Include="ColladaExporter.cs" />
+ <Compile Include="ObjExporter.cs" />
+ <Compile Include="ObjImporter.cs" />
+ <Compile Include="DisplayListWriter.cs" />
+ <Compile Include="Models\Animation\Shared.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <None Include="OpenTK.dll.config">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+</Project>
\ No newline at end of file diff --git a/NW4RTools/Texture.cs b/NW4RTools/Texture.cs index a328648..f2cf231 100644 --- a/NW4RTools/Texture.cs +++ b/NW4RTools/Texture.cs @@ -63,6 +63,9 @@ namespace NW4RTools { } +#if DEV_DONT_MODIFY_TEXTURE + public byte[][] ImageData; +#endif public Bitmap[] Images; @@ -95,7 +98,11 @@ namespace NW4RTools { } public int GetDataSize(int imageID) { +#if DEV_DONT_MODIFY_TEXTURE + return ImageData[imageID].Length; +#else return GetDataSize(Images[imageID].Width, Images[imageID].Height, Format); +#endif } @@ -104,9 +111,9 @@ namespace NW4RTools { int blkWidth = info.TexelWidth; int blkHeight = info.TexelHeight; - int width = Images[imageID].Width; - int height = Images[imageID].Height; - var bits = Images[imageID].LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + int width = Images [imageID].Width; + int height = Images [imageID].Height; + var bits = Images [imageID].LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); var data = new OutputStream(ByteEndian.BigEndian); @@ -238,7 +245,7 @@ namespace NW4RTools { for (int iBlockX = 0; iBlockX < 2; iBlockX++) { var block = Util.NVDXT.compressDXT1a((Util.NVDXT.ARGBPixel*)bits.Scan0, x + (iBlockX * 4), y + (iBlockY * 4), - bits.Width, bits.Height); + (bits.Stride / 4), bits.Height); data.WriteUInt16(block.Color0); data.WriteUInt16(block.Color1); diff --git a/NW4RTools/Types.cs b/NW4RTools/Types.cs index 619303e..9bfa0db 100644 --- a/NW4RTools/Types.cs +++ b/NW4RTools/Types.cs @@ -1,6 +1,16 @@ using System; namespace NW4RTools { + public struct Pair<TOne, TTwo> { + public TOne one; + public TTwo two; + + public Pair(TOne _one, TTwo _two) { + one = _one; + two = _two; + } + } + public enum ByteEndian { LittleEndian, BigEndian diff --git a/TestApp/Main.cs b/TestApp/Main.cs index 9d01957..a61577f 100644..100755 --- a/TestApp/Main.cs +++ b/TestApp/Main.cs @@ -9,10 +9,14 @@ using OpenTK.Graphics.OpenGL; namespace TestApp { class MainClass { public static void Main(string[] args) { + blah(args); + return; + string mdlPath = "/home/me/Games/Newer/ModelRev/"; //skawo(); return; - oldBehaviour(); return; + oldBehaviour(); + return; // Going to create a model! @@ -116,6 +120,29 @@ namespace TestApp { //ColladaExporter.WriteModel(objFile, rf, "lift_han_wood_M"); //objFile.Close(); } + + + 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/"; + + ResFile rf = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "CS_W4.brres")); + File.WriteAllBytes(mdlPath + "CS_W4a.brres", BrresWriter.WriteFile(rf)); + + if (args.Length > 0 && args [0] == "a") { + ResFile rf2 = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "CS_W4a.brres")); + File.WriteAllBytes(mdlPath + "CS_W4b.brres", BrresWriter.WriteFile(rf2)); + ResFile rf3 = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "CS_W4b.brres")); + File.WriteAllBytes(mdlPath + "CS_W4c.brres", BrresWriter.WriteFile(rf3)); + ResFile rf4 = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "CS_W4c.brres")); + File.WriteAllBytes(mdlPath + "CS_W4d.brres", BrresWriter.WriteFile(rf4)); + ResFile rf5 = BrresReader.LoadFile(File.ReadAllBytes(mdlPath + "CS_W4d.brres")); + File.WriteAllBytes(mdlPath + "CS_W4e.brres", BrresWriter.WriteFile(rf5)); + } + } } } diff --git a/TestApp/TestApp.csproj b/TestApp/TestApp.csproj index d21525e..4168d26 100644 --- a/TestApp/TestApp.csproj +++ b/TestApp/TestApp.csproj @@ -1,51 +1,51 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProductVersion>9.0.21022</ProductVersion> - <SchemaVersion>2.0</SchemaVersion> - <ProjectGuid>{3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}</ProjectGuid> - <OutputType>Exe</OutputType> - <RootNamespace>TestApp</RootNamespace> - <AssemblyName>TestApp</AssemblyName> - <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug</OutputPath> - <DefineConstants>DEBUG</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>none</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Release</OutputPath> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - <Externalconsole>true</Externalconsole> - </PropertyGroup> - <ItemGroup> - <Reference Include="System" /> - <Reference Include="OpenTK, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\ExternalLibs\OpenTK.dll</HintPath> - </Reference> - </ItemGroup> - <ItemGroup> - <Compile Include="Main.cs" /> - <Compile Include="AssemblyInfo.cs" /> - <Compile Include="RenderWindow.cs" /> - </ItemGroup> - <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <ItemGroup> - <ProjectReference Include="..\NW4RTools\NW4RTools.csproj"> - <Project>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</Project> - <Name>NW4RTools</Name> - </ProjectReference> - </ItemGroup> -</Project> +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.21022</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{3A064CD8-CFAD-412D-986F-ED7D2D54CDB1}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>TestApp</RootNamespace>
+ <AssemblyName>TestApp</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Externalconsole>true</Externalconsole>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>none</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <Externalconsole>true</Externalconsole>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="OpenTK">
+ <HintPath>..\ExternalLibs\OpenTK.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Drawing" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Main.cs" />
+ <Compile Include="AssemblyInfo.cs" />
+ <Compile Include="RenderWindow.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+ <ItemGroup>
+ <ProjectReference Include="..\NW4RTools\NW4RTools.csproj">
+ <Project>{A9C9FABD-0A5F-4DAB-979D-9F288F96866F}</Project>
+ <Name>NW4RTools</Name>
+ </ProjectReference>
+ </ItemGroup>
+</Project>
\ No newline at end of file |