• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            天行健 君子當自強而不息

            Blending Skeletal Animations(2)

            Enhancing Skeletal Animation Objects

            Now that you've seen how simple it is to blend multiple skeletal animations, why not take this new knowledge and add on to the skeletal animation objects? Sounds like a great idea; by adding a single function to the cAnimationCollection class, you can be on your way to blending animations like the pros.

            In fact, rather than messing with the code from cAnimationCollection, just derive a new class that handles blended animations. This new derived class, cBlendedAnimationCollection, is defined as follows:

            class cBlendedAnimationCollection : public cAnimationCollection
            {
            public:
            void Blend(char *AnimationSetName,
            DWORD Time, BOOL Loop,
            float Blend = 1.0f);
            };

            Wow, that's a small class! The one and only function declared in cBlendedAnimationCollection is Blend, which is meant to take over the cAnimationCollection::Update function. Why not just derive a new Update function, you ask? Well, with cBlendedAnimationCollection, you can use the regular animation sets you used, as well as your (soon to be) newly developed blended animation sets.

            Take a close look at the Blend function to see what's going on, and then I'll show you how to put your new class to good use.

            void cBlendedAnimationCollection::Blend(
                char *AnimationSetName,
                DWORD Time, BOOL Loop,
                float Blend)
            {

            The Blend function prototype takes four parameters, the first of which is AnimationSetName. When calling Blend, you need to set AnimationSetName to the name of the animation set you are going to blend into the animation. Each animation set in your source .X file has a unique animation name (as defined by the AnimationSet data object's instance name). You must set AnimationSetName to a matching name from the .X file.

            I'll get back to the animation sets in a bit. For now, I want to get back to the Blend prototype. The second parameter of Blend is Time, which represents the time in the animation that you are using to blend the animation. If you have an animation that is 1000 milliseconds in length, then Time can be any value from 0 to 999. Specifying a value larger than the animation's length forces the Blend function to use the last key frame in the list to blend the animation.

            What about looping the animation? Well, that's the purpose of the third parameter, Loop. If you set Loop to FALSE, then your animations will refuse to update if you try to update using a time value that is greater than the length of the animation. However, if you set Loop to TRUE, the Blend function bounds−checks the time value (Time) to always fall within the range of the animation's time.

            The previous paragraph may not make perfect sense at first, so to help you understand, imagine the following function:

            void UpdateAnimation(DWORD Elapsed)
            {
            static DWORD AnimationTime = 0; // Animation time
            	// Call Blend, using AnimationTime as the time in the animation
            AnimationBlend.Blend("Walk", AnimationTime, FALSE, 1.0f);
            	// Update the time of the animation
            AnimationTime += ELapsed;
            }

            In the UpdateAnimation function, you are tracking the animation time via a static variable. Every time UpdateAnimation is called, the Blend function is used to blend in an animation called Walk, using a time value specified as AnimationTime. Assuming the Walk animation is 1000 milliseconds in length and the elapsed time between calls to UpdateAnimation is 50 ms, you can see tell that the animation would reach its end after 20 calls to the function. This means after you call UpdateAnimation 20 times, the animation will stop (because you set Loop to FALSE).

            Going back and changing the Loop value to TRUE forces Blend to bounds−check the timing value and make sure it always uses a valid timing value. When I say bounds−check, I mean to use a modulus calculation. I'll show you how to use the modulus calculation in a moment; for now I want to get back to the fourth and final parameter.

            The last parameter is Blend, which is a floating−point value that represents a scalar value used to modify the blended transformation matrix before it is applied to the skeletal structure. For example, if you are blending a walking animation, but you only want 50 percent of the transformation to be applied, then you would set Blend to 0.5.

            Okay, that's enough for the parameters; let's get into the function code! If you've perused the cAnimationCollection::Update function, you'll notice that the majority of code in the Blend function is the same. Starting off, you'll find a bit of code that scans the linked list of animation sets to find the one that matches the name you provided as AnimationSetName.

            cAnimationSet *AnimSet = m_AnimationSets;
            // Look for matching animation set name if used
            if(AnimationSetName) {
            	// Find matching animation set name
            while(AnimSet != NULL) {
            // Break when match found
            if(!stricmp(AnimSet−>m_Name, AnimationSetName))
            break;
            		// Go to next animation set object
            AnimSet = AnimSet−>m_Next;
            }
            }
            // Return no set found
            if(AnimSet == NULL)
            return;

            If you set AnimationSetName to NULL, then Blend will use the first animation set in the linked list of animation sets. If you specified a name in AnimationSetName and none was found in the linked list, then Blend will return without any further ado.

            Now that you have a pointer to the appropriate animation set object, you can bounds−check the time according to the value set in Time and the looping flag Loop.

            // Bounds time to animation length
            if(Time > AnimSet−>m_Length)
                Time = (Loop==TRUE)?Time % (AnimSet−>m_Length+1):AnimSet−>m_Length;

            Quite an ingenious little bit of code, the previous tidbit does one of two things, depending on the Loop flag. If Loop is set to FALSE, then Time is checked against the length of the animation (AnimSet−>m_Length). If Time is greater than the length of the animation, then Time is set to the length of the animation, thus locking it at the last millisecond (and later on, the last key frame) of the animation. If you set Loop to TRUE, then a modulus calculation forces Time to always lie within the range of the animation's length (from 0 to AnimSet−>m_Length).

            After you have calculated the appropriate Time to use for the animation, it is time to scan the list of bones in your skeletal structure. For each bone, you are going to track the combined transformations from the appropriate key frames. For each key frame found in the animation, you need to add (not multiply) the transformation to the skeletal structure's transformations.

            // Go through each animation
            cAnimation *Anim = AnimSet−>m_Animations;
            while(Anim) {
            //Only process if it's attached to a bone
            if(Anim−>m_Bone) {
            // Reset transformation
            D3DXMATRIX matAnimation;
            D3DXMatrixIdentity(&matAnimation);
            		// Apply various matrices to transformation

            From here, scan each key frame (depending on the type of keys used) and calculate the transformation to apply to your skeletal structure. For the sake of space, I'm only going to list the code that scans matrix keys.

            // Matrix
            if(Anim−>m_NumMatrixKeys && Anim−>m_MatrixKeys) {
            // Loop for matching matrix key
            DWORD Key1 = 0, Key2 = 0;
            	for(DWORD i=0;i<Anim−>m_NumMatrixKeys;i++) {
            if(Time >= Anim−>m_MatrixKeys[i].m_Time)
            Key1 = i;
            }
            	// Get 2nd key number
            Key2 = (Key1>=(Anim−>m_NumMatrixKeys−1))?Key1:Key1+1;
            	// Get difference in keys' times
            DWORD TimeDiff = Anim−>m_MatrixKeys[Key2].m_Time − Anim−>m_MatrixKeys[Key1].m_Time;
            	if(!TimeDiff)
            TimeDiff = 1;
            	// Calculate a scalar value to use
            float Scalar = (float)(Time − Anim−>m_MatrixKeys[Key1].m_Time) / (float)TimeDiff;
            	// Calculate interpolated matrix
            D3DXMATRIX matDiff;
            matDiff = Anim−>m_MatrixKeys[Key2].m_matKey − Anim−>m_MatrixKeys[Key1].m_matKey;
            matDiff *= Scalar;
            matDiff += Anim−>m_MatrixKeys[Key1].m_matKey;
            	// Combine with transformation
            matAnimation *= matDiff;
            }

            Basically, the code is searching the key frames and calculating an appropriate transformation to use. This transformation is stored in
            matAnimation.

            From this point on, things take a decidedly different course than the cAnimationCollection::Update function code. Instead of storing the transformation matrix (matAnimation) in the skeletal structure's frame object, you will calculate the difference in the transformation from matAnimation to the skeletal structure's initial transformation (stored in matOriginal when the skeletal structure was loaded). This difference in transformation values is scaled using the floating−point Blend value you provided, and the resulting transformation is then added (not multiplied, as you do with concoction) to the skeletal structure's frame transformation. This ensures that the transformations are properly blended at the appropriate blending values.

            After that, the next bone's key frames are scanned, and the loop continues until all bones have been processed.

            			// Get the difference in transformations
            D3DXMATRIX matDiff = matAnimation − Anim−>m_Bone−>matOriginal;
            			// Adjust by blending amount
            matDiff *= Blend;
            			// Add to transformation matrix
            Anim−>m_Bone−>TransformationMatrix += matDiff;
            }
            		// Go to next animation
            Anim = Anim−>m_Next;
            }
            }

            Congratulations, you've just completed your Blend function'! Let's put this puppy to work! Suppose you have a mesh and frame hierarchy already loaded, and you want to load a series of animations from an .X file. Suppose this .X file (called Anims.x) has four animation sets: Stand,Walk, Wave, and Shoot. That's two animations for the legs and two for the arms and torso. Here's a bit of code to load the animation sets:

            // pFrame = root frame in frame hierarchy
            cBlendedAnimationSet BlendedAnims;
            BlendedAnims.Load("Anims.x");

            // Map animations frame hierarchy
            BlendedAnims.Map(pFrame);

            Now that you have an animation collection loaded, you can begin blending the animations before updating and rendering your skinned mesh. Suppose you want to blend the Walk and Shoot animations, both using 100 percent of the transformations. To start, you must reset your frame hierarchy's transformations to their original states. This means you need to copy the D3DXFRAME_EX::matOriginal transformation into the D3DXFRAME_EX::TransformationMatrix transformation. This is very important because it serves as a base to which your animation set transformations are added during the blending operation.

            // Use D3DXFRAME_EX::Reset to reset transformations
            pFrame−>Reset();

            Once the transformations have been reset to their original states, you can blend your animation sets.

            // AnimationTime = time of animation, which is the elapsed time since start of the animation

            // Blend in the walk animation
            BlendedAnims.Blend("Walk", AnimationTime, TRUE, 1.0f);

            // Blend in the shoot animation
            BlendedAnims.Blend("Shoot", AnimationTime, TRUE, 1.0f);

            Once you've blended all the animation sets you're going to use, you need to update your frame hierarchy, which is then used to update your skinned mesh.

            // Update the frame hierarchy
            pFrame−>UpdateHierarchy();

            After you have updated the hierarchy (the transformation matrices have been combined and the results are stored in D3DXFRAME_EX::matCombined), you can update your skinned mesh and render away!


            posted on 2008-04-25 19:35 lovedday 閱讀(361) 評論(0)  編輯 收藏 引用

            公告

            導航

            統計

            常用鏈接

            隨筆分類(178)

            3D游戲編程相關鏈接

            搜索

            最新評論

            久久这里只精品99re66| 久久精品国产免费一区| 综合久久一区二区三区 | 久久精品国产精品亜洲毛片| 国产高潮国产高潮久久久| 久久综合狠狠综合久久激情 | 久久亚洲sm情趣捆绑调教| 一级做a爰片久久毛片看看| 人妻无码αv中文字幕久久琪琪布| 亚洲第一极品精品无码久久 | 噜噜噜色噜噜噜久久| 久久天堂AV综合合色蜜桃网| 国产精品成人无码久久久久久 | 精品国产热久久久福利| 中文字幕精品久久| 国产精品久久久久9999| 99精品国产免费久久久久久下载| 久久亚洲中文字幕精品有坂深雪| 久久亚洲精品视频| 亚洲综合熟女久久久30p| 精品欧美一区二区三区久久久| 久久久无码精品亚洲日韩蜜臀浪潮 | 精品久久久久久无码免费| 99久久免费国产精品特黄| 国产精品丝袜久久久久久不卡 | 国产99久久久久久免费看| 亚洲精品视频久久久| 精品久久一区二区三区| 国产成人无码精品久久久性色| 精品九九久久国内精品| 无码伊人66久久大杳蕉网站谷歌 | 囯产极品美女高潮无套久久久| 欧美日韩中文字幕久久伊人| 久久www免费人成看片| 久久亚洲2019中文字幕| 日本精品一区二区久久久| 久久国产香蕉视频| 国产成人久久久精品二区三区| 99久久免费国产精品热| 色综合久久无码五十路人妻| 亚洲中文字幕久久精品无码喷水|