'Ogre3D 삽질란/Intermediate Tutorial 4'에 해당되는 글 3건

  1. 2008.12.14 중급 튜토리얼 4-1
  2. 2008.12.14 중급 튜토리얼 4-2
  3. 2008.12.14 중급 튜토리얼 4-3 (마지막)

중급 튜토리얼 4-1

Ogre3D 삽질란/Intermediate Tutorial 4 2008. 12. 14. 19:47

중급 튜토리얼 4 (번역 : n_Sys)

중급 튜토리얼 4: Volume Selection(영역 선택) Manual Object기초

튜토리얼 진행에 있어서 문제가 생긴다면 Help 포럼 문의하세요.

목차

                               1 소개

                               2 사전 준비사항

                               3 ManualObjects

                                       3.1 짤막한 3D객체 특강

                                       3.2 소개

                                       3.3 소스코드

                               4 Volume Selection (영역 선택)

                                       4.1 설정

                                       4.2 Mouse 핸들러

                                       4.3 PlaneBoundedVolumeListSceneQuery

                               5 A Final Note About Bounding Boxes

 

소개

튜토리얼에서는 영역 선택(volume selection) 대한 내용을 다룹니다. 마우스를 드래그하여 생성되는 흰색 사각형안에 포함되는 영역이 선택되게끔 하는 기법입니다. 마우스버튼을 떼면 선택된 영역안의 모든 객체들이 하이라이트화 됩니다. 이런 기능을 구현하기 위해서 2가지 종류의 객체들에 대한 사용법을 익혀야 합니다 : ManualObject(사각형생성용) PlaneBoundedVolumeListSceneQuery입니다. 앞으로 소개드릴 ManualObject 대한 기초적인 사용법은 간단한 소개에 불과한 내용이며 3D객체를 생성하는 내용은 담고있지 않습니다. 튜토리얼에서 필요한 내용만 다룰예정 입니다.

튜토리얼에 대한 코드는 여기 찾을 있습니다. 내용을 스스로 천천히 진행하면서 완성되어지는 결과물을 직접 확인하세요

 

사전 준비사항

프로젝트에서 Cpp 소스파일을 하나 생성한 다음 다음 코드를 추가하세요 :

#include <CEGUI/CEGUI.h>

#include <OgreCEGUIRenderer.h>

 

#include "ExampleApplication.h"

 

class SelectionRectangle : public ManualObject

{

public:

    SelectionRectangle(const String &name)

        : ManualObject(name)

    {

    }

 

    /**

    * Sets the corners of the SelectionRectangle.  Every parameter should be in the

    * range [0, 1] representing a percentage of the screen the SelectionRectangle

    * should take up.

    */

    void setCorners(float left, float top, float right, float bottom)

    {

    }

 

    void setCorners(const Vector2 &topLeft, const Vector2 &bottomRight)

    {

        setCorners(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);

    }

};

 

class DemoListener : public ExampleFrameListener, public OIS::MouseListener

{

public:

    DemoListener(RenderWindow* win, Camera* cam, SceneManager *sceneManager)

        : ExampleFrameListener(win, cam, false, true), mSceneMgr(sceneManager), mSelecting(false)

    {

        mMouse->setEventCallback(this);

    } // DemoListener

 

    ~DemoListener()

    {

    }

 

    /* MouseListener callbacks. */

    bool mouseMoved(const OIS::MouseEvent &arg)

    {

        CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);

        return true;

    }

 

    bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)

    {

        return true;

    }

 

    bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)

    {

        return true;

    }

 

    void performSelection(const Vector2 &first, const Vector2 &second)

    {

    }

 

   void deselectObjects()

   {

       std::list<MovableObject*>::iterator itr;

       for (itr = mSelected.begin(); itr != mSelected.end(); ++itr)

           (*itr)->getParentSceneNode()->showBoundingBox(false);

   }

 

   void selectObject(MovableObject *obj)

   {

       obj->getParentSceneNode()->showBoundingBox(true);

       mSelected.push_back(obj);

   }

 

private:

    Vector2 mStart, mStop;

    SceneManager *mSceneMgr;

    PlaneBoundedVolumeListSceneQuery *mVolQuery;

    std::list<MovableObject*> mSelected;

    SelectionRectangle *mRect;

    bool mSelecting;

 

 

    static void swap(float &x, float &y)

    {

        float tmp = x;

        x = y;

        y = tmp;

    }

};

 

class DemoApplication : public ExampleApplication

{

public:

