private static void AddOneScaleFrame(bool useScaleFrames, AquaMotion.KeyData node, Assimp.Node aiNode = null) { if (useScaleFrames) { AquaMotion.MKEY sclKeys = new AquaMotion.MKEY(); sclKeys.keyType = 3; sclKeys.dataType = 1; sclKeys.keyCount = 1; if (aiNode == null) { sclKeys.vector4Keys = new List <Vector4>() { new Vector4(1.0f, 1.0f, 1.0f, 0) }; } else { Matrix4x4.Invert(GetMat4FromAssimpMat4(aiNode.Transform), out Matrix4x4 mat4); sclKeys.vector4Keys = new List <Vector4>() { new Vector4(mat4.M11, mat4.M22, mat4.M33, 0) }; } node.keyData.Add(sclKeys); } }
private static void AddOnePosFrame(AquaMotion.KeyData node, Assimp.Node aiNode, float baseScale) { AquaMotion.MKEY posKeys = new AquaMotion.MKEY(); posKeys.keyType = 1; posKeys.dataType = 1; posKeys.keyCount = 1; var mat4 = GetMat4FromAssimpMat4(aiNode.Transform); posKeys.vector4Keys = new List <Vector4>() { new Vector4(mat4.M14 * baseScale, mat4.M24 * baseScale, mat4.M34 * baseScale, 0) }; node.keyData.Add(posKeys); }
private static void AddOneRotFrame(AquaMotion.KeyData node, Assimp.Node aiNode) { AquaMotion.MKEY rotKeys = new AquaMotion.MKEY(); rotKeys.keyType = 2; rotKeys.dataType = 3; rotKeys.keyCount = 1; Matrix4x4.Invert(GetMat4FromAssimpMat4(aiNode.Transform), out Matrix4x4 mat4); var quat = Quaternion.CreateFromRotationMatrix(mat4); rotKeys.vector4Keys = new List <Vector4>() { new Vector4(quat.X, quat.Y, quat.Z, quat.W) }; node.keyData.Add(rotKeys); }
public void UpdateToNGSPlayerMotion(AquaMotion motion) { //Add the nodeTreeFlag if it's there AquaMotion.KeyData nodeTree = null; if (motion.motionKeys[motion.motionKeys.Count - 1].mseg.nodeName.GetString().Contains("NodeTreeFlag")) { nodeTree = motion.motionKeys[motion.motionKeys.Count - 1]; } //Remove to the old count while (motion.motionKeys.Count > oldRange) { motion.motionKeys.RemoveAt(motion.motionKeys.Count - 1); } //Go through and add NGS nodes for (int i = oldRange; i < endRange + 1; i++) { var keySet = new AquaMotion.KeyData(); keySet.mseg.nodeDataCount = 3; keySet.mseg.nodeId = i; keySet.mseg.nodeType = 2; keySet.mseg.nodeName = AquaCommon.PSO2String.GeneratePSO2String(nodeNames[i]); var pos = new AquaMotion.MKEY(); var rot = new AquaMotion.MKEY(); var scale = new AquaMotion.MKEY(); //If the key isn't in there, set to the default. Else set based on the old id if (!remapDict.ContainsKey(i)) { // position pos.dataType = 1; pos.keyCount = 1; pos.keyType = 1; pos.vector4Keys.Add(defaultPos[i]); // rotation rot.dataType = 3; rot.keyCount = 1; rot.keyType = 2; rot.vector4Keys.Add(new Vector4(defaultRots[i].X, defaultRots[i].Y, defaultRots[i].Z, defaultRots[i].W)); // scale scale.dataType = 1; scale.keyCount = 1; scale.keyType = 3; scale.vector4Keys.Add(new Vector4(1.0f, 1.0f, 1.0f, 0)); } else { for (int keyData = 0; keyData < motion.motionKeys[remapDict[i]].keyData.Count; keyData++) { switch (motion.motionKeys[remapDict[i]].keyData[keyData].keyType) { case 1: pos = motion.motionKeys[remapDict[i]].keyData[keyData]; if (motion.motionKeys[remapDict[i]].keyData[keyData].vector4Keys.Count > 1) { for (int frameId = 0; frameId < motion.motionKeys[remapDict[i]].keyData[keyData].vector4Keys.Count; frameId++) { var frame = motion.motionKeys[remapDict[i]].keyData[keyData].vector4Keys[frameId]; frame = frame - defaultPos[remapDict[i]]; motion.motionKeys[remapDict[i]].keyData[keyData].vector4Keys[frameId] = frame + defaultPos[i]; } } break; case 2: rot = motion.motionKeys[remapDict[i]].keyData[keyData]; //Extract and apply rotation for every frame for (int frameId = 0; frameId < motion.motionKeys[remapDict[i]].keyData[keyData].vector4Keys.Count; frameId++) { var frame = motion.motionKeys[remapDict[i]].keyData[keyData].vector4Keys[frameId]; var quat = new Quaternion(frame.X, frame.Y, frame.Z, frame.W); quat = quat * Quaternion.Inverse(defaultRots[remapDict[i]]); quat = quat * defaultRots[i]; motion.motionKeys[remapDict[i]].keyData[keyData].vector4Keys[frameId] = new Vector4(quat.X, quat.Y, quat.Z, quat.W); } break; case 3: scale = motion.motionKeys[remapDict[i]].keyData[keyData]; break; } } //Check if anything was left out and set to defaults if so if (pos.keyType == 0) { pos.dataType = 1; pos.keyCount = 1; pos.keyType = 1; pos.vector4Keys.Add(defaultPos[i]); } if (rot.keyType == 0) { rot.dataType = 3; rot.keyCount = 1; rot.keyType = 2; rot.vector4Keys.Add(new Vector4(defaultRots[i].X, defaultRots[i].Y, defaultRots[i].Z, defaultRots[i].W)); } if (scale.keyType == 0) { scale.dataType = 1; scale.keyCount = 1; scale.keyType = 3; scale.vector4Keys.Add(new Vector4(1.0f, 1.0f, 1.0f, 0)); } } keySet.keyData.Add(pos); keySet.keyData.Add(rot); keySet.keyData.Add(scale); motion.motionKeys.Add(keySet); } if (nodeTree != null) { motion.motionKeys.Add(nodeTree); } else { //Generate NodeTreeFlag nodeTree = new AquaMotion.KeyData(); nodeTree.mseg.nodeDataCount = 3; nodeTree.mseg.nodeId = 0; nodeTree.mseg.nodeType = 16; nodeTree.mseg.nodeName = AquaCommon.PSO2String.GeneratePSO2String("__NodeTreeFlag__"); for (int set = 0; set < 3; set++) { var mkey = new AquaMotion.MKEY(); mkey.dataType = 5; mkey.keyCount = motion.moHeader.endFrame + 1; mkey.keyType = 16 + set; for (int key = 0; key < mkey.keyCount; key++) { if (key == 0) { mkey.frameTimings.Add(0x9); } else if (key == mkey.keyCount - 1) { mkey.frameTimings.Add((ushort)((key * 0x10) + 0xA)); } else { mkey.frameTimings.Add((ushort)((key * 0x10) + 0x8)); } mkey.intKeys.Add(0x31); } nodeTree.keyData.Add(mkey); } } motion.moHeader.nodeCount = motion.motionKeys.Count; }
public static void AssimpAQMConvert(string initialFilePath, bool playerExport, bool useScaleFrames, float scaleFactor) { float baseScale = 1f / 100f * scaleFactor; //We assume that this will be 100x the true scale because 1 unit to 1 meter isn't the norm Assimp.AssimpContext context = new Assimp.AssimpContext(); context.SetConfig(new Assimp.Configs.FBXPreservePivotsConfig(false)); Assimp.Scene aiScene = context.ImportFile(initialFilePath, Assimp.PostProcessSteps.Triangulate | Assimp.PostProcessSteps.JoinIdenticalVertices | Assimp.PostProcessSteps.FlipUVs); string inputFilename = Path.GetFileNameWithoutExtension(initialFilePath); List <string> aqmNames = new List <string>(); //Leave off extensions in case we want this to be .trm later List <AquaMotion> aqmList = new List <AquaMotion>(); Dictionary <int, Assimp.Node> aiNodes = GetAnimatedNodes(aiScene); var nodeKeys = aiNodes.Keys.ToList(); nodeKeys.Sort(); int animatedNodeCount = nodeKeys.Last() + 1; AquaUtil aqua = new AquaUtil(); for (int i = 0; i < aiScene.Animations.Count; i++) { if (aiScene.Animations[i] == null) { continue; } AquaMotion aqm = new AquaMotion(); var anim = aiScene.Animations[i]; int animEndFrame = 0; //We'll fill this later. Assumes frame 0 to be the start if (anim.Name != null && anim.Name != "") { //Make sure we're not overwriting anims that somehow have duplicate names if (aqmNames.Contains(anim.Name)) { aqmNames.Add($"Anim_{i}_" + anim.Name + ".aqm"); } else { aqmNames.Add(anim.Name + ".aqm"); } } else { aqmNames.Add($"Anim_{i}_" + inputFilename + ".aqm"); } aqm.moHeader = new AquaMotion.MOHeader(); //Get anim fps if (anim.TicksPerSecond == 0) { //Default to 30 aqm.moHeader.frameSpeed = 30; } else { aqm.moHeader.frameSpeed = (float)anim.TicksPerSecond; } aqm.moHeader.unkInt0 = 2; //Always, always 2 for NIFL aqm.moHeader.variant = 0x2; //These are flags for the animation to tell the game what type it is. Since this is a skeletal animation, we always put 2 here. //If it's a player one specifically, the game generally adds 0x10 to this. aqm.moHeader.nodeCount = animatedNodeCount; if (playerExport) { aqm.moHeader.variant += 0x10; aqm.moHeader.nodeCount++; //Add an extra for the nodeTreeFlag 'node' } aqm.moHeader.testString.SetString("test"); //Set this ahead of time in case these are out of order aqm.motionKeys = new List <AquaMotion.KeyData>(new AquaMotion.KeyData[aqm.moHeader.nodeCount]); //Nodes foreach (var animNode in anim.NodeAnimationChannels) { if (animNode == null) { continue; } int id = GetNodeNumber(animNode.NodeName); var node = aqm.motionKeys[id] = new AquaMotion.KeyData(); node.mseg.nodeName.SetString(animNode.NodeName); node.mseg.nodeId = id; node.mseg.nodeType = 2; node.mseg.nodeDataCount = useScaleFrames ? 3 : 2; if (animNode.HasPositionKeys) { AquaMotion.MKEY posKeys = new AquaMotion.MKEY(); posKeys.keyType = 1; posKeys.dataType = 1; var first = true; foreach (var pos in animNode.PositionKeys) { posKeys.vector4Keys.Add(new Vector4(pos.Value.X * baseScale, pos.Value.Y * baseScale, pos.Value.Z * baseScale, 0)); //Account for first frame difference if (first) { posKeys.frameTimings.Add(1); first = false; } else { posKeys.frameTimings.Add((ushort)(pos.Time * 0x10)); } posKeys.keyCount++; } posKeys.frameTimings[posKeys.keyCount - 1] += 2; //Account for final frame bitflags animEndFrame = Math.Max(animEndFrame, posKeys.keyCount); node.keyData.Add(posKeys); } if (animNode.HasRotationKeys) { AquaMotion.MKEY rotKeys = new AquaMotion.MKEY(); rotKeys.keyType = 2; rotKeys.dataType = 3; var first = true; foreach (var rot in animNode.RotationKeys) { rotKeys.vector4Keys.Add(new Vector4(rot.Value.X, rot.Value.Y, rot.Value.Z, rot.Value.W)); //Account for first frame difference if (first) { rotKeys.frameTimings.Add(1); first = false; } else { rotKeys.frameTimings.Add((ushort)(rot.Time * 0x10)); } rotKeys.keyCount++; } rotKeys.frameTimings[rotKeys.keyCount - 1] += 2; //Account for final frame bitflags animEndFrame = Math.Max(animEndFrame, rotKeys.keyCount); node.keyData.Add(rotKeys); } if (animNode.HasScalingKeys) { AquaMotion.MKEY sclKeys = new AquaMotion.MKEY(); sclKeys.keyType = 2; sclKeys.dataType = 3; var first = true; foreach (var scl in animNode.ScalingKeys) { sclKeys.vector4Keys.Add(new Vector4(scl.Value.X, scl.Value.Y, scl.Value.Z, 0)); //Account for first frame difference if (first) { sclKeys.frameTimings.Add(1); first = false; } else { sclKeys.frameTimings.Add((ushort)(scl.Time * 0x10)); } sclKeys.keyCount++; } sclKeys.frameTimings[sclKeys.keyCount - 1] += 2; //Account for final frame bitflags animEndFrame = Math.Max(animEndFrame, sclKeys.keyCount); node.keyData.Add(sclKeys); } } //NodeTreeFlag if (playerExport) { var node = aqm.motionKeys[aqm.motionKeys.Count - 1] = new AquaMotion.KeyData(); node.mseg.nodeName.SetString("__NodeTreeFlag__"); node.mseg.nodeId = aqm.motionKeys.Count - 1; node.mseg.nodeType = 0x10; node.mseg.nodeDataCount = useScaleFrames ? 3 : 2; //Position AquaMotion.MKEY posKeys = new AquaMotion.MKEY(); posKeys.keyType = 0x10; posKeys.dataType = 5; posKeys.keyCount = animEndFrame + 1; for (int frame = 0; frame < posKeys.keyCount; frame++) { if (frame == 0) { posKeys.frameTimings.Add(0x9); } else if (frame == posKeys.keyCount - 1) { posKeys.frameTimings.Add((ushort)((frame * 0x10) + 0xA)); } else { posKeys.frameTimings.Add((ushort)((frame * 0x10) + 0x8)); } posKeys.intKeys.Add(0x31); } node.keyData.Add(posKeys); //Rotation AquaMotion.MKEY rotKeys = new AquaMotion.MKEY(); rotKeys.keyType = 0x11; rotKeys.dataType = 5; rotKeys.keyCount = animEndFrame + 1; for (int frame = 0; frame < rotKeys.keyCount; frame++) { if (frame == 0) { rotKeys.frameTimings.Add(0x9); } else if (frame == rotKeys.keyCount - 1) { rotKeys.frameTimings.Add((ushort)((frame * 0x10) + 0xA)); } else { rotKeys.frameTimings.Add((ushort)((frame * 0x10) + 0x8)); } rotKeys.intKeys.Add(0x31); } node.keyData.Add(rotKeys); //Scale if (useScaleFrames) { AquaMotion.MKEY sclKeys = new AquaMotion.MKEY(); sclKeys.keyType = 0x12; sclKeys.dataType = 5; sclKeys.keyCount = animEndFrame + 1; for (int frame = 0; frame < sclKeys.keyCount; frame++) { if (frame == 0) { sclKeys.frameTimings.Add(0x9); } else if (frame == sclKeys.keyCount - 1) { sclKeys.frameTimings.Add((ushort)((frame * 0x10) + 0xA)); } else { sclKeys.frameTimings.Add((ushort)((frame * 0x10) + 0x8)); } sclKeys.intKeys.Add(0x31); } node.keyData.Add(sclKeys); } } //Sanity check foreach (var aiPair in aiNodes) { var node = aqm.motionKeys[aiPair.Key]; var aiNode = aiPair.Value; if (node == null) { node = aqm.motionKeys[aiPair.Key] = new AquaMotion.KeyData(); node.mseg.nodeName.SetString(aiNode.Name); node.mseg.nodeId = aiPair.Key; node.mseg.nodeType = 2; node.mseg.nodeDataCount = useScaleFrames ? 3 : 2; //Position AddOnePosFrame(node, aiNode, baseScale); //Rotation AddOneRotFrame(node, aiNode); //Scale AddOneScaleFrame(useScaleFrames, node); } else { if (node.keyData[0].vector4Keys.Count < 1) { AddOnePosFrame(node, aiNode, baseScale); } if (node.keyData[1].vector4Keys.Count < 1) { AddOneRotFrame(node, aiNode); } if (useScaleFrames && node.keyData[2].vector4Keys.Count < 1) { AddOneScaleFrame(useScaleFrames, node, aiNode); } } } aqmList.Add(aqm); } for (int i = 0; i < aqmList.Count; i++) { var aqm = aqmList[i]; AquaUtil.AnimSet set = new AquaUtil.AnimSet(); set.anims.Add(aqm); aqua.aquaMotions.Add(set); aqua.WriteNIFLMotion(initialFilePath + "_" + aqmNames[i]); aqua.aquaMotions.Clear(); } }