/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.

#include "CryLegacy_precompiled.h"

#include "Helper.h"

#include "ControllerOpt.h"

#include "CharacterInstance.h"
#include "SkeletonAnim.h"
#include "SkeletonPose.h"

#include "Command_Buffer.h"
#include "Command_Commands.h"

#include "Skeleton.h"
#include "LoaderDBA.h"
#include "CharacterManager.h"


extern float g_YLine;

namespace Command {
    void LoadControllers(const GlobalAnimationHeaderCAF& rGAH, const Command::CState& state, IController** controllers)
    {
        memset(controllers, 0, state.m_jointCount * sizeof(IController*));

        if (rGAH.IsAssetOnDemand() && !rGAH.IsAssetLoaded())
        {
            CryWarning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, "LoadControllers: Skipping asset because it's not loaded (%s)", rGAH.GetFilePath());
            return;
        }

        if (rGAH.m_nControllers2)
        {
            if (rGAH.m_nControllers == 0)
            {
                uint32 dba_exists = 0;
                if (rGAH.m_FilePathDBACRC32)
                {
                    size_t numDBA_Files = g_AnimationManager.m_arrGlobalHeaderDBA.size();
                    for (uint32 d = 0; d < numDBA_Files; d++)
                    {
                        CGlobalHeaderDBA& pGlobalHeaderDBA = g_AnimationManager.m_arrGlobalHeaderDBA[d];
                        if (rGAH.m_FilePathDBACRC32 != pGlobalHeaderDBA.m_FilePathDBACRC32)
                        {
                            continue;
                        }

                        dba_exists++;
                        break;
                    }
                }

                if (dba_exists)
                {
                    if (Console::GetInst().ca_DebugCriticalErrors)
                    {
                        //this case is virtually impossible, unless something went wrong with a DBA or maybe a CAF in a DBA was compressed to death and all controllers removed
                        //  const char* mname = state.m_pInstance->GetFilePath();
                        //  f32 fColor[4] = {1,1,0,1};
                        //  g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.2f, fColor, false,"model: %s",mname);
                        //  g_YLine+=0x10;
                        //  g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.3f, fColor, false,"No Controllers found in Asset: %02x %08x %s",rGAH.m_nControllers2,rGAH.m_FilePathDBACRC32,rGAH.m_FilePath.c_str() );
                        //  g_YLine+=23.0f;
                        CryFatalError("CryAnimation: No Controllers found in Asset: %s", rGAH.GetFilePath());
                    }

                    g_pISystem->Warning(VALIDATOR_MODULE_ANIMATION, VALIDATOR_WARNING, VALIDATOR_FLAG_FILE, 0, "No Controllers found in Asset: %s", rGAH.GetFilePath());
                }

                return; //return and don't play animation, because we don't have any controllers
            }
        }

        uint32* pLodJointMask = NULL;
        uint32 lodJointMaskCount = 0;
        if (state.m_lod > 0)
        {
            if (uint32 lodCount = state.m_pDefaultSkeleton->m_arrAnimationLOD.size())
            {
                uint32 lod = state.m_lod;
                if (lod > lodCount)
                {
                    lod = lodCount;
                }
                --lod;

                pLodJointMask = &state.m_pDefaultSkeleton->m_arrAnimationLOD[lod][0];
                lodJointMaskCount = state.m_pDefaultSkeleton->m_arrAnimationLOD[lod].size();
            }
        }

        const CDefaultSkeleton::SJoint* pModelJoint = &state.m_pDefaultSkeleton->m_arrModelJoints[0];
        uint32 jointCount = state.m_jointCount;
        for (uint32 i = 0; i < jointCount; ++i)
        {
            uint32 crc32 = pModelJoint[i].m_nJointCRC32;
            if (pLodJointMask)
            {
                if (Helper::FindFromSorted<uint32>(pLodJointMask, lodJointMaskCount, crc32) == NULL)
                {
                    continue;
                }
            }

            if (!state.IsJointActive(crc32))
            {
                continue;
            }

            controllers[i] = rGAH.GetControllerByJointCRC32(pModelJoint[i].m_nJointCRC32);
        }
    }



    void ClearPoseBuffer::Execute(const CState& state, void* buffers[]) const
    {
        const ClearPoseBuffer& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer is only ever supposed to have the values Command::TargetBuffer or Command::TmpBuffer
        // (Other commands do this with a flag instead of passing a possibly invalid index ...)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        uint32 numJoints = state.m_jointCount;

        //clear buffer for the relative pose
        f32 fIsIndentity = f32(ac.m_nPoseInit);
        QuatT* parrRelPoseDst = (QuatT*) CBTemp[ac.m_TargetBuffer + 0];
        for (uint32 j = 0; j < numJoints; j++)
        {
            parrRelPoseDst[j].q.v.x = 0.0f;
            parrRelPoseDst[j].q.v.y = 0.0f;
            parrRelPoseDst[j].q.v.z = 0.0f;
            parrRelPoseDst[j].q.w = fIsIndentity;
            parrRelPoseDst[j].t.x = 0.0f;
            parrRelPoseDst[j].t.y = 0.0f;
            parrRelPoseDst[j].t.z = 0.0f;
        }


        //clear buffer joint states
        JointState* parrStatusDst = (JointState*)CBTemp[ac.m_TargetBuffer + 1];
        for (uint32 j = 0; j < numJoints; j++)
        {
            parrStatusDst[j] = ac.m_nJointStatus;
        }


        //clear buffer for weight mask (this is only used for partial body blends)
        Vec2*   parrWeightMask = (Vec2*)CBTemp[ac.m_TargetBuffer + 2];
        if (parrWeightMask == 0)
        {
            return; //not initialized
        }
        for (uint32 j = 0; j < numJoints; j++)
        {
            parrWeightMask[j] = Vec2(ZERO);
        }
    }


    //this function operates on "rGlobalAnimHeaderCAF"
    void SampleAddAnimFull::Execute(const CState& state, void* buffers[]) const
    {
        float fColor[4] = {1, 0, 0, 1};
        const SampleAddAnimFull& ac = *this;
        void** CBTemp = buffers;

        const int32 nBufferID = (ac.m_flags & Flag_TmpBuffer) ? Command::TmpBuffer : Command::TargetBuffer;
        QuatT*      parrRelPoseDst  = (QuatT*)  CBTemp[nBufferID + 0];
        JointState* parrStatusDst       = (JointState*)CBTemp[nBufferID + 1];

        uint32 numJoints = state.m_jointCount;
        JointState& getOPResult = parrStatusDst[0];

        CAnimationSet* pAnimationSet = state.m_pDefaultSkeleton->m_pAnimationSet;
        const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(ac.m_nEAnimID);

        // PARANOIA: SampleAddAnim is only called from locations that have already pulled the MAG and checked the asset type
        CRY_ASSERT(pMAG && pMAG->m_nAssetType == CAF_File);

        int32 nEGlobalID = pMAG->m_nGlobalAnimId;
        if (nEGlobalID < 0 || nEGlobalID >= g_AnimationManager.m_arrGlobalCAF.size())
        {
            // Unexpected: Header references an asset that doesn't exist
            return;
        }

        const GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[nEGlobalID];
        if (rCAF.IsAssetOnDemand())
        {
            if (rCAF.IsAssetLoaded() == 0)
            {
                int nCurrentFrameID = g_pCharacterManager->m_nUpdateCounter;
                g_pIRenderer->Draw2dLabel(1, g_YLine, 2.3f, fColor, false, "CryAnimation: Asset Not Loaded: %s   nCurrentFrameID: %d  Weight: %f", rCAF.GetFilePath(), nCurrentFrameID, ac.m_fWeight);
            }
        }

        //if we have an animation in the base-layer, then we ALWAYS initialize all joints
        memset(parrStatusDst, eJS_Position | eJS_Orientation | eJS_Scale, numJoints);


        PREFAST_SUPPRESS_WARNING(6255)
        IController * *parrController = (IController**)alloca(state.m_jointCount * sizeof(IController*));
        LoadControllers(rCAF, state, parrController);

        PREFAST_SUPPRESS_WARNING(6255)
        QuatT * qtemp = (QuatT*)alloca(state.m_jointCount * sizeof(QuatT));
        const QuatT* parrDefJoints = state.m_pDefaultSkeleton->m_poseDefaultData.GetJointsRelativeMain();
        {
            DEFINE_PROFILER_SECTION("cryMemcpy");
            cryMemcpy(&qtemp[0], parrDefJoints, numJoints * sizeof(QuatT));
        }

        f32 fDuration       = max(1.0f / rCAF.GetSampleRate(), rCAF.m_fTotalDuration);
        f32 fKeyTime1       = rCAF.NTime2KTime(1);
        f32 fKeyTimeNew = rCAF.NTime2KTime(ac.m_fETimeNew);

        uint32 nStartJoint = 0;
        if (ac.m_flags & Flag_ADMotion)
        {
            nStartJoint = 1;

            // When root motion is enabled, set to first frame of the animation, rather than just setting identity.
            // This is tolerant to animations with non-identity root transforms on first frame, even if the root isn't animated.
            if (parrController[0])
            {
                parrController[0]->GetOP(rCAF.NTime2KTime(0.f), parrRelPoseDst[0].q, parrRelPoseDst[0].t);
            }
        }


        //-------------------------------------------------------------------------------------
        //----             evaluate all controllers for this animation                    -----
        //-------------------------------------------------------------------------------------
        uint32 nAdditiveAnimation = rCAF.IsAssetAdditive();
        if (nAdditiveAnimation)
        {
            //additive asset
            for (uint32 j = nStartJoint; j < numJoints; j++)
            {
                if (parrController[j])
                {
                    JointState ops = parrController[j]->GetOP(fKeyTimeNew, qtemp[j].q, qtemp[j].t);
                    if (ops & eJS_Orientation)
                    {
                        qtemp[j].q = qtemp[j].q * parrDefJoints[j].q;
                    }
                    if (ops & eJS_Position)
                    {
                        qtemp[j].t = qtemp[j].t + parrDefJoints[j].t;
                    }
                }

                ANIM_ASSERT(qtemp[j].q.IsUnit());
                ANIM_ASSERT(qtemp[j].q.IsValid());
                ANIM_ASSERT(qtemp[j].t.IsValid());

                parrRelPoseDst[j].q += ac.m_fWeight * qtemp[j].q * fsgnnz(parrRelPoseDst[j].q | qtemp[j].q);
                parrRelPoseDst[j].t += ac.m_fWeight * qtemp[j].t;
            }
        }
        else
        {
            //overwrite asset
            for (uint32 j = nStartJoint; j < numJoints; j++)
            {
                if (parrController[j])
                {
                    parrController[j]->GetOP(fKeyTimeNew, qtemp[j].q, qtemp[j].t);
                }

                ANIM_ASSERT(qtemp[j].q.IsUnit());
                ANIM_ASSERT(qtemp[j].q.IsValid());
                ANIM_ASSERT(qtemp[j].t.IsValid());

                parrRelPoseDst[j].q += ac.m_fWeight * qtemp[j].q * fsgnnz(parrRelPoseDst[j].q | qtemp[j].q);
                parrRelPoseDst[j].t += ac.m_fWeight * qtemp[j].t;
            }
        }

#ifdef ANIM_ASSERT_ENABLED
        for (uint32 j = 0; j < numJoints; j++)
        {
            ANIM_ASSERT(parrRelPoseDst[j].q.IsValid());
            ANIM_ASSERT(parrRelPoseDst[j].t.IsValid());
        }
#endif

        g_pCharacterManager->g_SkeletonUpdates++;
    }

    //this function operates on "rGlobalAnimHeaderAIM"
    void SampleAddPoseFull::Execute(const CState& state, void* buffers[]) const
    {
        const SampleAddPoseFull& ac = *this;
        void** CBTemp = buffers;

        const int32 nBufferID = (ac.m_flags & SampleAddAnimFull::Flag_TmpBuffer) ? Command::TmpBuffer : Command::TargetBuffer;
        QuatT*      parrRelPoseDst  = (QuatT*)  CBTemp[nBufferID + 0];
        JointState* parrStatusDst       = (JointState*)CBTemp[nBufferID + 1];

        uint32 numJoints = state.m_jointCount;
        JointState& getOPResult = parrStatusDst[0];

        CAnimationSet* pAnimationSet = state.m_pDefaultSkeleton->m_pAnimationSet;
        const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(ac.m_nEAnimID);

        // PARANOIA: SampleAddPoseFull is only called from locations that have already pulled the MAG and checked the asset type
        CRY_ASSERT(pMAG && pMAG->m_nAssetType == AIM_File);

        int32 nEGlobalID = pMAG->m_nGlobalAnimId;
        if (nEGlobalID < 0 || nEGlobalID >= g_AnimationManager.m_arrGlobalAIM.size())
        {
            // Header references an asset that doesn't exist
            return;
        }

        const GlobalAnimationHeaderAIM& rGlobalAnimHeaderAIM = g_AnimationManager.m_arrGlobalAIM[nEGlobalID];

        memset(parrStatusDst, 0xff, numJoints);
        const CDefaultSkeleton::SJoint* pModelJoint = &state.m_pDefaultSkeleton->m_arrModelJoints[0];
        PREFAST_SUPPRESS_WARNING(6255)
        IController * *parrController = (IController**)alloca(state.m_jointCount * sizeof(IController*));
        memset(parrController, 0, state.m_jointCount * sizeof(IController*));
        for (uint32 i = 0; i < numJoints; ++i)
        {
            parrController[i] = rGlobalAnimHeaderAIM.GetControllerByJointCRC32(pModelJoint[i].m_nJointCRC32);
        }

        PREFAST_SUPPRESS_WARNING(6255)
        QuatT * qtemp = (QuatT*)alloca(state.m_jointCount * sizeof(QuatT));
        const QuatT* parrDefJoints = state.m_pDefaultSkeleton->m_poseDefaultData.GetJointsRelativeMain();
        cryMemcpy(&qtemp[0], parrDefJoints, numJoints * sizeof(QuatT));

        f32 fKeyTimeNew = rGlobalAnimHeaderAIM.NTime2KTime(ac.m_fETimeNew);

        //-------------------------------------------------------------------------------------
        //----             evaluate all controllers for this animation                    -----
        //-------------------------------------------------------------------------------------
        for (uint32 j = 0; j < numJoints; j++)
        {
            if (parrController[j])
            {
                parrController[j]->GetOP(fKeyTimeNew, qtemp[j].q, qtemp[j].t);
            }
            ANIM_ASSERT(qtemp[j].q.IsUnit());
            ANIM_ASSERT(qtemp[j].q.IsValid());
            ANIM_ASSERT(qtemp[j].t.IsValid());

            parrRelPoseDst[j].q += ac.m_fWeight * qtemp[j].q * fsgnnz(parrRelPoseDst[j].q | qtemp[j].q);
            parrRelPoseDst[j].t += ac.m_fWeight * qtemp[j].t;
        }

#ifdef ANIM_ASSERT_ENABLED
        for (uint32 j = 0; j < numJoints; j++)
        {
            CRY_ASSERT(parrRelPoseDst[j].q.IsValid());
            CRY_ASSERT(parrRelPoseDst[j].t.IsValid());
        }
#endif
    }

    //reads content from m_SourceBuffer, multiplies the pose by a blend weight, and adds the result to the m_TargetBuffer
    void AddPoseBuffer::Execute(const CState& state, void* buffers[]) const
    {
        const AddPoseBuffer& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer and ac.m_SourceBuffer are only ever supposed to be assigned this way
        // (why these are parameters is anyone's guess)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_SourceBuffer == Command::TmpBuffer);

        QuatT*      parrRelPoseSrc      = (QuatT*)  CBTemp[ac.m_SourceBuffer + 0];
        JointState* parrStatusSrc       = (JointState*)CBTemp[ac.m_SourceBuffer + 1];

        QuatT*      parrRelPoseDst      = (QuatT*)  CBTemp[ac.m_TargetBuffer + 0];
        JointState* parrStatusDst       = (JointState*)CBTemp[ac.m_TargetBuffer + 1];

        f32 t = ac.m_fWeight;

        uint32 numJoints = state.m_jointCount;
        for (uint32 i = 0; i < numJoints; i++)
        {
            parrRelPoseDst[i].q += parrRelPoseSrc[i].q * t;
            parrRelPoseDst[i].t += parrRelPoseSrc[i].t * t;
            parrStatusDst[i]    |= parrStatusSrc[i];
        }
#ifdef ANIM_ASSERT_ENABLED
        for (uint32 j = 0; j < numJoints; j++)
        {
            CRY_ASSERT(parrRelPoseDst[j].q.IsValid());
            CRY_ASSERT(parrRelPoseDst[j].t.IsValid());
        }
#endif
    }

    void NormalizeFull::Execute(const CState& state, void* buffers[]) const
    {
        const NormalizeFull& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer is only ever supposed to have the values Command::TargetBuffer or Command::TmpBuffer
        // (Other commands do this with a flag instead of passing a possibly invalid index ...)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        QuatT*          parrRelPoseDst  = (QuatT*)  CBTemp[ac.m_TargetBuffer + 0];
        JointState*     parrStatusDst       = (JointState*)CBTemp[ac.m_TargetBuffer + 1];

        f32 fDotLocator = fabsf(parrRelPoseDst[0].q | parrRelPoseDst[0].q);
        if (fDotLocator > 0.0001f)
        {
            parrRelPoseDst[0].q *= isqrt_tpl(fDotLocator);
        }
        else
        {
            parrRelPoseDst[0].q.SetIdentity();
        }

        uint32 numJoints = state.m_jointCount;
        for (uint32 i = 1; i < numJoints; i++)
        {
            f32 dot = fabsf(parrRelPoseDst[i].q | parrRelPoseDst[i].q);
            if (dot > 0.0001f)
            {
                parrRelPoseDst[i].q *= isqrt_tpl(dot);
            }
            else
            {
                parrRelPoseDst[i].q.SetIdentity();
            }

            ANIM_ASSERT(parrRelPoseDst[i].q.IsUnit());
        }
    }

    void ScaleUniformFull::Execute(const CState& state, void* buffers[]) const
    {
        const ScaleUniformFull& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer is only ever supposed to have the values Command::TargetBuffer or Command::TmpBuffer
        // (Other commands do this with a flag instead of passing a possibly invalid index ...)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        QuatT*          parrRelPoseDst  = (QuatT*)    CBTemp[ac.m_TargetBuffer + 0];
        JointState*     parrStatusDst       = (JointState*)  CBTemp[ac.m_TargetBuffer + 1];
        uint32 numJoints = state.m_jointCount;
        for (uint32 j = 0; j < numJoints; j++)
        {
            if (parrStatusDst[j] & eJS_Position)
            {
                parrRelPoseDst[j].t *= ac.m_fScale;
            }
        }
#ifdef ANIM_ASSERT_ENABLED
        for (uint32 j = 0; j < numJoints; j++)
        {
            ANIM_ASSERT(parrRelPoseDst[j].q.IsValid());
            ANIM_ASSERT(parrRelPoseDst[j].t.IsValid());
        }
#endif
    }

#if defined(USE_PROTOTYPE_ABS_BLENDING)
    struct SAbsoluteTransform
    {
        Quat prevAbs;
        Quat newAbs;
    };
#endif //!(USE_PROTOTYPE_ABS_BLENDING)

    //replace handposes in BSpaces
    void SampleReplaceAnimPart::Execute(const CState& state, void* buffers[]) const
    {
        const SampleReplaceAnimPart& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer is only ever supposed to have the values Command::TargetBuffer or Command::TmpBuffer
        // (Other commands do this with a flag instead of passing a possibly invalid index ...)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        QuatT*          parrRelPoseDst  = (QuatT*)      CBTemp[ac.m_TargetBuffer + 0];

        CAnimationSet* pAnimationSet = state.m_pDefaultSkeleton->m_pAnimationSet;
        const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(ac.m_nEAnimID);

        // PARANOIA: SampleReplaceAnimPart is only called from locations that have already pulled the MAG and checked the asset type
        CRY_ASSERT(pMAG && pMAG->m_nAssetType == CAF_File);

        int32 nEGlobalID = pMAG->m_nGlobalAnimId;
        if (nEGlobalID < 0 || nEGlobalID >= g_AnimationManager.m_arrGlobalCAF.size())
        {
            // Header references an asset that doesn't exist
            return;
        }

        // cache vars to local pointers (spares using this pointer everytime)
        uint32 numJoints = state.m_jointCount;
        GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[nEGlobalID];

        PREFAST_SUPPRESS_WARNING(6255)
        IController * *parrController = (IController**)alloca(state.m_jointCount * sizeof(IController*));
        LoadControllers(rCAF, state, parrController);

        f32 fKeyTimeNew = rCAF.NTime2KTime(ac.m_fAnimTime);
        uint32 nAdditiveAnimation = rCAF.IsAssetAdditive();
        //-------------------------------------------------------------------------------------
        //----             evaluate all controllers for this animation                    -----
        //-------------------------------------------------------------------------------------
        g_pCharacterManager->g_SkeletonUpdates++;

        if (nAdditiveAnimation)
        {
            //add full Additive-pose to the base-pose
            for (uint32 j = 1; j < numJoints; j++)
            {
                if (parrController[j])
                {
                    Quat rot;
                    Vec3 pos;
                    JointState jointState = parrController[j]->GetOP(fKeyTimeNew, rot, pos);
                    if (jointState & eJS_Orientation)
                    {
                        parrRelPoseDst[j].q = rot * parrRelPoseDst[j].q;
                    }
                    if (jointState & eJS_Position)
                    {
                        parrRelPoseDst[j].t += pos;
                    }
                }
            }
        }
        else
        {
            const QuatT* parrDefJoints = state.m_pDefaultSkeleton->m_poseDefaultData.GetJointsRelativeMain();
            //replace base-pose with controllers in Override-Animations
            for (uint32 j = 1; j < numJoints; j++)
            {
                if (parrController[j])
                {
                    Quat rot;
                    Vec3 pos;
                    JointState jointState = parrController[j]->GetOP(fKeyTimeNew, rot, pos);

                    if (jointState & eJS_Orientation)
                    {
                        parrRelPoseDst[j].q = rot;
                    }
                    if (jointState & eJS_Position)
                    {
                        parrRelPoseDst[j].t = pos;
                    }
                }
            }
        }
    }



    void SampleAddAnimPart::Execute(const CState& state, void* buffers[]) const
    {
        const SampleAddAnimPart& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer is only ever supposed to have the values Command::TargetBuffer or Command::TmpBuffer
        // (Other commands do this with a flag instead of passing a possibly invalid index ...)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        QuatT*          parrRelPoseDst  = (QuatT*)      CBTemp[ac.m_TargetBuffer + 0];
        JointState* parrStatusDst       = (JointState*) CBTemp[ac.m_TargetBuffer + 1];
        Vec2*               parrJWeightsDst = (Vec2*)       CBTemp[ac.m_TargetBuffer + 2];
        if (parrJWeightsDst == 0)
        {
            return; //not initialized
        }
        CAnimationSet* pAnimationSet = state.m_pDefaultSkeleton->m_pAnimationSet;
        const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(ac.m_nEAnimID);

        // PARANOIA: SampleAddAnimPart is only called from locations that have already pulled the MAG and checked the asset type
        CRY_ASSERT(pMAG && pMAG->m_nAssetType == CAF_File);

        int32 nEGlobalID = pMAG->m_nGlobalAnimId;
        if (nEGlobalID < 0 || nEGlobalID >= g_AnimationManager.m_arrGlobalCAF.size())
        {
            // Header references an asset that doesn't exist
            return;
        }

        // cache vars to local pointers (spares using this pointer everytime)
        uint32 numJoints = state.m_jointCount;
        CRY_ASSERT(ac.m_fAnimTime >= 0.0f && ac.m_fAnimTime <= 1.0f);

        GlobalAnimationHeaderCAF& rCAF = g_AnimationManager.m_arrGlobalCAF[nEGlobalID];
        uint32 nAdditiveAnimation = rCAF.IsAssetAdditive();

        PREFAST_SUPPRESS_WARNING(6255)
        IController * *parrController = (IController**)alloca(state.m_jointCount * sizeof(IController*));
        LoadControllers(rCAF, state, parrController);

        f32 fKeyTimeNew = rCAF.NTime2KTime(ac.m_fAnimTime);

        //-------------------------------------------------------------------------------------
        //----             evaluate all controllers for this animation                    -----
        //-------------------------------------------------------------------------------------
        g_pCharacterManager->g_SkeletonUpdates++;

        if (nAdditiveAnimation)
        {
            //accumulate all Additive-Animations and store the result in a temp buffer
            for (uint32 j = 1; j < numJoints; j++)
            {
                if (parrController[j])
                {
                    Quat rot;
                    Vec3 pos;
                    JointState jointState = parrController[j]->GetOP(fKeyTimeNew, rot, pos);
                    if (jointState & eJS_Orientation)
                    {
                        parrJWeightsDst[j].x += ac.m_fWeight;
                        parrRelPoseDst[j].q = Quat::CreateNlerp(IDENTITY, rot, ac.m_fWeight) * parrRelPoseDst[j].q;
                    }
                    if (jointState & eJS_Position)
                    {
                        parrJWeightsDst[j].y += ac.m_fWeight;
                        parrRelPoseDst[j].t += pos * ac.m_fWeight;
                    }
                    parrStatusDst[j] |= jointState;
                }
            }
        }
#if defined(USE_PROTOTYPE_ABS_BLENDING)
        else if (ac.m_maskNumJoints > 0)
        {
            const strided_pointer<const int>& maskIDs           = ac.m_maskJointIDs;
            const strided_pointer<const float>& maskWeights = ac.m_maskJointWeights;

            const QuatT* parrRelPoseSrc = (const QuatT*)  CBTemp[ac.m_SourceBuffer + 0];

            const int32 numWeighted = ac.m_maskNumJoints;

            PREFAST_SUPPRESS_WARNING(6255)
            SAbsoluteTransform * pAbsoluteTransforms = (SAbsoluteTransform*)alloca(numWeighted * sizeof(SAbsoluteTransform));
            PREFAST_SUPPRESS_WARNING(6255)
            uint8 * pJointSettings = (uint8*)alloca(numJoints * sizeof(uint8));

            memset(pJointSettings, 0, numJoints * sizeof(uint8));

            JointState jointState = 0;
            const IController* pController;

            Skeleton::CPoseData* pPoseData = &state.m_pDefaultSkeleton->m_poseData;
            const QuatT* bindPoseTrans = pPoseData->GetJointsRelativeMain();

            //--- Calculate absolute rotations
            Quat rot;
            Vec3 pos;
            Quat rotAbsPrev(IDENTITY);
            Quat rotAbsNew(IDENTITY);
            Quat rotAbsPrevParent(IDENTITY);
            Quat rotAbsNewParent(IDENTITY);
            Quat rotAbsBlendedRot(IDENTITY);
            for (int32 i = 0; i < numWeighted; i++)
            {
                uint32 root = maskIDs[i];
                float weight = maskWeights[i];

                rotAbsPrevParent.SetIdentity();
                rotAbsNewParent.SetIdentity();

                bool isDone = false;
                for (int32 j = pModelJoint[root].m_idxParent; (j >= 0) && !isDone; j = pModelJoint[j].m_idxParent)
                {
                    for (int32 k = 0; k < i; k++)
                    {
                        if (j == maskIDs[k])
                        {
                            //--- Early exit, we have this already
                            rotAbsPrevParent = pAbsoluteTransforms[k].prevAbs * rotAbsPrevParent;
                            rotAbsNewParent  = pAbsoluteTransforms[k].newAbs * rotAbsNewParent;
                            isDone = true;
                            break;
                        }
                    }

                    if ((j >= 0) && !isDone)
                    {
                        rotAbsPrevParent = parrRelPoseSrc[j].q * rotAbsPrevParent;
                        jointState = 0;
                        if (parrController[j])
                        {
                            jointState = parrController[j]->GetO(fKeyTimeNew, rot);
                        }
                        if ((jointState & eJS_Orientation) == 0)
                        {
                            rot = bindPoseTrans[j].q;
                        }

                        rotAbsNewParent = rot * rotAbsNewParent;
                    }
                }

                //--- Now extract the root's animated state and setup the blended absolute rotation
                jointState = 0;
                if (parrController[root])
                {
                    jointState =  parrController[root]->GetOP(fKeyTimeNew, rot, pos);
                }
                if ((jointState & (eJS_Orientation | eJS_Position)) != (eJS_Orientation | eJS_Position))
                {
                    const QuatT& tran = bindPoseTrans[root];

                    if ((jointState & eJS_Orientation) == 0)
                    {
                        rot = tran.q;
                    }
                    if ((jointState & eJS_Position) == 0)
                    {
                        pos = tran.t;
                    }
                }

                rotAbsPrev = rotAbsPrevParent * parrRelPoseSrc[root].q;
                rotAbsNew  = rotAbsNewParent * rot;

                //--- Blend joint
                rotAbsBlendedRot.SetNlerp(rotAbsPrev, rotAbsNew, ac.m_fWeight * weight);

                //--- Store for future blends
                pAbsoluteTransforms[i].prevAbs = rotAbsBlendedRot;
                pAbsoluteTransforms[i].newAbs  = rotAbsNew;

                JointState& RESTRICT_REFERENCE rStatusDstRoot = parrStatusDst[root];
                parrRelPoseDst[root].t += (pos * (ac.m_fWeight * weight));
                parrRelPoseDst[root].q += (!rotAbsPrevParent * rotAbsNew) * (ac.m_fWeight * weight);
                parrJWeightsDst[root].x += (ac.m_fWeight * weight);
                parrJWeightsDst[root].y += (ac.m_fWeight * weight);
                rStatusDstRoot |= (jointState | eJS_Orientation);

                pJointSettings[root] = (weight >= 0.999f) ? 1 : 2;
            }

            for (uint32 j = 1; j < numJoints; j++)
            {
                if (pJointSettings[j] == 0)
                {
                    QuatT& RESTRICT_REFERENCE rRelPoseDst = parrRelPoseDst[j];
                    JointState& RESTRICT_REFERENCE rStatusDst = parrStatusDst[j];

                    int parent = pModelJoint[j].m_idxParent;
                    if ((parent >= -1) && (pJointSettings[pModelJoint[j].m_idxParent] == 1))
                    {
                        pJointSettings[j] = 1;
                        jointState = 0;

                        pController = parrController[j];

                        if (pController)
                        {
                            jointState = pController->GetOP(fKeyTimeNew, rot, pos);
                            rStatusDst |= jointState;
                        }
                        if ((jointState & (eJS_Orientation | eJS_Position)) != (eJS_Orientation | eJS_Position))
                        {
                            const QuatT& tran = bindPoseTrans[j];

                            if ((jointState & eJS_Orientation) == 0)
                            {
                                rot = tran.q;
                            }
                            if ((jointState & eJS_Position) == 0)
                            {
                                pos = tran.t;
                            }
                        }

                        rRelPoseDst.q += rot * fsgnnz(rRelPoseDst[j].q | rot) * ac.m_fWeight;
                        rRelPoseDst.t += pos * ac.m_fWeight;
                        rStatusDst |= jointState;

                        parrJWeightsDst[j].x += ac.m_fWeight;
                        parrJWeightsDst[j].y += ac.m_fWeight;
                    }
                }
            }
        }
#endif //!USE_PROTOTYPE_ABS_BLENDING
        else
        {
            const QuatT* parrDefJoints = state.m_pDefaultSkeleton->m_poseDefaultData.GetJointsRelativeMain();
            //accumulate all Override-Animations and store the result in a temp buffer
            for (uint32 j = 1; j < numJoints; j++)
            {
                if (parrController[j])
                {
                    Quat rot;
                    Vec3 pos;
                    JointState jointState = parrController[j]->GetOP(fKeyTimeNew, rot, pos);

                    if (jointState & eJS_Orientation)
                    {
                        parrJWeightsDst[j].x += ac.m_fWeight;
                        parrRelPoseDst[j].q += rot * fsgnnz(parrRelPoseDst[j].q | rot) * ac.m_fWeight;
                    }
                    if (jointState & eJS_Position)
                    {
                        parrJWeightsDst[j].y += ac.m_fWeight;
                        parrRelPoseDst[j].t += pos * ac.m_fWeight;
                    }
                    parrStatusDst[j] |= jointState;
                }
            }
        }


#ifdef ANIM_ASSERT_ENABLED
        uint32 o = 0;
        uint32 p = 0;
        for (uint32 j = 0; j < numJoints; j++)
        {
            if (parrStatusDst[j] & eJS_Orientation)
            {
                ANIM_ASSERT(parrRelPoseDst[j].q.IsValid());
                o++;
            }
            if (parrStatusDst[j] & eJS_Position)
            {
                ANIM_ASSERT(parrRelPoseDst[j].t.IsValid());
                p++;
            }
        }
#endif
    }



    void PerJointBlending::Execute(const CState& state, void* buffers[]) const
    {
        const PerJointBlending& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer is only ever supposed to have the values Command::TargetBuffer or Command::TmpBuffer
        // (Other commands do this with a flag instead of passing a possibly invalid index ...)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        //this is source-buffer No.1
        QuatT* parrRelPoseLayer         = (QuatT*)CBTemp[ac.m_SourceBuffer + 0];
        JointState* parrStatusLayer = (JointState*)CBTemp[ac.m_SourceBuffer + 1];
        Vec2* parrWeightsLayer          = (Vec2*)CBTemp[ac.m_SourceBuffer + 2];
        if (parrWeightsLayer == 0)
        {
            return; //not initialized
        }
        //this is Source-Buffer No.2 and also the Target-Buffer
        QuatT* parrRelPoseBase          = (QuatT*)CBTemp[ac.m_TargetBuffer + 0];

        uint32 numJoints = state.m_jointCount;
        if (ac.m_BlendMode)
        {
            for (uint32 j = 0; j < numJoints; j++)
            {
                //Additive Blending
                if (parrStatusLayer[j] & eJS_Orientation)
                {
                    parrRelPoseBase[j].q = Quat::CreateNlerp(IDENTITY, parrRelPoseLayer[j].q, parrWeightsLayer[j].x) * parrRelPoseBase[j].q;
                }
                if (parrStatusLayer[j] & eJS_Position)
                {
                    parrRelPoseBase[j].t += Vec3::CreateLerp(ZERO, parrRelPoseLayer[j].t, parrWeightsLayer[j].y);
                }
            }
        }
        else
        {
            for (uint32 j = 0; j < numJoints; j++)
            {
                //Override Blending
                if (parrStatusLayer[j] & eJS_Orientation)
                {
                    parrRelPoseBase[j].q = (parrRelPoseBase[j].q * (1.0f - parrWeightsLayer[j].x) + parrRelPoseLayer[j].q * fsgnnz(parrRelPoseBase[j].q | parrRelPoseLayer[j].q)).GetNormalized(); //Quaternion LERP
                }
                if (parrStatusLayer[j] & eJS_Position)
                {
                    parrRelPoseBase[j].t = parrRelPoseBase[j].t * (1.0f - parrWeightsLayer[j].y) +  parrRelPoseLayer[j].t;//Vector LERP
                }
            }
        }
    }


    //used to playback the frames in a CAF-file which stored is in GlobalAnimationHeaderAIM
    void SamplePosePart::Execute(const CState& state, void* buffers[]) const
    {
        const SamplePosePart& ac = *this;
        void** CBTemp = buffers;

        // PARANOIA: ac.m_TargetBuffer is only ever supposed to have the values Command::TargetBuffer or Command::TmpBuffer
        // (Other commands do this with a flag instead of passing a possibly invalid index ...)
        CRY_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        QuatT* parrRelPoseDst = (QuatT*)CBTemp[ac.m_TargetBuffer + 0];
        JointState* parrStatusDst = (JointState*)CBTemp[ac.m_TargetBuffer + 1];

        CAnimationSet* pAnimationSet = state.m_pDefaultSkeleton->m_pAnimationSet;
        const ModelAnimationHeader* pMAG = pAnimationSet->GetModelAnimationHeader(ac.m_nEAnimID);

        // PARANOIA: SamplePosePart is only called from locations that have already pulled the MAG and checked the asset type
        CRY_ASSERT(pMAG && pMAG->m_nAssetType == AIM_File);

        int32 nEGlobalID = pMAG->m_nGlobalAnimId;
        if (nEGlobalID < 0 || nEGlobalID >= g_AnimationManager.m_arrGlobalAIM.size())
        {
            // Header references an asset that doesn't exist
            return;
        }

        // cache vars to local pointers (spares using this pointer everytime)
        uint32 numJoints                = state.m_jointCount;
        CRY_ASSERT(ac.m_fAnimTime >= 0.0f && ac.m_fAnimTime <= 1.0f);


        GlobalAnimationHeaderAIM& rGlobalAnimHeader = g_AnimationManager.m_arrGlobalAIM[nEGlobalID];

        const CDefaultSkeleton::SJoint* pModelJoint = &state.m_pDefaultSkeleton->m_arrModelJoints[0];

        //f32 fFadeColor[4] = {1,0,0,1};
        //if (nAdditiveAnimation)
        //  g_pIRenderer->Draw2dLabel( 1,300, 6.0f, fFadeColor, false,"SingleEvaluation" ); g_YLine+=0x18;

        // f32 fFadeColor[4] = {1,0,0,1};
        // g_pIRenderer->Draw2dLabel( 1,g_YLine, 2.0f, fFadeColor, false,"SamplePosePart: %f",ac.m_fWeight); g_YLine+=0x18;

        //-------------------------------------------------------------------------------------
        //----             evaluate all controllers for this animation                    -----
        //-------------------------------------------------------------------------------------
        g_pCharacterManager->g_SkeletonUpdates++;

        f32 fKeyTimeNew = rGlobalAnimHeader.NTime2KTime(ac.m_fAnimTime);
        //override animations
        for (uint32 j = 1; j < numJoints; j++)
        {
            IController* pController = rGlobalAnimHeader.GetControllerByJointCRC32(pModelJoint[j].m_nJointCRC32);
            if (pController)
            {
                QuatT& RESTRICT_REFERENCE rRelPoseDst = parrRelPoseDst[j];
                JointState& RESTRICT_REFERENCE rStatusDst = parrStatusDst[j];
                Quat rot;
                Vec3 pos;
                JointState jointState = pController->GetOP(fKeyTimeNew, rot, pos);
                //Overwrite Mode
                if (jointState & eJS_Orientation)
                {
                    if (rStatusDst & eJS_Orientation)
                    {
                        rRelPoseDst.q.SetNlerp(rRelPoseDst.q, rot, ac.m_fWeight);
                    }
                    else
                    {
                        rRelPoseDst.q = rot;
                    }
                }
                if (jointState & eJS_Position)
                {
                    if (rStatusDst & eJS_Position)
                    {
                        rRelPoseDst.t.SetLerp(rRelPoseDst.t, pos, ac.m_fWeight);
                    }
                    else
                    {
                        rRelPoseDst.t = pos;
                    }
                }
                rStatusDst |= jointState;
            }
        }


#ifdef ANIM_ASSERT_ENABLED
        uint32 o = 0;
        uint32 p = 0;
        for (uint32 j = 0; j < numJoints; j++)
        {
            if (parrStatusDst[j] & eJS_Orientation)
            {
                ANIM_ASSERT(parrRelPoseDst[j].q.IsValid());
                o++;
            }
            if (parrStatusDst[j] & eJS_Position)
            {
                ANIM_ASSERT(parrRelPoseDst[j].t.IsValid());
                p++;
            }
        }
#endif
    }

    void PoseModifier::Execute(const CState& state, void* buffers[]) const
    {
        const PoseModifier& ac = *this;
        void** CBTemp = buffers;

        SAnimationPoseModifierParams params;
        params.pCharacterInstance = state.m_pInstance;
        params.pPoseData = state.m_pPoseData;
        params.timeDelta = state.m_pInstance->m_fOriginalDeltaTime;
        params.location = state.m_location;
        ac.m_pPoseModifier->Execute(params);
        /*
        #ifdef ANIM_ASSERT_ENABLED
            for (uint32 j=0; j<params.jointCount; j++)
            {
                ANIM_ASSERT( params.pPoseRelative[j].q.IsUnit() );
                ANIM_ASSERT( params.pPoseAbsolute[j].q.IsUnit() );
                ANIM_ASSERT( params.pPoseRelative[j].IsValid() );
                ANIM_ASSERT( params.pPoseAbsolute[j].IsValid() );
            }
        #endif
        */
    }



    void VerifyFull::Execute(const CState& state, void* buffers[]) const
    {
#ifdef ANIM_ASSERT_ENABLED
        const VerifyFull& ac = *this;
        void** CBTemp = buffers;

        ANIM_ASSERT(ac.m_TargetBuffer == Command::TargetBuffer || ac.m_TargetBuffer == Command::TmpBuffer);

        QuatT*          parrRelPoseDst  = (QuatT*)    CBTemp[ac.m_TargetBuffer + 0];
        JointState*     parrStatusDst       = (JointState*)  CBTemp[ac.m_TargetBuffer + 1];
        uint32 numJoints = state.m_jointCount;
        for (uint32 j = 0; j < numJoints; j++)
        {
            ANIM_ASSERT(parrRelPoseDst[j].q.IsValid());
            ANIM_ASSERT(parrRelPoseDst[j].t.IsValid());
        }
#endif
    }
} // namespace Command