您的位置:首页 > 其它

XSI Vertex Animation and OGRE Facial Animation

2017-02-19 21:21 471 查看
目前OGRE只有XSI 5.x、Maya的导出器及oFusion Pro for 3ds max(收费)支持顶点动画的导出,而在众多建模软件中XSI(http://www.softimage.com/)以其对变形动画的支持最为突出,OGRE提供的Facial Demo中使用的Dr. Bunsen的头就是XSI中提供的一个示例动画,该模型就是使用对顶点簇(cluster)的位置改变来产生动画。
XSI变形动画(顶点动画)的制作
1、 将Mode改为Shape Modeling Mode;
2、将关键帧移至期望的动画开始处存储并应用物体或顶点簇(cluster)的shape key,该key记录了物体的初始形状;
3、将关键帧移至所期望的变形动画结束处,利用Animation中的Deform对物体或顶点簇产生变形;
4、存储并应用shape key,该key记录了物体变形后的状态;
5、你可以在Animation中的Shape菜单的Shape Manager…改变物体变形的程度。
在OGRE中播放顶点动画
我们需要从 ogre 官方网站 上下载XSI模型导出器 , 安装后(安装过程中需要 XSI 5.x 的目录)在XSI 的 Export 中就会出现 OGRE
mesh/Skeleton 菜单项,你需要按照下列步骤来导出顶点动画模型: SoftImage XSI 5.0 Exporter v1.2.3
1、 选中需要导出的模型;
2、在ogre export的Basic页中输入导出mesh的路径及文件名,在Materials页中填入导出材质的路径及文件名(需要说明的是在该导出器中材质导出有BUG,不一定能正确导出材质),在Animation页中选中Export Vertex Animation复选框(其实,如果选中物体中有Vertex Animation或Export Skeleton,那么复选框会自动选上),如果模型包含动画,则在Animation栏目会出现动画的信息,包括:名字、开始帧、结束帧、采样频率;
3、按ok导出。
我们可以在ogre提供的Facial Demo基础上修改代码以适合导出的模型,同时,你可以利用 OgreCommandLineTools 中的OgreXmlConverter来将.mesh文件逆向转换为.xml文件便于研究模型及动画数据的格式,下面给出了一个简单球体顶点动画模型及其xml数据:



<mesh>
    <submeshes>
        <submesh material="Scene_Material" usesharedvertices="false" use32bitindexes="false" operationtype="triangle_list">
            <faces count="288">
                <face v1="0" v2="1" v3="2" />
                                                                 :(省略)
                                                                 :
                <face v1="145" v2="16" v3="17" />
            </faces>
            <geometry vertexcount="146">
                <vertexbuffer positions="true"> [1] 
                    <vertex>
                        <position x="-3.21394" y="-9.39693" z="1.16978" />
                    </vertex>
                                                                                             :
                                                                                             :
                    <vertex>
                        <position x="-3.21394" y="9.39693" z="-1.16978" />
                    </vertex>
                </vertexbuffer>
                <vertexbuffer normals="true"> [2] 
                    <vertex>
                        <normal x="-0.321129" y="-0.939795" z="0.116881" />
                    </vertex>
                                                                                             :
                                                                                             :
                    <vertex>
                        <normal x="-0.321129" y="0.939795" z="-0.116881" />
                    </vertex>
                </vertexbuffer>
            </geometry>
        </submesh>
    </submeshes>
    <submeshnames>
        <submeshname name="sphere" index [3]  ="0" />
    </submeshnames>
    <poses> [4] 
        <pose target="submesh" index="0" name="s1" /> [5] 
        <pose target="submesh" index="0" name="s2"> [6] 
            <poseoffset index [7]  ="0" x="0" y="-0.457566" z="0.0569602" />
                                                                                             :
                                                                                             :
            <poseoffset index="145" x="0" y="0.457566" z="-0.0569602" />
        </pose>
    </poses>
    <animations> [8] 
        <animation name="ss" length="1.72414"> [9] 
            <tracks> [10] 
                <track target="submesh" index="0" type="pose"> [11] 
                    <keyframes> [12] 
                        <keyframe time="0">
                            <poseref poseindex="0" influence="1" /> [13] 
                        </keyframe>
                        <keyframe time="0.0344828">
                            <poseref poseindex="0" influence="1" /> [14] 
                        </keyframe>
                        <keyframe time="1.68966">
                            <poseref poseindex="0" influence="0.00123248" /> [15] 
                            <poseref poseindex="1" influence="0.998767" /> [16] 
                        </keyframe>
                        <keyframe time="1.72414" />
                    </keyframes>
                </track>
            </tracks>
        </animation>
    </animations>
</mesh> 
OGRE程序实现分下面步骤:
1、  由于OGRE中采用了CEGUI(Crazy Eddie's GUI System)作为GUI,所以我们可以通过其提供的CELayoutEditor来编辑出想要的GUI。下图是该程序的一个简单的GUI,右边空白区域作为场景渲染区;

2、 将其保存成一个layout文件,如aaa.layout,并拷贝至OGRESDK/media/gui下。
下面我着重对程序代码进行说明:
000001     #include <CEGUI/CEGUIImageset.h> 

000002     #include <CEGUI/CEGUISystem.h> 

000003     #include <CEGUI/CEGUILogger.h> 

000004     #include <CEGUI/CEGUISchemeManager.h> 

000005     #include <CEGUI/CEGUIWindowManager.h> 

000006     #include <CEGUI/CEGUIWindow.h> 

000007     #include <CEGUI/elements/CEGUICombobox.h> 

000008     #include <CEGUI/elements/CEGUIListbox.h> 

000009     #include <CEGUI/elements/CEGUIListboxTextItem.h> 

000010     #include <CEGUI/elements/CEGUIPushButton.h> 

000011     #include <CEGUI/elements/CEGUIScrollbar.h> 

000012     #include <CEGUI/elements/CEGUIStaticImage.h> 

000013     #include <CEGUI/elements/CEGUIRadioButton.h> 

000014     #include "OgreCEGUIRenderer.h" 

000015     #include "OgreCEGUIResourceProvider.h" 

000016     #include "ExampleApplication.h" 

000017     

000018     // 将OGRE中的鼠标按键映射成CEGUI的按键 

000019    CEGUI::MouseButton convertOgreButtonToCegui( int buttonID) 

000020    { 

000021        switch (buttonID) 

000022        { 

000023        case MouseEvent::BUTTON0_MASK: 

000024             return CEGUI::LeftButton; 

000025        case MouseEvent::BUTTON1_MASK: 

000026             return CEGUI::RightButton; 

000027        case MouseEvent::BUTTON2_MASK: 

000028             return CEGUI::MiddleButton; 

000029        case MouseEvent::BUTTON3_MASK: 

000030             return CEGUI::X1Button; 

000031        default: 

000032             return CEGUI::LeftButton; 

000033        } 

000034    } 

000035     

000036    AnimationState* morphAnimState;     // 定义用于自动播放的变形动画的指针 

000037    AnimationState* manualAnimState; // 定义用于手动播放的变形动画的指针 

 000038    VertexPoseKeyFrame* manualKeyFrame; // 定义用于手动播放VertexPose关键帧指针 

000039     

 000040    String scrollbarNames = "aaa/Morph_Scroll";     // 用于产生横向滚动条状态变化时,确定是哪个滚动条(当然这是针对有多个滚动条的情况) 

000041     

000042    CEGUI::Scrollbar* scrollbars;     // 滚动条指针,通过后面的代码从layout文件中获取 

000043     

000044     // 定义GUI事件监听器类 

 000045     class GuiFrameListener : public ExampleFrameListener, public MouseMotionListener, public MouseListener 

000046    { 

000047     private: 

000048        CEGUI::Renderer* mGUIRenderer; 

000049         bool mShutdownRequested; 

000050     

000051     public: 

 000052        GuiFrameListener(RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer) 

000053            : ExampleFrameListener(win, cam, true, true), 

000054            mGUIRenderer(renderer), 

000055            mShutdownRequested(false) 

000056        { 

000057            mEventProcessor->addMouseMotionListener(this); // 添加鼠标动作监听器 

000058            mEventProcessor->addMouseListener(this);     // 添加鼠标监听器 

000059            mEventProcessor->addKeyListener(this);         // 添加键盘按键监听器 

000060        } 

000061     

000062         // 设置退出标志 

000063         void requestShutdown( void) 

000064        { 

000065            mShutdownRequested = true; 

000066        } 

000067     

000068         bool frameEnded( const FrameEvent& evt) 

000069        { 

000070             if (mShutdownRequested) 

000071                 return false; 

000072             else 

000073                 return ExampleFrameListener::frameEnded(evt); 

000074        } 

000075     

000076         void mouseMoved (MouseEvent *e) 

000077        { 

000078             // CEGUI类方法,将鼠标移动事件及其参数“注入”GUI系统以便处理 

000079            CEGUI::System::getSingleton().injectMouseMove( 

000080                e->getRelX() * mGUIRenderer->getWidth(), 

000081                e->getRelY() * mGUIRenderer->getHeight()); 

000082             // OGRE类方法,销毁该事件消息,以使其不被它的产生者以默认的方式来处理 

000083            e->consume(); 

000084        } 

000085     

000086         void mouseDragged (MouseEvent *e) 

000087        { 

000088            mouseMoved(e); 

000089        } 

000090     

000091         void mousePressed (MouseEvent *e) 

000092        { 

000093            CEGUI::System::getSingleton().injectMouseButtonDown( 

000094                convertOgreButtonToCegui(e->getButtonID())); 

000095            e->consume(); 

000096        } 

000097     

000098         void mouseReleased (MouseEvent *e) 

000099        { 

000100            CEGUI::System::getSingleton().injectMouseButtonUp( 

000101                convertOgreButtonToCegui(e->getButtonID())); 

000102            e->consume(); 

000103        } 

000104     

000105         void mouseClicked(MouseEvent* e) {} 

000106         void mouseEntered(MouseEvent* e) {} 

000107         void mouseExited(MouseEvent* e) {} 

000108     

000109         void keyPressed(KeyEvent* e) 

000110        { 

000111             if(e->getKey() == KC_ESCAPE) 

000112            { 

000113                mShutdownRequested = true; 

000114                e->consume(); 

000115                 return; 

000116            } 

000117     

000118             if (e->getKey() == KC_SYSRQ) 

000119            { 

000120                mWindow->writeContentsToTimestampedFile("screenshot", ".png"); 

000121            } 

000122     

000123            CEGUI::System::getSingleton().injectKeyDown(e->getKey()); 

000124            CEGUI::System::getSingleton().injectChar(e->getKeyChar()); 

000125            e->consume(); 

000126        } 

000127     

000128         void keyReleased(KeyEvent* e) 

000129        { 

000130            CEGUI::System::getSingleton().injectKeyUp(e->getKey()); 

000131            e->consume(); 

000132        } 

000133         void keyClicked(KeyEvent* e) 

000134        { 

000135             // 什么事都不做 

000136            e->consume(); 

000137        } 

000138     

000139         bool frameStarted( const FrameEvent& evt) 

000140        { 

000141            morphAnimState->addTime(evt.timeSinceLastFrame); 

000142             return ExampleFrameListener::frameStarted(evt); 

000143     

000144        } 

000145    }; 

000146     

000147     // 定义变形动画的应用程序类主类 

000148     class MorphApplication : public ExampleApplication 

000149    { 

000150     private: 

 000151        CEGUI::OgreCEGUIRenderer* mGUIRenderer; // 定义OGRE中CEGUI的渲染器指针 

000152        CEGUI::System* mGUISystem; // 定义GUI系统指针 

000153        CEGUI::Listbox* mList;     // 列表框指针 

000154     

000155     public: 

000156        FacialApplication() 

000157            : mGUIRenderer(0), 

000158            mGUISystem(0) 

000159        { 

000160     

000161        } 

000162     

000163        ~FacialApplication() 

000164        { 

000165             if(mGUISystem) 

000166            { 

000167                delete mGUISystem; 

000168                mGUISystem = 0; 

000169            } 

000170             if(mGUIRenderer) 

000171            { 

000172                delete mGUIRenderer; 

000173                mGUIRenderer = 0; 

000174            } 

000175        } 

000176     

000177     protected: 

000178     

000179         bool mPlayAnimation; // 自动/手动控制Radio Button对应变量 

000180     

000181         // 滚动条状态改变事件相应函数 

000182         bool handleScrollChanged( const CEGUI::EventArgs& e) 

000183        { 

000184             if (!mPlayAnimation) 

000185            { 

 000186                 const CEGUI::WindowEventArgs& args = static_cast< const CEGUI::WindowEventArgs&>(e); 

000187                 // 得到Scroll的名字 

000188                String name = args.window->getName().c_str(); 

000189                 // 判断是否是我想要改变的那一个(在多个scrollbar时) 

000190                 if(scrollbarNames == name) 

000191                { 

000192                     // 调试信息:显示scrollbar当前位置(用于调试) 

 000193                    mWindow->setDebugText(StringConverter::toString(scrollbars->getScrollPosition())); 

000194                     // 根据滚动条的位置更新顶点变形百分比 

000195                    manualKeyFrame->updatePoseReference( 

000196                        1, scrollbars->getScrollPosition()); 

000197                     // 得到 AnimationStateSet(动画状态集)指针, 

000198                     // 并通知当前动画状态已经改变 

000199                    manualAnimState->getParent()->_notifyDirty(); 

000200                } 

000201            } 

000202             return true; 

000203     

000204        } 

000205     

000206         // Radio Button状态改变响应函数 

000207         bool handleRadioChanged( const CEGUI::EventArgs& e) 

000208        { 

000209            mPlayAnimation = !mPlayAnimation; 

000210            morphAnimState->setEnabled(mPlayAnimation); 

000211            manualAnimState->setEnabled(!mPlayAnimation); 

000212             // 允许/禁止scrollbar 

000213            scrollbars->setEnabled(!mPlayAnimation); 

000214     

000215             return true; 

000216     

000217        } 

000218     

000219         // List状态改变响应函数 

000220         bool handleListBoxChanged( const CEGUI::EventArgs& e) 

000221        { 

000222             // 根据选择项,改变多边形模式 

 000223             if(mList->getFirstSelectedItem()->getText() == CEGUI::String("Solid Mode")) 

000224                mCamera->setPolygonMode(PM_SOLID); 

 000225             else if(mList->getFirstSelectedItem()->getText() == CEGUI::String("Wireframe Mode")) 

000226                mCamera->setPolygonMode(PM_WIREFRAME); 

000227             else 

000228                mCamera->setPolygonMode(PM_POINTS); 

000229             return true; 

000230        } 

000231     

000232         // 创建场景函数,重写ExampleApplication::createScene() 

000233         void createScene( void) 

000234        { 

000235             // 设置环境光 

000236            mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5)); 

000237     

000238             // 创建一个点光源 

000239            Light* l = mSceneMgr->createLight("MainLight"); 

000240             // 设置光源位置 

000241            l->setPosition(20,80,50); 

000242             // 设置表面反射的漫射光颜色 

000243            l->setDiffuseColour(0.5, 0.0, 0.5); 

000244     

000245             // 创建一个点光源 

000246            l = mSceneMgr->createLight("MainLight2"); 

000247             // 设置光源位置 

000248            l->setPosition(-120,-80,-50); 

000249             // 设置表面反射的漫射光颜色 

000250            l->setDiffuseColour(0.7, 0.7, 0.6); 

000251     

000252             // 载入mesh 

 000253            MeshPtr mesh = MeshManager::getSingleton().load("ss.mesh", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); 

000254            Animation* anim = mesh->createAnimation("smile", 0); 

000255             // 创建顶点动画轨迹,参数说明请参考OGRE中的人脸动画一文 

000256            VertexAnimationTrack* track = anim->createVertexTrack(1, VAT_POSE); 

000257             // 创建时间位置为0的关键帧并将其添加到动画中 

000258            manualKeyFrame = track->createVertexPoseKeyFrame(0); 

 000259             // 添加一个新的pose参考,第一个参数为poseindex,第二个参数为效果值(这里是0%) 

000260            manualKeyFrame->addPoseReference(1, 0.0f); 

000261     

000262             // 创建GUI渲染器对象和GUI系统对象 

000263            mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, 

000264                Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr); 

000265     

000266            mGUISystem = new CEGUI::System(mGUIRenderer); 

000267             // 开启GUI系统日志 

000268            CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative); 

000269             // 创建实体,命名为Head1 

000270            Entity* head = mSceneMgr->createEntity("Head1", "ss.mesh"); 

000271             // 从mesh中取得名字为ss的自动动画(请参考xml中的tag<animations>) 

000272            morphAnimState = head->getAnimationState("ss"); 

000273            morphAnimState->setEnabled(true); 

 000274             // 得到前面mesh->createAnimation("smile", 0)创建的手动动画smile,并将其时间位置置为0 

000275            manualAnimState = head->getAnimationState("smile"); 

000276            manualAnimState->setTimePosition(0); 

000277             // 创建场景子节点,并将实体head加入到其中 

 000278            SceneNode* headNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(); 

000279            headNode->attachObject(head); 

000280             // 设置摄像机参数 

000281            mCamera->setPosition(0, 0, 50); 

000282            mCamera->lookAt(0,0,0); 

000283     

000284             // 载入GUI皮肤元素,并将layout文件作为一个sheet(页)添加进GUI系统对象中 

000285            CEGUI::SchemeManager::getSingleton().loadScheme( 

000286                (CEGUI::utf8*)"TaharezLookSkin.scheme"); 

000287            mGUISystem->setDefaultMouseCursor( 

000288                (CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow"); 

000289            mGUISystem->setDefaultFont((CEGUI::utf8*)"BlueHighway-12"); 

000290     

000291            CEGUI::Window* sheet = 

000292                CEGUI::WindowManager::getSingleton().loadWindowLayout( 

000293                (CEGUI::utf8*)"aaa.layout"); 

000294            mGUISystem->setGUISheet(sheet); 

000295             // 得到窗口管理器 

 000296            CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton(); 

000297             // 从窗口管理器中得到滚动条对象 

000298            scrollbars = static_cast<CEGUI::Scrollbar*>( 

000299                wmgr.getWindow(scrollbarNames)); 

000300             // 注册滚动条事件处理函数 

000301            scrollbars->subscribeEvent( 

000302                CEGUI::Scrollbar::EventScrollPositionChanged, 

000303                CEGUI::Event::Subscriber(&FacialApplication::handleScrollChanged, this)); 

000304             // 缺省禁止使用滚动条 

000305            scrollbars->setEnabled(false); 

000306             // 得到Animation Radio按钮对象 

000307            CEGUI::RadioButton* btn = static_cast<CEGUI::RadioButton*>( 

000308                wmgr.getWindow((CEGUI::utf8*)"aaa/Radio/Play")); 

000309             // 允许使用Radio按钮 

000310            btn->setSelected(true); 

000311             // 注册该Radio按钮事件处理函数 

000312            btn->subscribeEvent(CEGUI::RadioButton::EventSelectStateChanged, 

 000313                CEGUI::Event::Subscriber(&FacialApplication::handleRadioChanged, this)); 

000314     

000315            mPlayAnimation = true; 

 000316             // 得到List对象,并向其中加入Solid Mode/Wireframe Mode/Point Mode三个选项 

000317            mList = static_cast<CEGUI::Listbox*>( 

000318                wmgr.getWindow((CEGUI::utf8*)"aaa/ListBox/PolygonMode")); 

000319            CEGUI::ListboxTextItem *listboxitem = 

000320                new CEGUI::ListboxTextItem ("Solid Mode"); 

 000321            listboxitem->setSelectionBrushImage("TaharezLook", "ListboxSelectionBrush"); 

000322            listboxitem->setSelected(mList->getItemCount() == 0); 

000323            mList->addItem(listboxitem); 

000324            listboxitem = new CEGUI::ListboxTextItem("Wireframe Mode"); 

 000325            listboxitem->setSelectionBrushImage("TaharezLook", "ListboxSelectionBrush"); 

000326            mList->addItem(listboxitem); 

000327            listboxitem = new CEGUI::ListboxTextItem("Point Mode"); 

 000328            listboxitem->setSelectionBrushImage("TaharezLook", "ListboxSelectionBrush"); 

000329            mList->addItem(listboxitem); 

000330             // 注册List选择事件处理函数 

000331            mList->subscribeEvent(CEGUI::Listbox::EventSelectionChanged , 

 000332                CEGUI::Event::Subscriber(&FacialApplication::handleListBoxChanged , this)); 

000333            mCamera->setPolygonMode(PM_SOLID); 

000334        } 

000335     

000336         // 创建帧(事件)监听器 

000337         void createFrameListener( void) 

000338        { 

 000339            mFrameListener= new GuiFrameListener(mWindow, mCamera, mGUIRenderer); 

000340            mRoot->addFrameListener(mFrameListener); 

000341        } 

000342     

000343         void setupEventHandlers( void) 

000344        { 

000345        } 

000346     

000347     

000348         void setupLoadedLayoutHandlers( void) 

000349        { 

000350        } 

000351     

000352         bool handleQuit( const CEGUI::EventArgs& e) 

000353        { 

000354             static_cast<GuiFrameListener*>(mFrameListener)->requestShutdown(); 

000355             return true; 

000356        } 

000357     

000358     

000359    }; 