    DemoApplication()

        : mRenderer(0), mSystem(0)

    {

    }

 

    ~DemoApplication()

    {

        if (mSystem)

            delete mSystem;

 

        if (mRenderer)

            delete mRenderer;

    }

 

protected:

    CEGUI::OgreCEGUIRenderer *mRenderer;

    CEGUI::System *mSystem;

 

    void createScene(void)

    {

        mRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr);

        mSystem = new CEGUI::System(mRenderer);

 

        CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");

        CEGUI::MouseCursor::getSingleton().setImage((CEGUI::utf8*)"TaharezLook", (CEGUI::utf8*)"MouseArrow");

 

        mCamera->setPosition(-60, 100, -60);

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

 

        mSceneMgr->setAmbientLight(ColourValue::White);

        for (int i = 0; i < 10; ++i)

            for (int j = 0; j < 10; ++j)

            {

                Entity *ent = mSceneMgr->createEntity("Robot" + StringConverter::toString(i + j * 10), "robot.mesh");

                SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(i * 15, 0, j * 15));

                node->attachObject(ent);

                node->setScale(0.1, 0.1, 0.1);

            }

    }

 

    void createFrameListener(void)

    {

        mFrameListener = new DemoListener(mWindow, mCamera, mSceneMgr);

        mFrameListener->showDebugOverlay(true);

        mRoot->addFrameListener(mFrameListener);

    }

};

 

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

#define WIN32_LEAN_AND_MEAN

#include "windows.h"

 

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

#else

int main(int argc, char **argv)

#endif

{

    // Create application object

    DemoApplication app;

 

    try {

        app.go();

    } catch(Exception& e) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

        MessageBoxA(NULL, e.getFullDescription().c_str(), "An exception has occurred!",

            MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

        fprintf(stderr, "An exception has occurred: %s\n",

            e.getFullDescription().c_str());

#endif

    }

 

    return 0;

}

컴파일이 제대로 되는지 확인하세요. 실행시키면 마우스커서를 움직일 있을겁니다. 하지만 외에는 아무기능도 없습니다. ESC키를 눌러서 빠져나오세요.

:

중급 튜토리얼 4-2

Ogre3D 삽질란/Intermediate Tutorial 4 2008. 12. 14. 19:46

ManualObjects

짤막한 3D객체 특강

메쉬를 만들기 전에 메쉬가 뭔지, 무엇으로 구성되는지 잠시 알고 넘어가도록 합시다. 간략하게 요약하면 메쉬는 전체적으로 크게 2파트로 구성됩니다: (vertex buffer)정점버퍼와 (index buffer)인덱스버퍼로 나뉩니다.

정점버퍼는 3D공간에서의 점들을 정의합니다. 정점버퍼에 대한 각각의 요소에 대해서 몇가지 속성들이 정의가능합니다. 필수적으로 설정해야 하는 속성은 정점의 위치입니다. 밖에도 정점의 색상, 텍스쳐좌표, 기타등등과 같은 선택가능한 수많은 설정용 옵션들이 있습니다. 메쉬로 무엇을 하느냐에 따라서 실제로 사용될 옵션은 가변적 입니다.

인덱스버퍼는 정점버퍼로부터 선택된 점들간의 연결입니다. GPU 의해서 그려지는 한개의 삼각형당 3개의 인덱스가 설정됩니다. 인덱스버퍼에서 선택되는 정점의 순서에 따라서 어느면이 앞면인지를 결정하게 됩니다. 삼각형의 앞면은 시계 반대방향으로 그려지게 됩니다. 시계방향으로 그려지는 반대면은 뒷면이 됩니다. 보통 삼각형의 앞면만 그려지게되므로 삼각형을 설정하는데 있어서 신중해야 합니다.

모든 메쉬가 정점버퍼를 가지고 있긴 하지만 인덱스버퍼는 없을 수도 있습니다. 예를 들어서 앞으로 사용될 텅빈 사각형(안이 채워진 사각형은 반대의 경우) 경우는 인덱스버퍼를 가지고 있지 않게 됩니다. 마지막으로 설명할 내용은 정점과 인덱스 버퍼는 대개 비디오카드의 메모리에 저장됩니다. 그러므로 프로그램은 미리 정의된 버퍼를 참조해 전체적인 3D메쉬가 그려지게하기 위해서 비디오카드로 적절한 명령조합들을 보내기만 하면 됩니다.

 

소개

오우거에서 메쉬를 생성하는 방법에는 2가지가 있습니다. 첫번째 방법은 SimpleRenderable클래스를 상속받아서 정점과 인덱스버퍼를 바로 넣어주는 방법입니다. 가장 직관적으로 생성하는 방법입니다. 하지만 가장 어려운 방법이기도 합니다. The GeneratingAMesh code snippet 이것에 대한 예제가 있습니다. 쉽게 하기 위해서 오우거는 ManualObject라는 훨씬 괜찮은 인터페이스를 제공합니다. raw데이터를 버퍼객체에 넣는것 대신에 간단한 함수를 이용해서 메쉬를 정의할 있도록 도와줍니다. 위치, 색상등 버퍼로 입력해줘야 하는 작업들이 간단하게 "position", "colour”함수를 호출하는것으로 해결됩니다.

튜토리얼에서는 마우스드래그로 선택될 객체들을 표시하기위해서 흰색 사각형을 생성해야 합니다. 오우거에서는 2D 사각형을 표시하는데 있는 클래스가 마땅치 않습니다. 어떻게 해야할지 따로 방법을 찾아내야 합니다. 오버레이를 이용해서 선택영역을 표시하고 리사이징을 있습니다. 그러나 문제는 선택영역으로 쓰일 사각형은 크기가 매우 가변적일 것이기 때문에 내부의 이미지를 늘리고 줄이는 과정에서 모양새가 깨질 가능성이 있습니다. 그러므로 선택영역용 사각형으로 동작될 매우 단순한 2D 메쉬를 생성시킬 입니다.

 

소스코드

선택용 사각형을 생성할때 2D처럼 보이게 해야 합니다. 그리고 그려질 오버레이를 다른 객체들 중에서 가장 상단에 위치하게 하여야 합니다. 과정은 어렵지 않습니다. SelectionRectangle생성자로 가서 다음 코드를 추가하세요 :

       setRenderQueueGroup(RENDER_QUEUE_OVERLAY);

       setUseIdentityProjection(true);

       setUseIdentityView(true);

       setQueryFlags(0);

첫번재 함수는 오버레이queue 렌더queue 설정합니다. 다음 2개의 함수들은 projection view 행렬이 identity 되도록 설정합니다. Projection view 행렬은 대부분의 렌더링 시스템(OpenGL이나 DirectX 포함하여)에서 객체들이 어디에서 보여야 하는지에 대해 사용됩니다. 오우거에서는 이러한 과정들을 간략화시켜 줍니다. 이러한 행렬값들이 구체적으로 어떤 과정을 거치는지는 다루지 않을겁니다. 대신 최소한 알아둬야 것은 지금상황처럼 projectionview 매트릭스를 identity 시키는 경우는 기본적으로 2D객체를 생성시켜야 할때 입니다. 객체를 생성시킬때의 좌표시스템이 약간 바뀝니다. 이상 Z축은 의미가 없어집니다(Z축값이 필요한 경우는 -1 대입합니다). 대신에 X, Y -1부터 1까지로 표시되는 새로운 좌표시스템을 얻게 됩니다. 마지막으로 선택용 사각형 자체가 쿼리결과에 포함되지 않도록 객체의 쿼리flag값은 0으로 설정합니다.

이제 객체가 설정되었으므로 실제로 사각형을 만들차례 입니다. 그런데 시작하기전에 작은 골칫거리가 있습니다. 함수는 마우스위치와 연관되서 호출될겁니다. 0~1사이의 x, y좌표를 얻기전에 [-1, 1]사이의 범위로 변환시켜주는 코드가 필요합니다. 약간의 계산이 필요합니다. 게다가 y축은 반대방향입니다. CEGUI에서는 마우스커서가 화면 상단에 위치할때 0, 하단일 경우1 값을 가집니다. 하지만 새로운 좌표시스템에서는 화면 상단은 +1, 하단은 -1입니다. 다행스럽게도 문제를 해결하는 간단한 변환식이 있습니다. setCorners함수를 찾아서 다음 코드를 추가하세요 :

       left = left * 2 - 1;

       right = right * 2 - 1;

       top = 1 - top * 2;

       bottom = 1 - bottom * 2;

이제 새로운 시스템에 맞춰서 위치를 잡았습니다. 이제 실제로 객체를 생성합니다. 그러기 위해서 begin멤버함수를 호출합니다. 2개의 매개변수를 받는데 객체에서 쓰일 재질이름과 그리는데 사용될 render operation입니다. 텍스쳐는 넣지 않을 계획이므로 재질란은 공백으로 놔둡니다. 두번째 매개변수는 RenderOperation입니다. 메쉬를 그리는데 또는 , 삼각형이 사용될 있습니다. 완전한 메쉬를 그리고 싶다면 삼각형을 사용하면 됩니다. 하지만 지금은 텅빈 사각형만 그릴것이므로 line strip 쓰게 것입니다. Line strip 미리 정의된 이전 정점으로부터 현재정점까지의 선을 그려나가는 방식입니다. 그러므로 사각형을 그리기 위해서는 5개의 점이 필요합니다(처음과 마지막점은 같은 위치로 연결되어 전체 사각형을 연결합니다) :

       clear();

       begin("", RenderOperation::OT_LINE_STRIP);

           position(left, top, -1);

           position(right, top, -1);

           position(right, bottom, -1);

           position(left, bottom, -1);

           position(left, top, -1);

       end();

자주 불려지게 함수이므로 새로 그려지기전에 이전에 그려졌던 사각형을 지우는 과정이 필요합니다. Manual object 정의할때 begin/end 여러번 호출하여 다수의 sub-mesh 만들수도 있습니다(다른 타입의 재질/ RenderOperation 사용해도 됩니다). 2D객체를 정의하므로 Z축은 쓰지 않습니다. 그러므로 Z 매개변수는 -1 되어야 합니다. -1 설정하면 그려질때 카메라 또는 뒤에 위치하지 않게 됩니다.

마지막으로 해야 일은 객체를 위해 바운딩박스를 설정하는 입니다. 많은 SceneManager에서는 객체가 화면에서 벗어날때 그려지는 대상에서 제외됩니다. 기본적으로는 2D객체를 생성하지만 오우거는 여전히3D엔진으로 동작됩니다. 2D객체라고해도 3D공간에 놓여지게 됩니다. , 이렇게 2D객체를 생성하고 SceneNode attach 이후(다음 섹션에서 하게 겁니다) 다른곳으로 시야를 돌리면 사라지게 된다는 의미입니다. 문제점을 고치기 위해서 객체의 바운딩박스가 infinite속성을 가지도록 설정합니다. 그렇게 하면 카메라는 항상 바운딩박스 안에 위치하게 것입니다 :

       AxisAlignedBox box;

       box.setInfinite();

       setBoundingBox(box);

참고로 바운딩박스를 모두 지우는 함수가 호출된 이후에 코드가 추가되었습니다. 매번 ManualObject::clear 호출될때마다 바운딩박스는 리셋될 것입니다. 자주 지워지는 다른 ManualObject 생성할시 유의하세요. 왜냐하면 다시 생성될때마다 바운딩박스를 다시 설정해줘야 하기 때문입니다.

SelectionRectangle 대한 코딩은 끝입니다. 지금 컴파일이 되는지 시험해 보세요. 하지만 아직까지 새로 추가된 기능은 없습니다.

 

영역 선택

설정

영역선택관련 코드로 넘어가기전에 몇가지 설정할 것이 있습니다. 우선은 SelectionRectangle클래스의 객체를 생성해야 합니다. 그리고 SceneManager 영역쿼리를 생성해야 합니다. 다음 코드를 DemoListener생성자에 추가하세요 :

       mRect = new SelectionRectangle("Selection SelectionRectangle");

       mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(mRect);

 

       mVolQuery = mSceneMgr->createPlaneBoundedVolumeQuery(PlaneBoundedVolumeList());

다음 frame listener 썼으면 지워주는 코드가 필요합니다. ~DemoListener 다음 코드를 추가하세요 :

       mSceneMgr->destroyQuery(mVolQuery);

       delete mRect;

이제 자동으로 SceneManager 쿼리를 삭제시켜줄 있게 되었습니다.

 

Mouse 핸들러

구현하고자 하는 기능은 영역선택기능 입니다. 만약 유저가 마우스를 클릭후 움직이면서 화면을 드래그하면 사각형이 그려질 입니다. 마우스버튼을 뗀다면 사각형 안에 있는 모든 객체들이 선택될 입니다. 우선 마우스버튼이 눌렸을때의 상황을 처리해야 합니다. 사각형 시작지점을 저장하고 SelectionRectangle 보이도록 설정해야 합니다. mousePressed함수를 찾아서 다음 코드를 추가하세요 :

       if (id == OIS::MB_Left)

       {

           CEGUI::MouseCursor *mouse = CEGUI::MouseCursor::getSingletonPtr();

           mStart.x = mouse->getPosition().d_x / (float)arg.state.width;

           mStart.y = mouse->getPosition().d_y / (float)arg.state.height;

           mStop = mStart;

 

           mSelecting = true;

           mRect->clear();

           mRect->setVisible(true);

       }

