레이블이 Ogre3D인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Ogre3D인 게시물을 표시합니다. 모든 게시물 표시

2010년 3월 9일 화요일

ogre, 비율을 유지하며 리사이징하기

이게 몇년만의 서브클래싱이냐?
win32 API 들이 아직도 기억나는게 신기하다.


그럴듯한 창모드 어플을 만드려면 리사이징이 자연스러워야 하는데 ogre 쪽에선 이걸 해주는 코드를 못찾았다. 윈도이벤트리스너 라고 리스너를 달아서 windowMoved, windowResized 등을 받는게 가능하긴 한데 리사이즈 된 뒤의 이벤트를 받는 거라 내가 원하는 동작을 하기엔 좀 무리가 있었다. 하지만 이 놈은 windowClosed 등 유용한 이벤트들이 떨어지니 어차피 코딩을 해야할놈이긴 하다.. 이쪽 코드는 블로그에 적어두진 않겠지만 필요하면  WindowEventUtilities 와 WindowEventListener 클래스를 참고해보자.


결국 내가 원하는 WM_SIZING 처리를 위해서는.. 서브클래싱을 해야했다. 헐 추억의 서브클래싱. WindowEventUtilities 가 wndProc 을 들고있으니 적절히 참고하면서 아래 코드를 만들었다. 아래 코드는 아직 리사이징이 좀 어색한게 남아있는데.. 일단 여기 적어둔다.

아 리사이징 가장 어색한게 window7 에서 사이징을 하면 모서리에 붙어버리는듯한 동작이 나오던데.. 간만에 msdn 좀 뒤져봐야겠다.

[code cpp]
typedef LRESULT (*WindowProcType)(HWND,UINT,WPARAM,LPARAM);
WindowProcType oldWndProc;
static void subclassRenderWindow(RenderWindow* w);
static LRESULT wndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam);

void subclassRenderWindow(RenderWindow* w)
{
    if(oldWndProc) return;
    HWND handle;
    w->getCustomAttribute("WINDOW", &handle);
    oldWndProc = (WindowProcType)SetWindowLong(handle, GWL_WNDPROC, (DWORD)&wndProc);
};
LRESULT wndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    // WindowEventUtilities::_WndProc 에 의하면 WM_CREATE 보다 WM_SIZE
    // 가 먼저 떨어질수도 있다네. 그랬던가? 어쨌건 저놈이 관련처리를
    // 해주고 있으니 나도 그코드를 퍼올렸다.
    // RenderWindow* w = (RenderWindow*)GetWindowLongPtr(wnd,GWLP_USERDATA);
    // if(!w) return oldWndProc(wnd, msg, wparam, lparam);

    switch(msg)
    {
    // 최소 사이즈 지정
    case WM_GETMINMAXINFO:
    {
        MINMAXINFO* mmi = (MINMAXINFO*)lparam;
        mmi->ptMinTrackSize.x = 800;
        mmi->ptMinTrackSize.y = 600;
        return 0;
    }
    // 아 씨댕 생각보다 복잡한 코드를 만들어야 하네.
    // 어느위치로 땡기는지에 따라 달리 계산해줘야 한다.
    // 조건을 좀더 합치는것도 되겠지만 아직 부자연스러운 움직임이 있어 그냥 둬본다.
    case WM_SIZING:
    {
        const double ratio = 1.33333;
        RECT* rc = (RECT*)lparam;
        int width = rc->right - rc->left;
        int height = rc->bottom - rc->top;
        if(wparam == WMSZ_LEFT || wparam == WMSZ_RIGHT)
        {
            int h = static_cast<int>(width / ratio);
            rc->bottom = rc->top + h;
        }
        else if(wparam == WMSZ_TOP || wparam == WMSZ_BOTTOM)
        {
            int w = static_cast<int>(height * ratio);
            rc->right = rc->left + w;
        }
        else if(wparam == WMSZ_TOPLEFT || wparam == WMSZ_BOTTOMLEFT)
        {
            int w = static_cast<int>(height * ratio);
            rc->left = rc->right - w;
        }
        else if(wparam == WMSZ_TOPRIGHT || wparam == WMSZ_BOTTOMRIGHT)
        {
            int w = static_cast<int>(height * ratio);
            rc->right = rc->left + w;
        }
        return FALSE;
    }
    default:
        break;
    }
    return oldWndProc(wnd, msg, wparam, lparam);
}
[/code]



2010/07/08 추가.

와.. 이게 생각보다 빡치는 문제였네.
이전 코드는 비율만 맟춰본 거였고.. 사실상 크기를 정확히 맟추려면 약간의 보정을 해줘야 한다. WM_SIZING 시 떨어지는 rect 는 스크린좌표이기 때문에 내가 원하는 크기가 800*600 이라고 해서 800*600 으로 맟춰버리면 윈도우가 그려주는 넌클라이언트 영역들때문에 800*600 에서 조금씩 짤린 크기가 되버린다. 이런 경우를 위해 AdjustWindwRect(Ex) 함수가 있는데.. CreateWindow 가 오우거안에 숨어있어서 ( D3DRenderWindow::create 참고 ).. 내가 원하던 클라이언트 사이즈와 만들어진 윈도우의 스크린 사이즈의 차이를 기억해뒀다가 sizing 시 보정해주는 방법을 썼다.

아래는 코드 일부,  내가 원했던 크기와 실제 만들어진 크기의 차이를 기억해 두는 함수
[code cpp]
void FerrariApp::prepareSizing()
{
    HWND handle;
    getRenderWindow()->getCustomAttribute("WINDOW", &handle);

    int wantedWidth  = config.getVideoWidth();
    int wantedHeight = config.getVideoHeight();

    RECT rc;
    GetWindowRect(handle, &rc);
    int realWidth  = rc.right - rc.left;
    int realHeight = rc.bottom - rc.top;

    adjustSize_.first  = realWidth - wantedWidth;
    adjustSize_.second = realHeight - wantedHeight;

    trace("adjustSize_ %d %d\n", adjustSize_.first, adjustSize_.second);
}
[/code]

다시 코드 일부, 처음 적었던 코드에 위에서 기억해둔 사이즈를 보정해주는 코드를 추가한것
[code cpp]
LRESULT FerrariApp::onSizing(HWND hwnd, WPARAM wparam, LPARAM lparam)
{
    const double ratio = static_cast<double>(config.getVideoWidth()) / static_cast<double>(config.getVideoHeight());

    RECT* rc = (RECT*)lparam;

    rc->left   += adjustSize_.first  / 2;
    rc->top    += adjustSize_.second / 2;
    rc->right  -= adjustSize_.first  / 2;
    rc->bottom -= adjustSize_.second / 2;
   
    double width = rc->right - rc->left;
    double height = rc->bottom - rc->top;
    if(wparam == WMSZ_LEFT || wparam == WMSZ_RIGHT)
    {
        int h = static_cast<int>(width / ratio);
        rc->bottom = rc->top + h;
    }
    else if(wparam == WMSZ_TOP || wparam == WMSZ_BOTTOM)
    {
        int w = static_cast<int>(height * ratio);
        rc->right = rc->left + w;
    }
    else if(wparam == WMSZ_TOPLEFT || wparam == WMSZ_BOTTOMLEFT)
    {
        int w = static_cast<int>(height * ratio);
        rc->left = rc->right - w;
    }
    else if(wparam == WMSZ_TOPRIGHT || wparam == WMSZ_BOTTOMRIGHT)
    {
        int w = static_cast<int>(height * ratio);
        rc->right = rc->left + w;
    }

    rc->left   -= adjustSize_.first  / 2;
    rc->top    -= adjustSize_.second / 2;
    rc->right  += adjustSize_.first  / 2;
    rc->bottom += adjustSize_.second / 2;
   
    return FALSE;
}
[/code]

아직도 좀 어색하게 돌아가는데 천천히 잡아보자.






2010년 3월 5일 금요일

ogre, archive 관련 작업을 시작하기위한 테스트 코드

예를들어 foo 라는 포맷을 정의하고 이를 ogre 가 처리하도록 하려면 아래 세개의 클래스를 구현하고
  1. FooArchive : 디렉토리 처리? 뭐 그런거라고 보자
  2. FooArchiveFactory : 보일러플레이트
  3. FooDataStream : 파일스트림 처리? 그렇게 생각하면 굳
이중 FooArchiveFactory 를 ArchiveManager 에 등록한 후에
리소스매니저를 통해 리소스에 접근이 가능하다.

아직 파일포맷에 대해서 생각하지는 않았지만 위와 같은 과정을 테스트해보기 위한 코드를 만들어보고 적어둔다.

fooarchive.hpp 는 위 세 클래스의 선언을 모두 담았다.

펼쳐두기..


fooarchive.cpp 는 FooArchive 클래스의 구현만 담았다.

펼쳐두기..


fooarchivefactory.cpp 는 FooArchvieFactory 클래스의 구현만 담았다.

펼쳐두기..


foodatastream.cpp 는 FooDataStream 클래스의 구현만 담았다. 이쪽은 대강 구현한거라 버그가 많을듯. 어차피 내용 붙이면 모두 새로구현해야한다.

펼쳐두기..


archive.cpp 는 위 세 클래스를 써먹는 예제를 담았다. 등록하는 코드도 봐두자..

펼쳐두기..

위 소스들을 빌드하는데 쓰인 CMakeLists.txt 의 일부. 아래 언급된 link_ogre3d_all() 매크로는 다른 파일에 있는데 적지 않았다.

펼쳐두기..




2010년 2월 21일 일요일

ogre3d 창모드일때 마우스 처리에 대해서 조금 적어둔다

OIS 를 non exclusive 모드로 돌리는것은 아래 링크대로 코드 추가
http://www.ogre3d.org/wiki/index.php/Using_OIS#Non-exclusive_input

[code cpp]
    paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_FOREGROUND" )));
    paramList.insert(std::make_pair(std::string("w32_mouse"), std::string("DISCL_NONEXCLUSIVE")));
    paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_FOREGROUND")));
    paramList.insert(std::make_pair(std::string("w32_keyboard"), std::string("DISCL_NONEXCLUSIVE")));
[/code]

그리고 기존 MouseMoved 에서 상대좌표로 마우스무브 인젝션 하던것을 절대좌표로 포지션 인젝션. 흠. 애초에 왜 상대좌표 썼는지는 원코드 쓴이에게 물어봐야 겠는데 분위기 보니 오우거 샘플이 원래 저걸 쓴모양인듯.

[code cpp]
OgreFramework::getSingletonPtr()->m_pGUISystem->injectMousePosition(evt.state.X.abs, evt.state.Y.abs);
[/code]

아 그리고 시스템 마우스 커서가 남아있으니 숨기는 코드도 필요. 적당히 ShowCursor 불러주면 끝. 이거 그다기 깔끔한 처리는 아닌데 다른 방법을 찾지 못했다.
[code cpp]
::ShowCursor(FALSE);
[/code]