000360     

000361     #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 

000362     #define WIN32_LEAN_AND_MEAN 

000363     #include "windows.h" 

000364     

000365    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) 

000366     #else 

000367     int main( int argc, char *argv[]) 

000368     #endif 

000369    { 

000370     

000371         // 创建应用程序对象 

000372        FacialApplication app; 

000373     

000374        try { 

000375            app.go();     // 运行 

000376        } catch( Ogre::Exception& e ) { 

000377     #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 

 000378            MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL); 

000379     #else 

000380            std::cerr << "An exception has occured: " << 

000381                e.getFullDescription().c_str() << std::endl; 

000382     #endif 

000383        } 

000384     

000385     

000386         return 0; 

000387    } 

程序运行的最终效果:



 [1]顶点坐标

 [2]顶点法线

 [3]Submesh的索引号,按照submeshs中submesh出现的先后顺序还确定。

 [4]该tag下pose的poseoffset用于标明index对应顶点产生变形后的偏移量,在手动动画中我们将根据横向滑块位置的百分比来确定顶点变化偏移量的百分之几。

 [5]索引号为0的顶点动画,在手动动画关键帧的 addPoseReference 与 updatePoseReference 函数的第一个参数中引用。

 [6]索引号为1的顶点动画。

 [7]顶点的索引号,按照vertexbuffer中vertex出现的先后次序来确定。

 [8]定义自动播放的动画。

 [9]定义一个名字为ss长度为1.72414(该值可作为横向滑块的长度,请参考layout说明)的动画。

 [10]定义动画轨迹。

 [11]针对索引号为0的submesh定义类型为pose的动画轨迹。

 [12]定义关键帧

 [13]在帧时间为0时,让索引号为0的顶点动画达到100%变形,因为0号pose没有poseoffset数据,因而表现为保持原来形态不变。

 [14]同上。

 [15]产生?%的变形,但是由于没有poseoffset数据,故实际上没有任何效果。

 [16]在帧时间为1.68966时,让索引号为1的顶点动画达到99%形变,也就是说从帧时间0到此时是一个渐变的效果,OGRE会根据时间间隔与端点时间的形变百分比自动计算中间的形变百分比。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: