Example #1
0
 // Get / Set
 //------------------------------------------
 public bool IsTimelineLayerContain(apAnimTimelineLayer animTimelineLayer)
 {
     return(_layers.Contains(animTimelineLayer));
 }
        //---------------------------------------------------
        // 계산함수 - KeyFrame
        //---------------------------------------------------
        private void CalculateWeight_KeyFrame()
        {
            if (_keyParamSetGroup == null || _keyParamSetGroup._keyAnimTimelineLayer == null)
            {
                return;
            }
            apAnimTimelineLayer timlineLayer = _keyParamSetGroup._keyAnimTimelineLayer;

            apOptCalculatedResultParam.OptParamKeyValueSet curParamKeyValue = null;
            int curFrame = timlineLayer._parentAnimClip.CurFrame;

            bool isLoop = timlineLayer._parentAnimClip.IsLoop;

            //apAnimKeyframe firstKeyframe = timlineLayer._firstKeyFrame;
            //apAnimKeyframe lastKeyframe = timlineLayer._lastKeyFrame;


            _totalWeight = 0.0f;

            apAnimKeyframe curKeyframe  = null;
            apAnimKeyframe prevKeyframe = null;
            apAnimKeyframe nextKeyframe = null;

            //int indexOffsetA = 0;
            //int indexOffsetB = 0;
            int lengthFrames = timlineLayer._parentAnimClip.EndFrame - timlineLayer._parentAnimClip.StartFrame;
            int tmpCurFrame  = 0;

            //Debug.Log("Calculated--------------------------------");


            for (int i = 0; i < _subParamKeyValues.Count; i++)
            {
                curParamKeyValue               = _subParamKeyValues[i];
                curParamKeyValue._dist         = -10.0f;
                curParamKeyValue._isCalculated = false;
                curParamKeyValue._weight       = 0.0f;

                //유효하지 않은 키프레임이면 처리하지 않는다.
                if (curParamKeyValue._paramSet.SyncKeyframe == null ||
                    !curParamKeyValue._paramSet.SyncKeyframe._isActive)
                {
                    //Debug.Log("[" + i + "] Not Active or Null Keyframe");
                    continue;
                }

                curKeyframe  = curParamKeyValue._paramSet.SyncKeyframe;
                prevKeyframe = curParamKeyValue._paramSet.SyncKeyframe._prevLinkedKeyframe;
                nextKeyframe = curParamKeyValue._paramSet.SyncKeyframe._nextLinkedKeyframe;


                //1. 프레임이 같다. => 100%
                if (curFrame == curKeyframe._frameIndex ||
                    ((curKeyframe._isLoopAsStart || curKeyframe._isLoopAsEnd) && curFrame == curKeyframe._loopFrameIndex))
                {
                    curParamKeyValue._dist         = 0.0f;
                    curParamKeyValue._isCalculated = true;
                    curParamKeyValue._weight       = 1.0f;
                    _totalWeight += 1.0f;

                    //Debug.Log("[" + i + "] <Exact Frame>");
                }
                //else if(curFrame >= curKeyframe._activeFrameIndexMin &&
                //		curFrame < curKeyframe._frameIndex)
                else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Prev))
                {
                    //범위 안에 들었다. [Prev - Cur]
                    if (prevKeyframe != null)
                    {
                        //indexOffsetA = 0;
                        //indexOffsetB = 0;
                        //if(prevKeyframe._frameIndex > curKeyframe._frameIndex)
                        //{
                        //	//Loop인 경우 Prev가 더 클 수 있다.
                        //	indexOffsetA = -lengthFrames;
                        //}

                        tmpCurFrame = curFrame;
                        if (tmpCurFrame > curKeyframe._frameIndex)
                        {
                            tmpCurFrame -= lengthFrames;
                        }

                        //float itp = apAnimCurve.GetCurvedRelativeInterpolation(prevKeyframe._curveKey, curKeyframe._curveKey, curFrame, curKeyframe._curveKey._isPrevKeyUseDummyIndex, false);

                        //이전 코드
                        //_cal_itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, prevKeyframe._curveKey, tmpCurFrame, true);

                        //>> 다음 코드
                        _cal_itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrame, true);

                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = _cal_itp;
                        _totalWeight += _cal_itp;


                        //추가 : Rotation Bias
                        //Prev와 연결되었다면 Prev 설정을 적용한다.
                        if (curKeyframe._prevRotationBiasMode != apAnimKeyframe.ROTATION_BIAS.None)
                        {
                            curParamKeyValue.SetAnimRotationBias(curKeyframe._prevRotationBiasMode, curKeyframe._prevRotationBiasCount);
                        }
                        //Debug.Log("[" + i + "] [Prev ~ Cur] " + itp);
                        //Debug.Log("Prev ~ Next : " + itp);
                    }
                    else
                    {
                        //연결된게 없다면 이게 100% 가중치를 갖는다.
                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = 1.0f;
                        _totalWeight += 1.0f;
                        //Debug.Log("[" + i + "] [Prev ?? ~ Cur] 1.0");
                    }
                }
                //else if(curFrame > curKeyframe._frameIndex &&
                //		curFrame <= curKeyframe._activeFrameIndexMax)
                else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Next))
                {
                    //범위안에 들었다 [Cur - Next]
                    if (nextKeyframe != null)
                    {
                        //indexOffsetA = 0;
                        //indexOffsetB = 0;
                        //if(nextKeyframe._frameIndex < curKeyframe._frameIndex)
                        //{
                        //	//Loop인 경우 Next가 더 작을 수 있다.
                        //	indexOffsetB = lengthFrames;
                        //}

                        tmpCurFrame = curFrame;
                        if (tmpCurFrame < curKeyframe._frameIndex)
                        {
                            tmpCurFrame += lengthFrames;
                        }

                        //float itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, nextKeyframe._curveKey, curFrame, false, curKeyframe._curveKey._isNextKeyUseDummyIndex);

                        //이전 코드
                        //_cal_itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, nextKeyframe._curveKey, tmpCurFrame, false);

                        //>> 다음 코드
                        _cal_itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrame, false);

                        //itp = 1.0f - itp;//결과가 B에 맞추어지므로 여기서는 Reverse

                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = _cal_itp;
                        _totalWeight += _cal_itp;

                        //Debug.Log("[" + i + "] [Cur ~ Next] " + itp);

                        //추가 : Rotation Bias
                        //Next와 연결되었다면 Next 설정을 적용한다.
                        if (curKeyframe._nextRotationBiasMode != apAnimKeyframe.ROTATION_BIAS.None)
                        {
                            curParamKeyValue.SetAnimRotationBias(curKeyframe._nextRotationBiasMode, curKeyframe._nextRotationBiasCount);
                        }
                    }
                    else
                    {
                        //연결된게 없다면 이게 100% 가중치를 갖는다.
                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = 1.0f;
                        _totalWeight += 1.0f;

                        //Debug.Log("[" + i + "] [Cur ~ Next ??] 1.0");
                    }
                }
            }

            if (_totalWeight > 0.0f)
            {
                //Debug.Log("Result --------------------------------");
                for (int i = 0; i < _subParamKeyValues.Count; i++)
                {
                    curParamKeyValue = _subParamKeyValues[i];

                    if (curParamKeyValue._isCalculated)
                    {
                        curParamKeyValue._weight /= _totalWeight;
                        //Debug.Log("[" + curParamKeyValue._weight + "]");
                    }
                    else
                    {
                        curParamKeyValue._weight = 0.0f;
                    }
                }
                //Debug.Log("-------------------------------------");
            }
        }
        /// <summary>
        /// ParamSet간의 Weight를 계산한다. [Keyframe이 입력값인 경우]
        /// </summary>
        private void CalculateWeight_Keyframe()
        {
            if (_keyParamSetGroup == null || _keyParamSetGroup._keyAnimTimelineLayer == null)
            {
                return;
            }

            bool isPlayedAnimClip = false;            //<<이 코드가 추가됨

            if (_keyParamSetGroup._keyAnimClip._isSelectedInEditor)
            {
                _keyParamSetGroup._layerWeight = 1.0f;
                isPlayedAnimClip = true;
            }
            else
            {
                _keyParamSetGroup._layerWeight = 0.0f;
                isPlayedAnimClip = false;
            }

            apAnimTimelineLayer timlineLayer = _keyParamSetGroup._keyAnimTimelineLayer;

            apCalculatedResultParam.ParamKeyValueSet curParamKeyValue = null;
            int curFrame = timlineLayer._parentAnimClip.CurFrame;

            bool isLoop = timlineLayer._parentAnimClip.IsLoop;


            _totalWeight = 0.0f;

            apAnimKeyframe curKeyframe  = null;
            apAnimKeyframe prevKeyframe = null;
            apAnimKeyframe nextKeyframe = null;

            int lengthFrames = timlineLayer._parentAnimClip.EndFrame - timlineLayer._parentAnimClip.StartFrame;
            int tmpCurFrame  = 0;


            for (int i = 0; i < _subParamKeyValues.Count; i++)
            {
                curParamKeyValue               = _subParamKeyValues[i];
                curParamKeyValue._dist         = -10.0f;
                curParamKeyValue._isCalculated = false;

                //추가 11.29 : Animation Key 위치 타입이 추가되었다.
                curParamKeyValue._animKeyPos = apCalculatedResultParam.AnimKeyPos.NotCalculated;

                //유효하지 않은 키프레임이면 처리하지 않는다.
                if (curParamKeyValue._paramSet.SyncKeyframe == null ||
                    !curParamKeyValue._paramSet.SyncKeyframe._isActive ||
                    //!curParamKeyValue._isActive_InEditorExclusive
                    !curParamKeyValue.IsActive ||
                    !isPlayedAnimClip                     //<<애니메이션 재생 안될때는 여기서 생략이 되어야 하는데, 이게 왜 없었지;;
                    )
                {
                    //Debug.Log("[" + i + "] Not Active or Null Keyframe");
                    continue;
                }

                curKeyframe  = curParamKeyValue._paramSet.SyncKeyframe;
                prevKeyframe = curParamKeyValue._paramSet.SyncKeyframe._prevLinkedKeyframe;
                nextKeyframe = curParamKeyValue._paramSet.SyncKeyframe._nextLinkedKeyframe;


                //1. 프레임이 같다. => 100%
                if (curFrame == curKeyframe._frameIndex ||
                    ((curKeyframe._isLoopAsStart || curKeyframe._isLoopAsEnd) && curFrame == curKeyframe._loopFrameIndex))
                {
                    curParamKeyValue._dist         = 0.0f;
                    curParamKeyValue._isCalculated = true;
                    curParamKeyValue._weight       = 1.0f;
                    _totalWeight += 1.0f;

                    //추가 11.29 : AnimKeyPos - 동일 프레임
                    curParamKeyValue._animKeyPos = apCalculatedResultParam.AnimKeyPos.ExactKey;
                }
                //else if(curFrame >= curKeyframe._activeFrameIndexMin &&
                //		curFrame < curKeyframe._frameIndex)
                else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Prev))
                {
                    //범위 안에 들었다. [Prev - Cur]
                    if (prevKeyframe != null)
                    {
                        //indexOffsetA = 0;
                        //indexOffsetB = 0;
                        //if(prevKeyframe._frameIndex > curKeyframe._frameIndex)
                        //{
                        //	//Loop인 경우 Prev가 더 클 수 있다.
                        //	indexOffsetA = -lengthFrames;
                        //}

                        tmpCurFrame = curFrame;
                        if (tmpCurFrame > curKeyframe._frameIndex)
                        {
                            tmpCurFrame -= lengthFrames;
                        }

                        //float itp = apAnimCurve.GetCurvedRelativeInterpolation(prevKeyframe._curveKey, curKeyframe._curveKey, curFrame, curKeyframe._curveKey._isPrevKeyUseDummyIndex, false);
                        //float itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, prevKeyframe._curveKey, tmpCurFrame, true);

                        //>> 변경
                        float itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrame, true);

                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = itp;
                        _totalWeight += itp;

                        //추가 : Rotation Bias
                        //Prev와 연결되었다면 Prev 설정을 적용한다.
                        if (curKeyframe._prevRotationBiasMode != apAnimKeyframe.ROTATION_BIAS.None)
                        {
                            curParamKeyValue.SetAnimRotationBias(curKeyframe._prevRotationBiasMode, curKeyframe._prevRotationBiasCount);
                        }

                        //Debug.Log("[" + i + "] [Prev ~ Cur] " + itp);
                        //Debug.Log("Prev ~ Next : " + itp);

                        //추가 11.29 : AnimKeyPos - Next 프레임으로서 Prev 프레임과 보간이 된다.
                        curParamKeyValue._animKeyPos = apCalculatedResultParam.AnimKeyPos.NextKey;
                    }
                    else
                    {
                        //연결된게 없다면 이게 100% 가중치를 갖는다.
                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = 1.0f;
                        _totalWeight += 1.0f;
                        //Debug.Log("[" + i + "] [Prev ?? ~ Cur] 1.0");

                        //추가 11.29 : AnimKeyPos - 동일 프레임
                        curParamKeyValue._animKeyPos = apCalculatedResultParam.AnimKeyPos.ExactKey;
                    }
                }
                //else if(curFrame > curKeyframe._frameIndex &&
                //		curFrame <= curKeyframe._activeFrameIndexMax)
                else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Next))
                {
                    //범위안에 들었다 [Cur - Next]
                    if (nextKeyframe != null)
                    {
                        //indexOffsetA = 0;
                        //indexOffsetB = 0;
                        //if(nextKeyframe._frameIndex < curKeyframe._frameIndex)
                        //{
                        //	//Loop인 경우 Next가 더 작을 수 있다.
                        //	indexOffsetB = lengthFrames;
                        //}

                        tmpCurFrame = curFrame;
                        if (tmpCurFrame < curKeyframe._frameIndex)
                        {
                            tmpCurFrame += lengthFrames;
                        }

                        //float itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, nextKeyframe._curveKey, curFrame, false, curKeyframe._curveKey._isNextKeyUseDummyIndex);
                        //float itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, nextKeyframe._curveKey, tmpCurFrame, false);

                        //>> 변경
                        float itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrame, false);

                        //itp = 1.0f - itp;//결과가 B에 맞추어지므로 여기서는 Reverse

                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = itp;
                        _totalWeight += itp;

                        //추가 : Rotation Bias
                        //Next와 연결되었다면 Next 설정을 적용한다.
                        if (curKeyframe._nextRotationBiasMode != apAnimKeyframe.ROTATION_BIAS.None)
                        {
                            curParamKeyValue.SetAnimRotationBias(curKeyframe._nextRotationBiasMode, curKeyframe._nextRotationBiasCount);
                        }

                        //추가 11.29 : AnimKeyPos - Prev 프레임으로서 Next 프레임과 보간이 된다.
                        curParamKeyValue._animKeyPos = apCalculatedResultParam.AnimKeyPos.PrevKey;
                    }
                    else
                    {
                        //연결된게 없다면 이게 100% 가중치를 갖는다.
                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = 1.0f;
                        _totalWeight += 1.0f;

                        //추가 11.29 : AnimKeyPos - 동일 프레임
                        curParamKeyValue._animKeyPos = apCalculatedResultParam.AnimKeyPos.ExactKey;
                    }
                }
            }

            if (_totalWeight > 0.0f)
            {
                //Debug.Log("Result --------------------------------");
                //float prevWeight = 0.0f;
                for (int i = 0; i < _subParamKeyValues.Count; i++)
                {
                    curParamKeyValue = _subParamKeyValues[i];

                    if (curParamKeyValue._isCalculated)
                    {
                        curParamKeyValue._weight /= _totalWeight;
                        //Debug.Log("[" + curParamKeyValue._weight + "]");
                    }
                    else
                    {
                        curParamKeyValue._weight = 0.0f;
                    }
                }
                //Debug.Log("-------------------------------------");
            }
        }
 public void Link(apAnimTimelineLayer parentTimelineLayer)
 {
     _parentTimelineLayer = parentTimelineLayer;
     _parentTimelineLayer._parentAnimClip._portrait.RegistUniqueID(apIDManager.TARGET.AnimKeyFrame, _uniqueID);
 }
        /// <summary>
        /// [Editor] 업데이트 중 Control Param 제어 Timeline에 대해 업데이트 후 적용을 한다.
        /// [Runtime] isAdaptToWeight = false로 두고 나머지 처리를 한다.
        /// </summary>
        /// <param name="isAdaptToWeight1">[Editor]에서 Weight=1로 두고 적용을 한다</param>
        public void UpdateControlParam(bool isAdaptToWeight1, int optLayer = 0, float optWeight = 1.0f, apAnimPlayUnit.BLEND_METHOD optBlendMethod = apAnimPlayUnit.BLEND_METHOD.Interpolation)
        {
            if (_controlParamResult.Count == 0)
            {
                return;
            }

            for (int i = 0; i < _controlParamResult.Count; i++)
            {
                _controlParamResult[i].Init();
            }

            apAnimTimeline      timeline = null;
            apAnimTimelineLayer layer    = null;

            int   curFrame  = CurFrame;
            float curFrameF = CurFrameFloat;

            //apAnimKeyframe firstKeyframe = null;
            //apAnimKeyframe lastKeyframe = null;

            apAnimKeyframe curKeyframe  = null;
            apAnimKeyframe prevKeyframe = null;
            apAnimKeyframe nextKeyframe = null;

            int lengthFrames = _endFrame - _startFrame;
            int tmpCurFrame  = 0;

            apAnimControlParamResult cpResult = null;

            for (int iTL = 0; iTL < _timelines.Count; iTL++)
            {
                timeline = _timelines[iTL];
                if (timeline._linkType != LINK_TYPE.ControlParam)
                {
                    continue;
                }

                for (int iL = 0; iL < timeline._layers.Count; iL++)
                {
                    layer = timeline._layers[iL];
                    if (layer._linkedControlParam == null || layer._linkedControlParamResult == null)
                    {
                        continue;
                    }

                    cpResult = layer._linkedControlParamResult;

                    //firstKeyframe = layer._firstKeyFrame;
                    //lastKeyframe = layer._lastKeyFrame;

                    for (int iK = 0; iK < layer._keyframes.Count; iK++)
                    {
                        curKeyframe  = layer._keyframes[iK];
                        prevKeyframe = curKeyframe._prevLinkedKeyframe;
                        nextKeyframe = curKeyframe._nextLinkedKeyframe;

                        if (curFrame == curKeyframe._frameIndex ||
                            ((curKeyframe._isLoopAsStart || curKeyframe._isLoopAsEnd) && curKeyframe._loopFrameIndex == curFrame)
                            )
                        {
                            cpResult.SetKeyframeResult(curKeyframe, 1.0f);
                        }
                        else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Prev))
                        {
                            //Prev - Cur 범위 안에 들었다.
                            if (prevKeyframe != null)
                            {
                                //Prev 키가 있다면
                                tmpCurFrame = curFrame;
                                if (tmpCurFrame > curKeyframe._frameIndex)
                                {
                                    //한바퀴 돌았다면
                                    tmpCurFrame -= lengthFrames;
                                }

                                //TODO : 여길 나중에 "정식 Curve로 변경"할 것
                                //float itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, prevKeyframe._curveKey, tmpCurFrame, true);

                                //>> 변경
                                float itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrame, true);

                                cpResult.SetKeyframeResult(curKeyframe, itp);
                            }
                            else
                            {
                                //Prev 키가 없다면 이게 100%다
                                cpResult.SetKeyframeResult(curKeyframe, 1.0f);
                            }
                        }
                        else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Next))
                        {
                            //Cur - Next 범위 안에 들었다.
                            if (nextKeyframe != null)
                            {
                                //Next 키가 있다면
                                tmpCurFrame = curFrame;
                                if (tmpCurFrame < curKeyframe._frameIndex)
                                {
                                    //한바퀴 돌았다면
                                    tmpCurFrame += lengthFrames;
                                }

                                //TODO : 여길 나중에 "정식 Curve로 변경"할 것
                                //float itp = apAnimCurve.GetCurvedRelativeInterpolation(curKeyframe._curveKey, nextKeyframe._curveKey, tmpCurFrame, false);

                                //>> 변경
                                float itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrame, false);

                                cpResult.SetKeyframeResult(curKeyframe, itp);
                            }
                            else
                            {
                                //Prev 키가 없다면 이게 100%다
                                cpResult.SetKeyframeResult(curKeyframe, 1.0f);
                            }
                        }
                    }
                }
            }


            //Control Param에 적용을 해야한다.
            if (isAdaptToWeight1)
            {
                //Editor인 경우 Weight 1로 강제한다.
                for (int i = 0; i < _controlParamResult.Count; i++)
                {
                    _controlParamResult[i].AdaptToControlParam();
                }
            }
            else
            {
                //Runtime인 경우 지정된 Weight, Layer로 처리한다.
                for (int i = 0; i < _controlParamResult.Count; i++)
                {
                    //Debug.Log("AnimClip [" + _name + " > Control Param : " + _controlParamResult[i]._targetControlParam._keyName + " ]");
                    _controlParamResult[i].AdaptToControlParam_Opt(optWeight, optBlendMethod);
                }
            }
        }
        public void Bake(apPortrait portrait, apOptModifierUnitBase parentModifier, apModifierParamSetGroup srcParamSetGroup, bool isAnimated, bool isUseModMeshSet)
        {
            _portrait       = portrait;
            _parentModifier = parentModifier;

            _syncTarget = srcParamSetGroup._syncTarget;

            //_keyControlParamName = srcParamSetGroup._keyControlParamName;
            _keyControlParamID = srcParamSetGroup._keyControlParamID;
            _keyControlParam   = null;          //<<이건 링크로 해결하자

            //애니메이션 값도 넣어주자
            _keyAnimClipID          = srcParamSetGroup._keyAnimClipID;
            _keyAnimTimelineID      = srcParamSetGroup._keyAnimTimelineID;
            _keyAnimTimelineLayerID = srcParamSetGroup._keyAnimTimelineLayerID;
            _keyAnimClip            = null;
            _keyAnimTimeline        = null;
            _keyAnimTimelineLayer   = null;

            _paramSetList.Clear();

            for (int i = 0; i < srcParamSetGroup._paramSetList.Count; i++)
            {
                apModifierParamSet srcParamSet = srcParamSetGroup._paramSetList[i];

                apOptParamSet optParamSet = new apOptParamSet();
                optParamSet.LinkParamSetGroup(this, portrait);
                optParamSet.BakeModifierParamSet(srcParamSet, portrait, isUseModMeshSet);


                _paramSetList.Add(optParamSet);
            }

            _isEnabled   = srcParamSetGroup._isEnabled;
            _layerIndex  = srcParamSetGroup._layerIndex;
            _layerWeight = srcParamSetGroup._layerWeight;
            if (!isAnimated)
            {
                _blendMethod = srcParamSetGroup._blendMethod;
            }
            else
            {
                _blendMethod = apModifierParamSetGroup.BLEND_METHOD.Additive;                //<<애니메이션에서는 Additive 강제
            }

            _isColorPropertyEnabled = srcParamSetGroup._isColorPropertyEnabled;            //<<추가.


            // 삭제 19.5.20 : _calculatedWeightedVertexList 변수 삭제
            //_calculatedWeightedVertexList.Clear();

            //for (int i = 0; i < srcParamSetGroup._calculatedWeightedVertexList.Count; i++)
            //{
            //	apModifierParamSetGroupVertWeight srcWV = srcParamSetGroup._calculatedWeightedVertexList[i];

            //	apOptParamSetGroupVertWeight optWV = new apOptParamSetGroupVertWeight();
            //	optWV.Bake(srcWV);

            //	optWV.Link(portrait.GetOptTransform(optWV._meshTransform_ID));//OptTransform을 연결한다.

            //	_calculatedWeightedVertexList.Add(optWV);
            //}

            LinkPortrait(portrait, parentModifier);
        }
        //---------------------------------------------------
        // 계산함수 - KeyFrame
        //---------------------------------------------------
        private void CalculateWeight_KeyFrame()
        {
            if (_keyParamSetGroup == null || _keyParamSetGroup._keyAnimTimelineLayer == null)
            {
                return;
            }
            apAnimTimelineLayer timlineLayer = _keyParamSetGroup._keyAnimTimelineLayer;

            apOptCalculatedResultParam.OptParamKeyValueSet curParamKeyValue = null;

            //9.26 : 이거 수정해야한다.
            //Int형 프레임과 Float형 프레임을 둘다 사용한다.
            int   curFrame      = timlineLayer._parentAnimClip.CurFrame;
            float curFrameFloat = timlineLayer._parentAnimClip.CurFrameFloat;            //<<이건 실수형

            bool isLoop = timlineLayer._parentAnimClip.IsLoop;

            //apAnimKeyframe firstKeyframe = timlineLayer._firstKeyFrame;
            //apAnimKeyframe lastKeyframe = timlineLayer._lastKeyFrame;


            _totalWeight = 0.0f;

            apAnimKeyframe curKeyframe  = null;
            apAnimKeyframe prevKeyframe = null;
            apAnimKeyframe nextKeyframe = null;

            //int indexOffsetA = 0;
            //int indexOffsetB = 0;
            int   lengthFrames     = timlineLayer._parentAnimClip.EndFrame - timlineLayer._parentAnimClip.StartFrame;
            int   tmpCurFrameInt   = 0;
            float tmpCurFrameFloat = 0;


            for (int i = 0; i < _subParamKeyValues.Count; i++)
            {
                curParamKeyValue = _subParamKeyValues[i];

                curParamKeyValue._dist         = -10.0f;
                curParamKeyValue._isCalculated = false;
                curParamKeyValue._weight       = 0.0f;

                //추가 12.5
                curParamKeyValue._animKeyPos = apOptCalculatedResultParam.AnimKeyPos.NotCalculated;

                //유효하지 않은 키프레임이면 처리하지 않는다.
                if (curParamKeyValue._paramSet.SyncKeyframe == null ||
                    !curParamKeyValue._paramSet.SyncKeyframe._isActive)
                {
                    //Debug.Log("[" + i + "] Not Active or Null Keyframe");
                    continue;
                }

                curKeyframe  = curParamKeyValue._paramSet.SyncKeyframe;
                prevKeyframe = curParamKeyValue._paramSet.SyncKeyframe._prevLinkedKeyframe;
                nextKeyframe = curParamKeyValue._paramSet.SyncKeyframe._nextLinkedKeyframe;


                //1. 프레임이 같다. => 100%
                if (curFrame == curKeyframe._frameIndex ||
                    ((curKeyframe._isLoopAsStart || curKeyframe._isLoopAsEnd) && curFrame == curKeyframe._loopFrameIndex))
                {
                    curParamKeyValue._dist         = 0.0f;
                    curParamKeyValue._isCalculated = true;
                    curParamKeyValue._weight       = 1.0f;
                    _totalWeight += 1.0f;

                    //추가 12.5 : AnimKeyPos : 동일 프레임
                    curParamKeyValue._animKeyPos = apOptCalculatedResultParam.AnimKeyPos.ExactKey;
                }
                //else if(curFrame >= curKeyframe._activeFrameIndexMin &&
                //		curFrame < curKeyframe._frameIndex)
                else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Prev))
                {
                    //범위 안에 들었다. [Prev - Cur]
                    if (prevKeyframe != null)
                    {
                        tmpCurFrameInt = curFrame;
                        if (tmpCurFrameInt > curKeyframe._frameIndex)
                        {
                            tmpCurFrameInt -= lengthFrames;
                        }

                        tmpCurFrameFloat = curFrameFloat;
                        if (tmpCurFrameFloat > curKeyframe._frameIndex)
                        {
                            tmpCurFrameFloat -= lengthFrames;
                        }

                        //[중요] 재생 프레임 -> 보간 가중치 계산 코드
                        //_cal_itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrameInt, true);//기존 : Int
                        _cal_itp = curKeyframe._curveKey.GetItp_Float(tmpCurFrameFloat, true, tmpCurFrameInt);                        //변경 : Float

                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = _cal_itp;
                        _totalWeight += _cal_itp;


                        //추가 : Rotation Bias
                        //Prev와 연결되었다면 Prev 설정을 적용한다.
                        if (curKeyframe._prevRotationBiasMode != apAnimKeyframe.ROTATION_BIAS.None)
                        {
                            curParamKeyValue.SetAnimRotationBias(curKeyframe._prevRotationBiasMode, curKeyframe._prevRotationBiasCount);
                        }
                        //Debug.Log("[" + i + "] [Prev ~ Cur] " + itp);
                        //Debug.Log("Prev ~ Next : " + itp);

                        //추가 12.5 : AnimKeyPos - Next 프레임으로서 Prev 프레임과 보간이 된다.
                        curParamKeyValue._animKeyPos = apOptCalculatedResultParam.AnimKeyPos.NextKey;
                    }
                    else
                    {
                        //연결된게 없다면 이게 100% 가중치를 갖는다.
                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = 1.0f;
                        _totalWeight += 1.0f;
                        //Debug.Log("[" + i + "] [Prev ?? ~ Cur] 1.0");


                        //추가 12.5 : AnimKeyPos - 동일 프레임
                        curParamKeyValue._animKeyPos = apOptCalculatedResultParam.AnimKeyPos.ExactKey;
                    }
                }
                //else if(curFrame > curKeyframe._frameIndex &&
                //		curFrame <= curKeyframe._activeFrameIndexMax)
                else if (curKeyframe.IsFrameIn(curFrame, apAnimKeyframe.LINKED_KEY.Next))
                {
                    //범위안에 들었다 [Cur - Next]
                    if (nextKeyframe != null)
                    {
                        tmpCurFrameInt = curFrame;
                        if (tmpCurFrameInt < curKeyframe._frameIndex)
                        {
                            tmpCurFrameInt += lengthFrames;
                        }

                        tmpCurFrameFloat = curFrameFloat;
                        if (tmpCurFrameFloat < curKeyframe._frameIndex)
                        {
                            tmpCurFrameFloat += lengthFrames;
                        }

                        //[중요] 재생 프레임 -> 보간 가중치 계산 코드
                        //_cal_itp = curKeyframe._curveKey.GetItp_Int(tmpCurFrameInt, false);//기존
                        _cal_itp = curKeyframe._curveKey.GetItp_Float(tmpCurFrameFloat, false, tmpCurFrameInt);                        //변경 : Float

                        //itp = 1.0f - itp;//결과가 B에 맞추어지므로 여기서는 Reverse

                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = _cal_itp;
                        _totalWeight += _cal_itp;

                        //Debug.Log("[" + i + "] [Cur ~ Next] " + itp);

                        //추가 : Rotation Bias
                        //Next와 연결되었다면 Next 설정을 적용한다.
                        if (curKeyframe._nextRotationBiasMode != apAnimKeyframe.ROTATION_BIAS.None)
                        {
                            curParamKeyValue.SetAnimRotationBias(curKeyframe._nextRotationBiasMode, curKeyframe._nextRotationBiasCount);
                        }

                        //추가 12.5 : AnimKeyPos - Prev 프레임으로서 Next 프레임과 보간이 된다.
                        curParamKeyValue._animKeyPos = apOptCalculatedResultParam.AnimKeyPos.PrevKey;
                    }
                    else
                    {
                        //연결된게 없다면 이게 100% 가중치를 갖는다.
                        curParamKeyValue._dist         = 0.0f;
                        curParamKeyValue._isCalculated = true;
                        curParamKeyValue._weight       = 1.0f;
                        _totalWeight += 1.0f;

                        //Debug.Log("[" + i + "] [Cur ~ Next ??] 1.0");

                        //추가 12.5 : AnimKeyPos - 동일 프레임
                        curParamKeyValue._animKeyPos = apOptCalculatedResultParam.AnimKeyPos.ExactKey;
                    }
                }
            }

            if (_totalWeight > 0.0f)
            {
                //Debug.Log("Result --------------------------------");
                for (int i = 0; i < _subParamKeyValues.Count; i++)
                {
                    curParamKeyValue = _subParamKeyValues[i];

                    if (curParamKeyValue._isCalculated)
                    {
                        curParamKeyValue._weight /= _totalWeight;
                        //Debug.Log("[" + curParamKeyValue._weight + "]");
                    }
                    else
                    {
                        curParamKeyValue._weight = 0.0f;
                    }
                }
                //Debug.Log("-------------------------------------");
            }
        }