알아두실 점은 OIS 마우스좌표가 아닌 CEGUI::MouseCursor x, y좌표를 사용한다는것 입니다. 이유는 OIS 가끔씩 CEGUI 출력하는 위치와는 다른위치에 있다고 잘못 인식하기 때문입니다. 유저가 실제로 바라보는 화면과 일치시키기 위해서 CEGUI 마우스 좌표를 사용하는 입니다.

다음은 사용자가 마우스버튼을 해제시켰을때 선택용 사각형출력을 중단하고 선택쿼리를 수행할 차례입니다. MouseReleased 다음 코드를 추가하세요 :

       if (id == OIS::MB_Left)

       {

           performSelection(mStart, mStop);

           mSelecting = false;

           mRect->setVisible(false);

       }

그리고 마지막으로 해줄 작업은 마우스가 움직이면 사각형을 새로운 좌표로 업데이트 시켜줘야 합니다 :

       if (mSelecting)

       {

           CEGUI::MouseCursor *mouse = CEGUI::MouseCursor::getSingletonPtr();

           mStop.x = mouse->getPosition().d_x / (float)arg.state.width;

           mStop.y = mouse->getPosition().d_y / (float)arg.state.height;

 

           mRect->setCorners(mStart, mStop);

       }

마우스가 움직일때 마다 mStop 벡터값을 교정해 주기때문에 setCorners멤버함수에서 간단하게 사용될 있습니다. 컴파일 실행시켜 보세요. 이제 마우스를 이용해서 사각형을 그릴 있습니다.

 

PlaneBoundedVolumeListSceneQuery

이제 SelectionRectangle객체를 제대로 그릴 있게 되었습니다. 이제는 영역선택이 수행되도록 만들어야 합니다. performSelection함수에 다음 코드를 추가하세요 :

       float left = first.x, right = second.x,

           top = first.y, bottom = second.y;

 

       if (left > right)

           swap(left, right);

 

       if (top > bottom)

           swap(top, bottom);

부분에서는 매개변수의 left, right, top, bottom변수를 대입했습니다. If구문에서는 실제로 가장 낮은 값을 left top 대입합니다. (만약 사각형이거꾸로그려졌다면 우측하단은 좌측상단으로 값이 설정되기때문에 값을 바꾸는 작업을 해줘야 합니다.)

다음에는 사각형 영역이 얼마나 큰지를 검사해야 합니다. 만약 사각형이 너무 작다면 영역경계를 제대로 설정할 수가 없게되며 객체가 너무 많게 혹은 적게 선택될 입니다. 만약 사각형이 화면크기에 비해서 너무 낮은 비율의 크기를 가진다면 선택동작은 수행되지 않을 입니다. 여기서는 적당히 0.0001값을 쿼리취소 경계값으로 두었습니다. 값은 여러분들이 테스트 해보고 적당히 조절해도 됩니다. 실제로 쓰이는 프로그램에서는 사각형의 중심점을 찾아서 아무것도 하지않는것 대신에 일반적인 RaySceneQuery 수행하기도 합니다 :

       if ((right - left) * (bottom - top) < 0.0001)

           return;

바로 부분이 함수의 핵심부분입니다. 이제 자체적인 쿼리를 수행할 차례입니다. PlaneBoundedVolumeQuery 평면으로 닫힌공간을 정의합니다. 다음 공간에 포함된 객체들을 선택하게 됩니다. 예제를 위해서 안쪽으로 향한 5개의 평면으로 구성된 닫힌공간을 생성할 입니다. 사각형을 위한 평면들을 생성하기 위해서 각각의 사각형 꼭지점을 담당하게 4개의 광선을 생성합니다. 4개의 광선을 생성하고 광선을 따라서 평면을 생성하게될 적당한 포인트를 잡게 것입니다 :

       Ray topLeft = mCamera->getCameraToViewportRay(left, top);

       Ray topRight = mCamera->getCameraToViewportRay(right, top);

       Ray bottomLeft = mCamera->getCameraToViewportRay(left, bottom);

       Ray bottomRight = mCamera->getCameraToViewportRay(right, bottom);

이제 평면을 생성해 봅시다. 광선을 따라서 100 unit만큼 떨어진 지점을 잡습니다. 역시 적당히 정한수치 입니다. 100대신에 2로도 있습니다. 평면에서 매우 가까운 정면값으로 카메라로 부터 3 unit만큼 떨어진 위치를 시작점으로 정할 입니다. 

       PlaneBoundedVolume vol;

       vol.planes.push_back(Plane(topLeft.getPoint(3), topRight.getPoint(3), bottomRight.getPoint(3)));         // front plane

       vol.planes.push_back(Plane(topLeft.getOrigin(), topLeft.getPoint(100), topRight.getPoint(100)));         // top plane

       vol.planes.push_back(Plane(topLeft.getOrigin(), bottomLeft.getPoint(100), topLeft.getPoint(100)));       // left plane

       vol.planes.push_back(Plane(bottomLeft.getOrigin(), bottomRight.getPoint(100), bottomLeft.getPoint(100)));   // bottom plane

       vol.planes.push_back(Plane(topRight.getOrigin(), topRight.getPoint(100), bottomRight.getPoint(100)));     // right plane

평면들은 “open box” 로써 카메라 정면으로부터 무한히 확장되는 형태 입니다. 그려지는 사각형은 카메라 정면으로부터 종점까지의 포인트를 잇는 박스라고 생각해도 됩니다. 평면들이 모두 생성되었으므로 이제 쿼리를 실행할 차례입니다 :

       PlaneBoundedVolumeList volList;

       volList.push_back(vol);

 

       mVolQuery->setVolumes(volList);

       SceneQueryResult result = mVolQuery->execute();

이제 마지막 단계로 쿼리에 대한 결과를 처리할 차례입니다. 먼저 이전에 선택되었던 모든 객체들을 선택해제시키며 쿼리로 찾아낸 객체들을 선택하게 됩니다. deselectObjects, selectObject함수는 이전 튜토리얼에서 다룬내용으로 작성되었으므로 이미 완성되어 있습니다 :

       deselectObjects();

       SceneQueryResultMovableList::iterator itr;

       for (itr = result.movables.begin(); itr != result.movables.end(); ++itr)

           selectObject(*itr);

쿼리를 위한 모든 작업이 끝났습니다. 비록 튜토리얼에서는 다루지 않았지만 영역 선택을 위해서도 쿼리flag  사용될 있습니다. 이전 튜토리얼을 참고하여 쿼리flag 대한 자세한내용을 참고하세요.

컴파일 실행시켜 보세요. 영역선택을 이용해서 객체들을 선택할 있습니다!

:

중급 튜토리얼 4-3 (마지막)

Ogre3D 삽질란/Intermediate Tutorial 4 2008. 12. 14. 19:44

바운딩 박스에 대한 부연 설명

튜토리얼 뿐만 아니라 이전 2개의 튜토리얼에서도 많이 보아왔듯 오우거에서의 선택은 메쉬에 포함되지 않은 객체의 바운딩박스를 이용해서 선택하는 방식을 취하고 있습니다.   RaySceneQuery, PlaneBoundedVolumeQuery 항상 실제로 쿼리에 포함되는 객체들보다 많이 선택된다는 의미 입니다. 픽셀기준의 완벽한 광선 선택법(FPS 건슈팅게임같이 정확한 hit 필요로 하는 상황에서 필요할 입니다 ) 정확성은 떨어지지만 속도를 향상시킬 있는 영역선택법이 있습니다. 불행하게도 자세한 내용은 튜토리얼의 범주를 넘는 내용입니다. 순수 오우거만을 이용해서 세밀한 선택을 하고 싶다면 Raycasting to the polygon level 참조하세요. 만약 OgreNewt같은 물리효과 관련 라이브러리와 오우거를 통합시킨다면 역시 세밀한 선택을 있도록 기능을 제공받을 있을 입니다.

광선쿼리와 영역쿼리에 대해서 모두 다루지는 못했습니다. 장면의 모든 메쉬들에 대해서 각각 선택하는 방식은 시간이 많이 걸리고 프레임율을 떨어트리는 결과를 가져올 입니다. 사실실제로쓰이는 마우스를 통한 선택방법은 먼저 오우거쿼리(RaySceneQuery같은) 수행한 다음 리턴되는 쿼리결과들의 겹치는 부분을 물리엔진으로 개별적으로 메쉬형태를 추적하여 실제로 hit되었는지 혹은 hit되지 않고 스쳤는지를 다시 검사하는 형태로 수행됩니다.

 

: