2009年4月6日 星期一

IrrLicht Example Learn: 03.CustomSceneNode

 

 

 

/*

 

IrrLicht Example Learn: 03.CustomSceneNode

 

 

03.CustomSceneNode

 

Here comes the more sophisticated part of this tutorial:

The class of our very own custom scene node. To keep it simple,

our scene node will not be an indoor portal renderer nor a terrain

scene node, but a simple tetraeder, a 3d object consisting of 4

connected vertices, which only draws itself and does nothing more.

Note that this scenario does not require a custom scene node in Irrlicht.

Instead one would create a mesh from the geometry and pass it to a

irr::scene::IMeshSceneNode. This example just illustrates creation of a custom

scene node in a very simple setting.

 

To let our scene node be able to be inserted into the Irrlicht

Engine scene, the class we create needs to be derived from the

irr::scene::ISceneNode class and has to override some methods.

*/

/*

     DEMO為自定義場景節點, 以顯示irrLicht 不支持的渲染方式

    

*/

class CSampleSceneNode : public scene::ISceneNode

{

 

     /*

     First, we declare some member variables:

     The bounding box, 4 vertices, and the material of the tetraeder.

     */

     core::aabbox3d Box;

     video::S3DVertex Vertices[4];

     video::SMaterial Material;

 

     /*

     The parameters of the constructor specify the parent of the scene node,

     a pointer to the scene manager, and an id of the scene node.

     In the constructor we call the parent class' constructor,

     set some properties of the material, and

     create the 4 vertices of the tetraeder we will draw later.

     */

     /*

         運行 scene::ISceneNode(..) 初始化. 設定要建立的三角立方體四個頂點.

         CSampleSceneNode 需要輸入三個參數, 1: 父節點, 2:場景管理器 3:這個節點的唯一ID

     */

 

public:

 

     CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)

         : scene::ISceneNode(parent, mgr, id)

     {

         Material.Wireframe = false;    //顯示網狀結構

         Material.Lighting = false;     //啟用光照顯示

 

         Vertices[0] = video::S3DVertex(0,0,10, 1,1,0,video::SColor(255,0,255,255), 0, 1);

         Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0,video::SColor(255,255,0,255), 1, 1);

         Vertices[2] = video::S3DVertex(0,20,0, 0,1,1,video::SColor(255,255,255,0), 1, 0);

         Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1,video::SColor(255,0,255,0), 0, 0);

 

     /*

     The Irrlicht Engine needs to know the bounding box of a scene node.

     It will use it for automatic culling and other things. Hence, we

     need to create a bounding box from the 4 vertices we use.

     If you do not want the engine to use the box for automatic culling,

     and/or don't want to create the box, you could also call

     irr::scene::ISceneNode::setAutomaticCulling() with irr::scene::EAC_OFF.

     */

    

         Box.reset(Vertices[0].Pos);          // reset Box, 設定BOX的位置到三角立方體的一個點上

         for (s32 i=1; i<4;>

              Box.addInternalPoint(Vertices[i].Pos);   // 依序把三角立方體的每个點給到BOX, Box重新計算所包含的範圍

     }

 

     /*

     Before it is drawn, the irr::scene::ISceneNode::OnRegisterSceneNode()

     method of every scene node in the scene is called by the scene manager.

     If the scene node wishes to draw itself, it may register itself in the

     scene manager to be drawn. This is necessary to tell the scene manager

     when it should call irr::scene::ISceneNode::render(). For

     example, normal scene nodes render their content one after another,

     while stencil buffer shadows would like to be drawn after all other

     scene nodes. And camera or light scene nodes need to be rendered before

     all other scene nodes (if at all). So here we simply register the

     scene node to render normally. If we would like to let it be rendered

     like cameras or light, we would have to call

     SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA);

     After this, we call the actual

     irr::scene::ISceneNode::OnRegisterSceneNode() method of the base class,

     which simply lets also all the child scene nodes of this node register

     themselves.

     */

     /*

         每个要顯示的節點都必須使用irr::scene::ISceneNode::OnRegisterSceneNode() 註冊.

         如果想要讓這個節點被相機或燈光渲染則應該使用這個函數SceneManager->registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA);

         最後我們呼叫  ISceneNode::OnRegisterSceneNode(); 以設定這個節點下的 子節點.

 

     */

     virtual void OnRegisterSceneNode()

     {

         if (IsVisible)

              SceneManager->registerNodeForRendering(this);

 

         ISceneNode::OnRegisterSceneNode();

     }

 

     /*

     In the render() method most of the interesting stuff happens: The

     Scene node renders itself. We override this method and draw the

     tetraeder.

     */

     virtual void render()

     {

         u16 indices[] = {  0,2,3, 2,1,3, 1,0,3, 2,0,1  };

         video::IVideoDriver* driver = SceneManager->getVideoDriver();

 

         driver->setMaterial(Material);

         driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);

         driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);

     }

 

     /*

     And finally we create three small additional methods.

     irr::scene::ISceneNode::getBoundingBox() returns the bounding box of

     this scene node, irr::scene::ISceneNode::getMaterialCount() returns the

     amount of materials in this scene node (our tetraeder only has one

     material), and irr::scene::ISceneNode::getMaterial() returns the

     material at an index. Because we have only one material here, we can

     return the only one material, assuming that no one ever calls

     getMaterial() with an index greater than 0.

     */

     virtual const core::aabbox3d& getBoundingBox() const

     {

         return Box;

     }

 

     virtual u32 getMaterialCount() const

     {

         return 1;

     }

 

     virtual video::SMaterial& getMaterial(u32 i)

     {

         return Material;

     }   

};

 

/*

That's it. The Scene node is done. Now we simply have to start

the engine, create the scene node and a camera, and look at the result.

*/

int main()

{

     // let user select driver type

 

     video::E_DRIVER_TYPE driverType;

 

     printf("Please select the driver you want for this example:\n"\

         " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\

         " (d) Software Renderer\n (e) Burning's Software Renderer\n"\

         " (f) NullDevice\n (otherKey) exit\n\n");

 

     char i;

     std::cin >> i;

 

     switch(i)

     {

         case 'a': driverType = video::EDT_DIRECT3D9;break;

         case 'b': driverType = video::EDT_DIRECT3D8;break;

         case 'c': driverType = video::EDT_OPENGL;   break;

         case 'd': driverType = video::EDT_SOFTWARE; break;

         case 'e': driverType = video::EDT_BURNINGSVIDEO;break;

         case 'f': driverType = video::EDT_NULL;     break;

         default: return 0;

     }

 

     // create device

 

     IrrlichtDevice *device = createDevice(driverType,

              core::dimension2d(640, 480), 16, false);

        

     if (device == 0)

         return 1; // could not create selected driver.

 

     // create engine and camera

 

     device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo");

 

     video::IVideoDriver* driver = device->getVideoDriver();

     scene::ISceneManager* smgr = device->getSceneManager();

 

     smgr->addCameraSceneNode(0, core::vector3df(0,-40,0), core::vector3df(0,0,0));

 

     /*

     Create our scene node. I don't check the result of calling new, as it

     should throw an exception rather than returning 0 on failure. Because

     the new node will create itself with a reference count of 1, and then

     will have another reference added by its parent scene node when it is

     added to the scene, I need to drop my reference to it. Best practice is

     to drop it only *after* I have finished using it, regardless of what

     the reference count of the object is after creation.

     */

     CSampleSceneNode *myNode =

         new CSampleSceneNode(smgr->getRootSceneNode(), smgr, 666);

 

     /*

     To animate something in this boring scene consisting only of one

     tetraeder, and to show that you now can use your scene node like any

     other scene node in the engine, we add an animator to the scene node,

     which rotates the node a little bit.

     irr::scene::ISceneManager::createRotationAnimator() could return 0, so

     should be checked.

     */

     /* 建立一個自旋的動作 , 這裡irrlicht 提供了幾種動作選擇

         1: createRotationAnimator

         2: createFlyCircleAnimator

         3: createFlyStraightAnimator

         4: createTextureAnimator

         5: createDeleteAnimator

         6: createCollisionResponseAnimator

         7: createFollowSplineAnimator

*/

     scene::ISceneNodeAnimator* anim =

         smgr->createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));

 

     if(anim)

     {

         /* 將自旋動作程序加入到這個節點中 */

         myNode->addAnimator(anim);

        

         /*

         I'm done referring to anim, so must

         irr::IReferenceCounted::drop() this reference now because it

         was produced by a createFoo() function. As I shouldn't refer to

         it again, ensure that I can't by setting to 0.

         */

         /* anim 已設定完成, 所以執行 drop注消 */

         anim->drop();

         anim = 0;

     }

 

     /*

     I'm done with my CSampleSceneNode object, and so must drop my reference.

     This won't delete the object, yet, because it is still attached to the

     scene graph, which prevents the deletion until the graph is deleted or the

     custom scene node is removed from it.

     */

     /* myNode 設定完成, 使用drop注消 */

     /*

         drop 的原始程式如下:

         bool drop() const

         {

              // someone is doing bad reference counting.

              _IRR_DEBUG_BREAK_IF(ReferenceCounter <= 0)

 

              --ReferenceCounter;

              if (!ReferenceCounter)

              {

                   delete this;

                   return true;

              }

 

              return false;

         }

         每執行一次ReferenceCounter減一, ReferenceCounter=0 時才會真正delete 這個資料.

         ReferenceCounter 記錄著這個資料的引用數目.

         因為myNode已經登記到了 smgr 所以未執行 myNode->drop() ReferenceCounter 2,

         myNode 以及 smgr 各有一個引用的鏈結.

         所以執行下一行的myNode->drop() 並不會真正的刪除這個資料.

     */

     myNode->drop();

     myNode = 0; // As I shouldn't refer to it again, ensure that I can't

 

     /*

     Now draw everything and finish.

     */

     u32 frames=0;

     while(device->run())

     {

         driver->beginScene(true, true, video::SColor(0,100,100,100));

 

         smgr->drawAll();

 

         driver->endScene();

         if (++frames==100)

         {

              core::stringw str = L"Irrlicht Engine [";

              str += driver->getName();

              str += L"] FPS: ";

              str += (s32)driver->getFPS();

 

              device->setWindowCaption(str.c_str());

              frames=0;

         }

     }

 

     device->drop();

    

     return 0;

}

 

/*

That's it. Compile and play around with the program.

**/

 

沒有留言: