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();
            }
        }