헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤헤
2010년 3월 27일 토요일
2010년 3월 26일 금요일
Microsoft Application Verifier
http://www.microsoft.com/downloads/details.aspx?familyid=c4a25ab9-649d-4a1b-b4a7-c9d8b095df18&displaylang=en
이런게 있었네.
valgrind 정도의 기능을 원하는데.. 일단 함 써보자.
사용법은 http://msdn.microsoft.com/en-us/library/aa480483.aspx 또는 그냥 appverif.exe 실행
당연히 valgrind 만큼 편하진 않은데 로우리소스 테스트등 재미난게 보인다.
추가.
으음...
그닥 맘에 드는 툴은 아니네.
그래도 기억해두자.
추가
http://stackoverflow.com/questions/413477/is-there-a-good-valgrind-substitute-for-windows
도 적어둔다. 주로 상용툴 야그라 나랑 무관하지만.
google perftools 로 heap 만이라도 체크해볼까? 흠
2010년 3월 21일 일요일
파이어폭스 검색 사이트 등록, 파이어제스쳐 RD 기능
사무실에서는 영어사전을 깔아서 쓰고있는데 집에까지 깔기는 싫고.. 어쩌다 검색할일이 있지만 집에선 키보드위에 손올리고 있는일이 적으니 마우스만으로 해야했다. 사무실이야 손이 키보드에 붙어있으니 파폭 키워드 검색으로 대부분 해결을 하지만...
그래서 파이어제스쳐중 RD 기능을 쓰기로 헀다.
http://mycroft.mozdev.org/
을통해 네이버영어사전을 등록하고 단어 선택후 RD 긁으면 끝.
2010년 3월 19일 금요일
cmake, visual studio 사용자를 위해서 솔루션 뽑아주는 배치파일
나야 nmake, jom 류를 쓰지만 다른이들은 스튜디오를 주로 쓰니 아래 배치파일을 만들어 mkbld.bat 등으로 이름지어서 소스트리에 포함했다. bootstrap.bat 등이 더 적당한 이름이겠지만..
bld 를 만들고 그안에 debug, release 를 각각 만들어서 솔루션을 만들어주는 구조.
솔루션만들거면 디버그 릴리즈 구분하지 않아도 되는데 그럴경우 install 명령이 릴리즈 타겟만 설치하려고 하는 문제가 있어서 (버그인가?) 이런 형태로 사용하게 됐다.
bld 디렉토리는 는 hg 등에서 ignore 해주는것 잊지말자.
[code]
@echo off
if exist bld (
goto err
)
mkdir bld
cd bld
mkdir debug
cd debug
cmake -D CMAKE_CONFIGURATION_TYPES:string=Debug ..\..
cd ..
mkdir release
cd release
cmake -D CMAKE_CONFIGURATION_TYPES:string=Release ..\..
cd ..\..
goto exit
:err
@echo bld directory exists
:exit
@echo on
[/code]
bld 를 만들고 그안에 debug, release 를 각각 만들어서 솔루션을 만들어주는 구조.
솔루션만들거면 디버그 릴리즈 구분하지 않아도 되는데 그럴경우 install 명령이 릴리즈 타겟만 설치하려고 하는 문제가 있어서 (버그인가?) 이런 형태로 사용하게 됐다.
bld 디렉토리는 는 hg 등에서 ignore 해주는것 잊지말자.
[code]
@echo off
if exist bld (
goto err
)
mkdir bld
cd bld
mkdir debug
cd debug
cmake -D CMAKE_CONFIGURATION_TYPES:string=Debug ..\..
cd ..
mkdir release
cd release
cmake -D CMAKE_CONFIGURATION_TYPES:string=Release ..\..
cd ..\..
goto exit
:err
@echo bld directory exists
:exit
@echo on
[/code]
2010년 3월 18일 목요일
jom, nmake 에 -j 기능(병렬 컴파일) 구현한놈 우왕ㅋ굳ㅋ
cmake 릴리즈노트를 읽다가 NMake Makefiles JOM 제네레이터가 추가된걸 보고 궁금해서 뒤져보니 우왕ㅋ굳ㅋ 이놈이 바로 nmake 에 -j 붙인놈이네.
http://labs.trolltech.com/blogs/2009/03/27/speeding-up-visual-c-qt-builds/
http://qt.gitorious.org/qt-labs/jom
다운로드
바로 테스트 해봤는데 시간 줄어드는거 측정까진 못해보고 코어 여러개 쓰는것만 확인했다.
바로 얼마전에 찾다가 못찾았는데 이제라도 찾았으니 다행.
그나저나 cmake 로 솔루션 만들어서 본격적으로 다른이들과 같이 써보니 문제가 좀 많네.
나야 nmake 나 jom 쓰면 쾌적하지만
헐..
솔루션쪽까지 만들어주는건 꽤나 성가시다.
http://labs.trolltech.com/blogs/2009/03/27/speeding-up-visual-c-qt-builds/
http://qt.gitorious.org/qt-labs/jom
다운로드
바로 테스트 해봤는데 시간 줄어드는거 측정까진 못해보고 코어 여러개 쓰는것만 확인했다.
바로 얼마전에 찾다가 못찾았는데 이제라도 찾았으니 다행.
그나저나 cmake 로 솔루션 만들어서 본격적으로 다른이들과 같이 써보니 문제가 좀 많네.
나야 nmake 나 jom 쓰면 쾌적하지만
헐..
솔루션쪽까지 만들어주는건 꽤나 성가시다.
mercurial subrepo
http://mercurial.selenic.com/wiki/subrepos
리소스가 덩치가 너무 커서 subrepo 를 이용 소스코드와 리소스를 분리해봤다.
근데 subrepo 쓰기가 좀 까다롭네. 아래쪽 리파지토리에서 푸쉬를 한후 위쪽 리파지토리에서 커밋을 해줘야 한다. 이 커밋을 잊지말자.
그리고 버그인지 clone 직후 default-path 가 이상하게 들어있다.( 패스구분자가 / 와 \ 가 섞인다) 현재는 매번 clone 후 수작업으로 손보고 있긴 한데... 뭐 수정되겠지
두가지만 기억해두자.
상위리포에서 커밋 잊지말것.
hg debugsub 명령 기억해둘것.
리소스가 덩치가 너무 커서 subrepo 를 이용 소스코드와 리소스를 분리해봤다.
근데 subrepo 쓰기가 좀 까다롭네. 아래쪽 리파지토리에서 푸쉬를 한후 위쪽 리파지토리에서 커밋을 해줘야 한다. 이 커밋을 잊지말자.
그리고 버그인지 clone 직후 default-path 가 이상하게 들어있다.( 패스구분자가 / 와 \ 가 섞인다) 현재는 매번 clone 후 수작업으로 손보고 있긴 한데... 뭐 수정되겠지
두가지만 기억해두자.
상위리포에서 커밋 잊지말것.
hg debugsub 명령 기억해둘것.
2010년 3월 12일 금요일
cmake, 서브프로젝트간 의존성 추적이 좀 부족해보인다.
예를들어 최상위 CMakeLists.txt 는 아래 세개의 서브프로젝트를 가진다.
[code]
add_subdirectory(foo)
add_subdirectory(bar)
add_subdirectory(main)
[/code]
foo, bar 는 라이브러리를, main 은 실행파일을 만드는데 main 에는 bar 를 링크해야 하고 bar 에는 foo 를 링크해야 한다. 뭐 사실상 라이브러리간 링크가 아니고 main 에 foo, bar 모두 링크해야 하지만 main 쪽에서 봤을때는 bar 만 보이고 foo 는 bar 뒤에 딸려오는 놈이라고 볼수있다. 즉 의미상 foo -> bar, bar -> main 의 관계가 생긴다.
foo 의 CmakeLists.txt 는 간단.
[code]
project(foo)
add_library(foo foo.c)
[/code]
bar 를 빌드할때는 foo 가 필요하다.
[code]
project(bar)
add_library(bar bar.c)
include_directories(${foo_SOURCE_DIR})
link_directories(${foo_BINARY_DIR})
target_link_libraries(bar foo)
[/code]
main 을 빌드할때는 bar 만 명시해주면 foo 는 cmake 가 알아서 붙여준다.
[code]
project(main)
add_executable(main main.c)
include_directories(${bar_SOURCE_DIR})
link_directories(${bar_BINARY_DIR})
target_link_libraries(main bar) // bar 가 필요하면 bar 에 필요한 foo 도 딸려온다.
[/code]
우왕굿.
그럴듯 해보인다.
main 은 직접적인 의존성이 걸린 bar 만 신경쓰면 되고 foo 는 존재조차 알 필요 없다.
하지만 저런식의 구성이 될경우 bar 는 foo 의 헤더를 인클루드 해서 main 에 까지 노출하는 경우가 태반이다. 즉 main 쪽에서 foo 쪽의 헤더를 찾아야 하고 cmake 는 링크 의존성은 줄줄이 해결해 주지만 인클루드 의존성은 해결해주지 않는다..
결국 main 의 CMakeLists.txt 에서 bar 뒤에 숨은 foo 에 접근을 해야할 필요가 생긴다
[code]
project(main)
add_executable(main main.c)
include_directories(${foo_SOURCE_DIR}) # 갓댐!
include_directories(${bar_SOURCE_DIR})
link_directories(${bar_BINARY_DIR})
target_link_libraries(main bar)
[/code]
흠. 뭔가 맘에 안드는 모양새다. bjam 은 include 의존성도 잘 풀어줬던걸로 기억나는데.. 뭐 cmake 가 계속 조금씩 나아지고 있으니 언젠가 방법이 나오겠지. 아니면 link_libraries 가 버려지고 target_link_libraries 가 나온것처럼 다른 명령어가 이미 추가됐을지도.. 는 아직(2.8 버전) 아닌거 같다.
추가.
흠 위에서 link_directories 를 매번 해줬는데 안해줘도 된다. target_link_libraries 가 알아서 경로까지 가져온다. boost 던가가 msvc 에서는 link_directories 를 해줘야 돌아서 버릇삼아 적은건데.. 후에 수정하자.
[code]
add_subdirectory(foo)
add_subdirectory(bar)
add_subdirectory(main)
[/code]
foo, bar 는 라이브러리를, main 은 실행파일을 만드는데 main 에는 bar 를 링크해야 하고 bar 에는 foo 를 링크해야 한다. 뭐 사실상 라이브러리간 링크가 아니고 main 에 foo, bar 모두 링크해야 하지만 main 쪽에서 봤을때는 bar 만 보이고 foo 는 bar 뒤에 딸려오는 놈이라고 볼수있다. 즉 의미상 foo -> bar, bar -> main 의 관계가 생긴다.
foo 의 CmakeLists.txt 는 간단.
[code]
project(foo)
add_library(foo foo.c)
[/code]
bar 를 빌드할때는 foo 가 필요하다.
[code]
project(bar)
add_library(bar bar.c)
include_directories(${foo_SOURCE_DIR})
link_directories(${foo_BINARY_DIR})
target_link_libraries(bar foo)
[/code]
main 을 빌드할때는 bar 만 명시해주면 foo 는 cmake 가 알아서 붙여준다.
[code]
project(main)
add_executable(main main.c)
include_directories(${bar_SOURCE_DIR})
link_directories(${bar_BINARY_DIR})
target_link_libraries(main bar) // bar 가 필요하면 bar 에 필요한 foo 도 딸려온다.
[/code]
우왕굿.
그럴듯 해보인다.
main 은 직접적인 의존성이 걸린 bar 만 신경쓰면 되고 foo 는 존재조차 알 필요 없다.
하지만 저런식의 구성이 될경우 bar 는 foo 의 헤더를 인클루드 해서 main 에 까지 노출하는 경우가 태반이다. 즉 main 쪽에서 foo 쪽의 헤더를 찾아야 하고 cmake 는 링크 의존성은 줄줄이 해결해 주지만 인클루드 의존성은 해결해주지 않는다..
결국 main 의 CMakeLists.txt 에서 bar 뒤에 숨은 foo 에 접근을 해야할 필요가 생긴다
[code]
project(main)
add_executable(main main.c)
include_directories(${foo_SOURCE_DIR}) # 갓댐!
include_directories(${bar_SOURCE_DIR})
link_directories(${bar_BINARY_DIR})
target_link_libraries(main bar)
[/code]
흠. 뭔가 맘에 안드는 모양새다. bjam 은 include 의존성도 잘 풀어줬던걸로 기억나는데.. 뭐 cmake 가 계속 조금씩 나아지고 있으니 언젠가 방법이 나오겠지. 아니면 link_libraries 가 버려지고 target_link_libraries 가 나온것처럼 다른 명령어가 이미 추가됐을지도.. 는 아직(2.8 버전) 아닌거 같다.
추가.
흠 위에서 link_directories 를 매번 해줬는데 안해줘도 된다. target_link_libraries 가 알아서 경로까지 가져온다. boost 던가가 msvc 에서는 link_directories 를 해줘야 돌아서 버릇삼아 적은건데.. 후에 수정하자.
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]
아직도 좀 어색하게 돌아가는데 천천히 잡아보자.
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월 8일 월요일
ogre, alt enter 로 창모드 전체모드 전환
오늘부로 오우거 1.7 로 갈아탔다.
cmake 기본지원에 boost 를 같이 배포하더군.
외부라이브러리를 껴서 배포하는건 좀 병신짓인데... 편하긴 하지만.. 흠
어쨌건 기존 내가 쓰던 cmake 파일을 FindOgre.cmake 를 쓰도록 적절히 수정하고
몇가지 api 가 바뀐게 있어서(제길) 조금 수정해주니 내가 지금껏 만들던 예제들은 잘 돌더라.
아직 오류가 남아있어서 종료처리가 비정상이긴 한데..
어쨌건 전체화면 전환 코드나 적어둔다.
렌더윈도우에서 크기를 얻어서 전체화면/창모드 만 바꾸는 코드
[code cpp]
void toggleFullscreen()
{
unsigned int width,height,color;
int left,top;
RenderWindow* w = app_->getRenderWindow();
w->getMetrics(width,height,color,left,top);
w->setFullscreen(!w->isFullScreen(),
width,
height);
}
[/code]
OIS 에서 alt-enter 를 받는건 enter 를 받은 시점에서 alt 를 검사하면 된다.
[code cpp]
virtual bool keyPressed(const OIS::KeyEvent& e)
{
switch(e.key)
{
case OIS::KC_ESCAPE:
keepRendering_ = false;
break;
case OIS::KC_RETURN:
if(keyboard_->isModifierDown(OIS::Keyboard::Alt))
toggleFullscreen();
break;
default:
break;
}
return true;
}
[/code]
cmake 기본지원에 boost 를 같이 배포하더군.
외부라이브러리를 껴서 배포하는건 좀 병신짓인데... 편하긴 하지만.. 흠
어쨌건 기존 내가 쓰던 cmake 파일을 FindOgre.cmake 를 쓰도록 적절히 수정하고
몇가지 api 가 바뀐게 있어서(제길) 조금 수정해주니 내가 지금껏 만들던 예제들은 잘 돌더라.
아직 오류가 남아있어서 종료처리가 비정상이긴 한데..
어쨌건 전체화면 전환 코드나 적어둔다.
렌더윈도우에서 크기를 얻어서 전체화면/창모드 만 바꾸는 코드
[code cpp]
void toggleFullscreen()
{
unsigned int width,height,color;
int left,top;
RenderWindow* w = app_->getRenderWindow();
w->getMetrics(width,height,color,left,top);
w->setFullscreen(!w->isFullScreen(),
width,
height);
}
[/code]
OIS 에서 alt-enter 를 받는건 enter 를 받은 시점에서 alt 를 검사하면 된다.
[code cpp]
virtual bool keyPressed(const OIS::KeyEvent& e)
{
switch(e.key)
{
case OIS::KC_ESCAPE:
keepRendering_ = false;
break;
case OIS::KC_RETURN:
if(keyboard_->isModifierDown(OIS::Keyboard::Alt))
toggleFullscreen();
break;
default:
break;
}
return true;
}
[/code]
2010년 3월 5일 금요일
ogre, archive 관련 작업을 시작하기위한 테스트 코드
예를들어 foo 라는 포맷을 정의하고 이를 ogre 가 처리하도록 하려면 아래 세개의 클래스를 구현하고
리소스매니저를 통해 리소스에 접근이 가능하다.
아직 파일포맷에 대해서 생각하지는 않았지만 위와 같은 과정을 테스트해보기 위한 코드를 만들어보고 적어둔다.
fooarchive.hpp 는 위 세 클래스의 선언을 모두 담았다.
fooarchive.cpp 는 FooArchive 클래스의 구현만 담았다.
fooarchivefactory.cpp 는 FooArchvieFactory 클래스의 구현만 담았다.
foodatastream.cpp 는 FooDataStream 클래스의 구현만 담았다. 이쪽은 대강 구현한거라 버그가 많을듯. 어차피 내용 붙이면 모두 새로구현해야한다.
archive.cpp 는 위 세 클래스를 써먹는 예제를 담았다. 등록하는 코드도 봐두자..
- FooArchive : 디렉토리 처리? 뭐 그런거라고 보자
- FooArchiveFactory : 보일러플레이트
- FooDataStream : 파일스트림 처리? 그렇게 생각하면 굳
리소스매니저를 통해 리소스에 접근이 가능하다.
아직 파일포맷에 대해서 생각하지는 않았지만 위와 같은 과정을 테스트해보기 위한 코드를 만들어보고 적어둔다.
fooarchive.hpp 는 위 세 클래스의 선언을 모두 담았다.
펼쳐두기..
[code cpp]
// 예를들어 foo 타입의 아카이브를 지원하려면
// 아래 세개의 클래스를 만들고
// 1. FooArchive
// 2. FooArchiveFactory
// 3. FooDataStream
//
// FooArchiveFactory 를 ArchiveManager 에 등록한후
// 리소스매니저를 통해 쓰면 된다.
#pragma once
#ifndef _FOOARCHIVE_H_
#define _FOOARCHIVE_H_
#include <OgrePrerequisites.h>
#include <OgreArchive.h>
#include <OgreArchiveFactory.h>
class FooArchive : public Ogre::Archive
{
public:
FooArchive(const Ogre::String name, const Ogre::String& archType);
~FooArchive();
bool isCaseSensitive() const { return true; }
void load();
void unload();
bool isReadOnly() const { return true; }
Ogre::DataStreamPtr open(const Ogre::String& filename) const;
Ogre::StringVectorPtr list(bool recursive=true, bool dirs=false);
Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false);
Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true, bool dirs = false);
Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, bool recursive = true, bool dirs = false);
bool exists(const Ogre::String& filename);
time_t getModifiedTime(const Ogre::String& filename);
};
class FooArchiveFactory : public Ogre::ArchiveFactory
{
public:
virtual ~FooArchiveFactory();
const Ogre::String& getType() const;
Ogre::Archive *createInstance(const Ogre::String& name);
void destroyInstance(Ogre::Archive* arch);
};
class FooDataStream : public Ogre::DataStream
{
public:
FooDataStream(const Ogre::String& name);
size_t read(void* buf, size_t count);
void skip(long count);
void seek(size_t pos);
size_t tell() const;
bool eof() const;
void close();
private:
size_t idx_;
char buf_[26];
};
#endif /* _FOOARCHIVE_H_ */
[/code]
// 예를들어 foo 타입의 아카이브를 지원하려면
// 아래 세개의 클래스를 만들고
// 1. FooArchive
// 2. FooArchiveFactory
// 3. FooDataStream
//
// FooArchiveFactory 를 ArchiveManager 에 등록한후
// 리소스매니저를 통해 쓰면 된다.
#pragma once
#ifndef _FOOARCHIVE_H_
#define _FOOARCHIVE_H_
#include <OgrePrerequisites.h>
#include <OgreArchive.h>
#include <OgreArchiveFactory.h>
class FooArchive : public Ogre::Archive
{
public:
FooArchive(const Ogre::String name, const Ogre::String& archType);
~FooArchive();
bool isCaseSensitive() const { return true; }
void load();
void unload();
bool isReadOnly() const { return true; }
Ogre::DataStreamPtr open(const Ogre::String& filename) const;
Ogre::StringVectorPtr list(bool recursive=true, bool dirs=false);
Ogre::FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false);
Ogre::StringVectorPtr find(const Ogre::String& pattern, bool recursive = true, bool dirs = false);
Ogre::FileInfoListPtr findFileInfo(const Ogre::String& pattern, bool recursive = true, bool dirs = false);
bool exists(const Ogre::String& filename);
time_t getModifiedTime(const Ogre::String& filename);
};
class FooArchiveFactory : public Ogre::ArchiveFactory
{
public:
virtual ~FooArchiveFactory();
const Ogre::String& getType() const;
Ogre::Archive *createInstance(const Ogre::String& name);
void destroyInstance(Ogre::Archive* arch);
};
class FooDataStream : public Ogre::DataStream
{
public:
FooDataStream(const Ogre::String& name);
size_t read(void* buf, size_t count);
void skip(long count);
void seek(size_t pos);
size_t tell() const;
bool eof() const;
void close();
private:
size_t idx_;
char buf_[26];
};
#endif /* _FOOARCHIVE_H_ */
[/code]
fooarchive.cpp 는 FooArchive 클래스의 구현만 담았다.
펼쳐두기..
[code cpp]
#include "fooarchive.hpp"
#include <OgreStringVector.h>
using namespace Ogre;
FooArchive::FooArchive(const Ogre::String name, const Ogre::String& archType) : Archive(name, archType)
{
}
FooArchive::~FooArchive()
{
}
void FooArchive::load()
{
}
void FooArchive::unload()
{
}
DataStreamPtr FooArchive::open(const String& filename) const
{
return DataStreamPtr(new FooDataStream(filename));
}
StringVectorPtr FooArchive::list(bool recursive, bool dirs)
{
// 걍 적당히 파일 두개를 가지고 있는셈 친다.
StringVectorPtr v(new StringVector);
v->push_back("1.foo");
v->push_back("2.foo");
return v;
}
FileInfoListPtr FooArchive::listFileInfo(bool recursive, bool dirs)
{
// 음 채울 정보가 많네. 그냥 빈거 줘본다.
// 원래는 이놈을 먼저 구현하고 list 를 구현해야 겠구나.
FileInfoListPtr fil(new FileInfoList);
return fil;
}
StringVectorPtr FooArchive::find(const String& pattern, bool recursive, bool dirs)
{
return StringVectorPtr(new StringVector);
}
FileInfoListPtr FooArchive::findFileInfo(const String& pattern, bool recursive, bool dirs)
{
return FileInfoListPtr(new FileInfoList);
}
bool FooArchive::exists(const String& filename)
{
return filename == "1.foo" || filename == "2.foo";
}
time_t FooArchive::getModifiedTime(const String& filename)
{
return 0;
}
[/code]
#include "fooarchive.hpp"
#include <OgreStringVector.h>
using namespace Ogre;
FooArchive::FooArchive(const Ogre::String name, const Ogre::String& archType) : Archive(name, archType)
{
}
FooArchive::~FooArchive()
{
}
void FooArchive::load()
{
}
void FooArchive::unload()
{
}
DataStreamPtr FooArchive::open(const String& filename) const
{
return DataStreamPtr(new FooDataStream(filename));
}
StringVectorPtr FooArchive::list(bool recursive, bool dirs)
{
// 걍 적당히 파일 두개를 가지고 있는셈 친다.
StringVectorPtr v(new StringVector);
v->push_back("1.foo");
v->push_back("2.foo");
return v;
}
FileInfoListPtr FooArchive::listFileInfo(bool recursive, bool dirs)
{
// 음 채울 정보가 많네. 그냥 빈거 줘본다.
// 원래는 이놈을 먼저 구현하고 list 를 구현해야 겠구나.
FileInfoListPtr fil(new FileInfoList);
return fil;
}
StringVectorPtr FooArchive::find(const String& pattern, bool recursive, bool dirs)
{
return StringVectorPtr(new StringVector);
}
FileInfoListPtr FooArchive::findFileInfo(const String& pattern, bool recursive, bool dirs)
{
return FileInfoListPtr(new FileInfoList);
}
bool FooArchive::exists(const String& filename)
{
return filename == "1.foo" || filename == "2.foo";
}
time_t FooArchive::getModifiedTime(const String& filename)
{
return 0;
}
[/code]
fooarchivefactory.cpp 는 FooArchvieFactory 클래스의 구현만 담았다.
펼쳐두기..
[code cpp]
#include "fooarchive.hpp"
#include <iostream>
using namespace Ogre;
using namespace std;
FooArchiveFactory::~FooArchiveFactory()
{
}
const String& FooArchiveFactory::getType() const
{
static const String s = "foo";
return s;
}
Archive* FooArchiveFactory::createInstance(const String& name)
{
// OGRE_NEW 등을 제공하는 모양인데 그냥 new 썼다.
return new FooArchive(name, "foo");
}
void FooArchiveFactory::destroyInstance(Ogre::Archive* arch)
{
delete arch;
}
[/code]
#include "fooarchive.hpp"
#include <iostream>
using namespace Ogre;
using namespace std;
FooArchiveFactory::~FooArchiveFactory()
{
}
const String& FooArchiveFactory::getType() const
{
static const String s = "foo";
return s;
}
Archive* FooArchiveFactory::createInstance(const String& name)
{
// OGRE_NEW 등을 제공하는 모양인데 그냥 new 썼다.
return new FooArchive(name, "foo");
}
void FooArchiveFactory::destroyInstance(Ogre::Archive* arch)
{
delete arch;
}
[/code]
foodatastream.cpp 는 FooDataStream 클래스의 구현만 담았다. 이쪽은 대강 구현한거라 버그가 많을듯. 어차피 내용 붙이면 모두 새로구현해야한다.
펼쳐두기..
[code cpp]
#include "fooarchive.hpp"
#include <algorithm>
using namespace std;
FooDataStream::FooDataStream(const Ogre::String& name) : DataStream(name)
{
// 어차피 예제 아닌가 그냥 적당히 고정 버퍼를 박았다.
mSize = sizeof(buf_);
idx_ = 0;
for(size_t i = 0; i < mSize; ++i)
{
buf_[i] = 'a'+i;
}
}
size_t FooDataStream::read(void* buf, size_t count)
{
int readlen = min(count, mSize - idx_);
memcpy(buf, buf_ + idx_, readlen);
idx_ += readlen;
return readlen;
}
void FooDataStream::skip(long count)
{
idx_ += count;
idx_ = min(idx_, mSize);
}
void FooDataStream::seek(size_t pos)
{
if(pos < mSize)
idx_ = pos;
}
size_t FooDataStream::tell() const
{
return idx_;
}
bool FooDataStream::eof() const
{
return idx_ == mSize;
}
void FooDataStream::close()
{
}
[/code]
#include "fooarchive.hpp"
#include <algorithm>
using namespace std;
FooDataStream::FooDataStream(const Ogre::String& name) : DataStream(name)
{
// 어차피 예제 아닌가 그냥 적당히 고정 버퍼를 박았다.
mSize = sizeof(buf_);
idx_ = 0;
for(size_t i = 0; i < mSize; ++i)
{
buf_[i] = 'a'+i;
}
}
size_t FooDataStream::read(void* buf, size_t count)
{
int readlen = min(count, mSize - idx_);
memcpy(buf, buf_ + idx_, readlen);
idx_ += readlen;
return readlen;
}
void FooDataStream::skip(long count)
{
idx_ += count;
idx_ = min(idx_, mSize);
}
void FooDataStream::seek(size_t pos)
{
if(pos < mSize)
idx_ = pos;
}
size_t FooDataStream::tell() const
{
return idx_;
}
bool FooDataStream::eof() const
{
return idx_ == mSize;
}
void FooDataStream::close()
{
}
[/code]
archive.cpp 는 위 세 클래스를 써먹는 예제를 담았다. 등록하는 코드도 봐두자..
펼쳐두기..
[code cpp]
#include "fooarchive.hpp"
#include <Ogre.h>
#include <iostream>
using namespace Ogre;
using namespace std;
class OgreApp
{
public:
OgreApp()
{
// root 를 만들기 전에는 ArchiveFactory 등록이 불가?
root_ = new Root("", "");
// 이제 등록가능하겠지
//
// 등록이 포인터를 등록하는 스타일이라 이걸 언제 delete 해줘야
// 하는지 애매한데 코드를 읽어보니 (1.6.4) delete 는 불러주지
// 않는다. 따라서 밖에서 포인터를 들고있다가 때되면(root
// 지운후가 되겠지) 지워주는 수고를 해줘야 한다.
faf_ = new FooArchiveFactory;
ArchiveManager& am = ArchiveManager::getSingleton();
am.addArchiveFactory(faf_);
// foo 타입을 처리하는 아카이브팩토리를 등록했으니 이제 foo 타입 리소스 추가가 가능할것이다
ResourceGroupManager& rgm = ResourceGroupManager::getSingleton();
rgm.addResourceLocation("noname", "foo", "nogroup");
rgm.initialiseAllResourceGroups();
// loadResourceGroup 을 하면 리소스를 완전히
// 로딩한다는데.. 지금 예제에선 별 의미가 없긴 하군.
rgm.loadResourceGroup("nogroup");
// 이제 리소스이름들을 읽어보자
// FooArchive 의 list 정도가 불리겠지?
StringVectorPtr v = rgm.listResourceNames("nogroup");
for(StringVector::const_iterator i = v->begin(); i != v->end(); ++i)
{
cout << "name: " << *i << endl;
}
// 이제 실제로 리소스에 접근을 해보자
for(StringVector::const_iterator i = v->begin(); i != v->end(); ++i)
{
cout << *i << " ==> " << rgm.openResource(*i, "nogroup")->getAsString() << endl;
}
}
~OgreApp()
{
delete root_;
delete faf_;
}
private:
Root* root_;
FooArchiveFactory* faf_;
};
int main()
{
OgreApp app;
}
[/code]
위 소스들을 빌드하는데 쓰인 CMakeLists.txt 의 일부. 아래 언급된 link_ogre3d_all() 매크로는 다른 파일에 있는데 적지 않았다.#include "fooarchive.hpp"
#include <Ogre.h>
#include <iostream>
using namespace Ogre;
using namespace std;
class OgreApp
{
public:
OgreApp()
{
// root 를 만들기 전에는 ArchiveFactory 등록이 불가?
root_ = new Root("", "");
// 이제 등록가능하겠지
//
// 등록이 포인터를 등록하는 스타일이라 이걸 언제 delete 해줘야
// 하는지 애매한데 코드를 읽어보니 (1.6.4) delete 는 불러주지
// 않는다. 따라서 밖에서 포인터를 들고있다가 때되면(root
// 지운후가 되겠지) 지워주는 수고를 해줘야 한다.
faf_ = new FooArchiveFactory;
ArchiveManager& am = ArchiveManager::getSingleton();
am.addArchiveFactory(faf_);
// foo 타입을 처리하는 아카이브팩토리를 등록했으니 이제 foo 타입 리소스 추가가 가능할것이다
ResourceGroupManager& rgm = ResourceGroupManager::getSingleton();
rgm.addResourceLocation("noname", "foo", "nogroup");
rgm.initialiseAllResourceGroups();
// loadResourceGroup 을 하면 리소스를 완전히
// 로딩한다는데.. 지금 예제에선 별 의미가 없긴 하군.
rgm.loadResourceGroup("nogroup");
// 이제 리소스이름들을 읽어보자
// FooArchive 의 list 정도가 불리겠지?
StringVectorPtr v = rgm.listResourceNames("nogroup");
for(StringVector::const_iterator i = v->begin(); i != v->end(); ++i)
{
cout << "name: " << *i << endl;
}
// 이제 실제로 리소스에 접근을 해보자
for(StringVector::const_iterator i = v->begin(); i != v->end(); ++i)
{
cout << *i << " ==> " << rgm.openResource(*i, "nogroup")->getAsString() << endl;
}
}
~OgreApp()
{
delete root_;
delete faf_;
}
private:
Root* root_;
FooArchiveFactory* faf_;
};
int main()
{
OgreApp app;
}
[/code]
펼쳐두기..
[code]
project(archive)
link_ogre3d_all()
add_library(fooarchive fooarchive.cpp fooarchivefactory.cpp foodatastream.cpp)
add_executable(archive archive.cpp)
target_link_libraries(archive fooarchive)
[/code]
project(archive)
link_ogre3d_all()
add_library(fooarchive fooarchive.cpp fooarchivefactory.cpp foodatastream.cpp)
add_executable(archive archive.cpp)
target_link_libraries(archive fooarchive)
[/code]
피드 구독하기:
글 (Atom)