summaryrefslogtreecommitdiff
path: root/NW4RTools/BrresWriter.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-x[-rw-r--r--]NW4RTools/BrresWriter.cs967
1 files changed, 957 insertions, 10 deletions
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;