삽질하는플머

구글 스캐치업의 작업을 블랜더로 가져와 보자.

여가생활/3DTools
블랜더에 지친 심신을 잠시 쉴 겸... 구글의 스캐치업을 한 번 돌려보았다.
오오~~ 캐감동~~~ 앞으로 이보다 쉬운 3D 프로그램이 나올 수 있을까... 감동의 눈물이 주르륵~~~
학생 때 이런 게 있었다면 얼마나 좋았을까~~~

3D 프로그램을 쓸 때 가장 관심이 가는 것은 아무래도 자료의 이동.
기껏 모델링한 내용이 해당 툴에 묶여있어야 한다면 그보다 안타까운 일은 없다.

아무튼 스캐치업의 "파일 -> 내보내기 -> 3D 모델..." 메뉴를 살펴보자. 




응? 뭐야?? "Google 어스 4" 와 "Google Earth" 달랑 두 개 뿐이다. 여기서 고민 시작...
예전에 찾아둔 정보에는 오우거 엔진용 익스포터가 있었는데... 그렇다고 루비를 배워서 짤 수도 없고...

이리저리 뒤져보다 걸린 정보.
http://update.multiverse.net/wiki/index.php/Exporting_Models#Google_SketchUp
오호~~ 구글어스 데이터는 COLLADA 파일을 를 압축한 거라는군. 햐~~

적당한 모델을 만들고 "Google 어스 4" 타입으로 저장한다. 슥슥~ 샥샥~~ 정말 편하군.






만들어진 *.kmz 의 확장자를 zip 으로 바꾸고 압축을 풀어준다. "models" 폴더에 얌전히 처분을 기다리는 *.dae 파일이 보인다.



블랜더를 실행시키고 "File -> Import -> COLLADA 1.4 (.dae)" 메뉴를 선택해 위의 파일을 불러들인다.





"Import and Close" 버튼을 살포시 눌러주면 짜잔~~~



블랜더로 예쁘게 옮겨졌다. 이제 구워먹던지 삶아먹던지 내 맘대로 하면 된다~~~!!!

(그런데... 스캐치업을 만지고 나니까, 다시 블랜더에서 해메기가 괴로워~~~)


주의!!! 구글 스캐치업의 결과물은 상용 프로젝트에서 사용할 수 없다!! Pro 버전을 구매해야 함.
얼마냐구? 단돈 $495.00!!! 맥스 하나 살 돈으로 이거 열 개 넘게 살 수 있다니~ 이건 뭐~~ 후덜덜~~~


루아 디버깅 테스트

탐구생활/Lua
게임오븐을 보고... 며칠간 잠을 설쳤다. "디버깅이 되는구나... 디버깅이 되는구나... 디버깅이 되는구나... "
도대체 어떤 멋진 PM이 이런 계획을 세우고 또 어떤 멋진 프로그래머가 그 계획을 구체화 시켰을까...
진짜 부럽고 부럽고 부러웠다.

나도 저런 것 만들어보고 싶어~~ 라는 기분도 참 오래간만에 느껴보았다.
디버거만 있다면 우리 회사 기획자들을 내가 만든 루아지옥에서 건져올려 좀 더 가정에 충실하게 만들 수 있을텐데...

우리 게임의 경우 서버와 클라이언트의 글루함수 및 호출규약이 많이 다르다. 따라서 게임오븐처럼 통일된 IDE에서 클라이언트, 또는 서버를 구동시키는 것은 그다지 효율적이지 않다. 뭐 조금 바뀔 때 마다 툴도 손대야 하는 상황은 이제 정말 싫어... 한 번 만들어놓고 멀건 국물 나올 때 까지 울궈먹는 게 무엇보다 쵝오.

때문에 디버거를 루아 모듈로 만들어 기존 환경에서 구동시키는 쪽으로 살짝 생각을 바꿔보았다. 그래서 탄생한 물건이 바로 이 놈!!

dbgtest.zip



단독실행이 아닌 모듈이기 때문에 기존 루아환경에서 불러들여야 한다. 뭐 멀리서 찾을 것 없이 lua.org 에서 배포하는 윈32용 루아 바이너리를 내려받자.

압축을 풀고 dbgtest.dll 을 여기에 복사 해 넣자. 이제 lua5.1.exe 을 실행시키고 다음과 같이 디버그 패키지를 부른다.

> require "dbgtest"

다음 화면과 같이 출력되면 디버그 패키지가 정상적으로 불러들여졌다는 뜻이다.


이제 방금 불러들인 디버그 모듈의 유일한 글루함수인 show() 를 실행시켜 보자.

> dbgtest.show()

화면에 썰렁하기 이를 데 없는 디버깅 화면이 나타날 것이다.


단축키는 기본적으로 델파이의 IDE와 동일하다. 

  F5 : 브레이크 포인트 설정. (줄번호 부분 클릭) 
  F7 : 트레이스 인투. (미구현) 
  F8 : 트레이스 스텝. (Next 버튼)
  F9 : 시작, 또는 다음 브레이크 포인트 까지 실행. (Start, Continue 버튼)
  Ctrl + F2 : 실행 종료. 디버깅 정지. (Stop 버튼) 

적당한 곳에 브레이크 포인트를 걸어주고 "Start" 버튼을 눌러주자. 현재 스탭에서의 지역, 전역변수 및 설정된 와치값이 표시된다.

이 모듈은 태생적인 제약을 몇가지 가지고 있다.

1. 루아 5.1.4 이전버전을 지원하지 않는다.
2. 코루틴의 디버깅이 엉망이다.
3. 지역변수에 설정된 이중테이블값을 와치에서 읽을 수 없다.
4. 화면 구성이 허접하다.

하지만, 어차피 사내 기획자들이 코루틴을 주무르는 것도 아니고... 로컬에서 이중테이블도 어차피 로컬변수 창에서 확인 할 수 있으니 일단은 써먹을 수 있다!!

덧붙여, 루아 컴파일시 설정에 따라 와치가 동작하지 않을 가능성도 있다.
http://www.gpgstudy.com/forum/viewtopic.php?p=120049#120049  (류광님의 답변)

이 부분은 3번 문제도 해결할 겸, 좀 더 맑은정신으로 하루 날잡아 스택을 하나 하나 정성스럽게 옮기는 방식으로 코드를 바꿔보자.

 

달랑 디버거만 구동되면 뭔가 썰렁할 듯 해서 예제코드를 넣어두었습니다. 이 코드가 보기 싫으면 실행파일과 같은 위치에 빈 noname.lua 파일을 넣어두면 됩니다.




방향 선호 셸 추출

이런저런잡다구리/Research
선공, 또는 먹자 몬스터가 시야 내의 유저나 아이템을 발견할 때 사용한 방법.
2004. 6. 8. 작업노트로 부터 정리 해 둠.




오브젝트가 향하는 방향값은 다음과 같이 정의되어있다.

4 0 5
3 C 1
7 2 6


첫번째 테이블, 즉 거리가 1일 때의 갯수는 8개이며 거리에 비례해 8의 배수로 늘어난다.

1st Table
0, 4, 5, 3, 1, 7, 6, 2
1, 5, 6, 0, 2, 4, 7, 3
2, 6, 7, 1, 3, 5, 4, 0
3, 7, 4, 2, 0, 6, 5, 1
4, 3, 0, 7, 5, 2, 1, 6
5, 0, 1, 4, 6, 3, 2, 7
6, 1, 2, 5, 7, 0, 3, 4
7, 2, 3, 6, 4, 1, 0, 5

2nd Table
0-0, 4-0, 5-0, 4-4, 5-5, 4-3, 5-1, 3-3, 1-1, 7-3, 6-1, 7-7, 6-6, 7-2, 6-2, 2-2
0-0의 의미는 0번 방향에서 다시 0번방향이라는 의미...

이 관계를 테이블로 정리 해 보면 다음과 같다.

4,4,4,4 4,4,4,0 4,4,0,0 4,0,0,0 0,0,0,0 5,0,0,0 5,5,0,0 5,5,5,0 5,5,5,5
(-4,-4) (-3,-4) (-2,-4) (-1,-4) (0,-4) (1,-4) (2,-4) (3,-4) (4,-4)
4,4,4,3 4,4,4 4,4,0 4,0,0 0,0,0 5,0,0 5,5,0 5,5,5 5,5,5,1
(-4,-3) (-3,-3) (-2,-3) (-1,-3) (0,-3) (1,-3) (2,-3) (3,-3) (4,-3)
4,4,3,3 4,4,3 4,4 4,0 0,0 5,0 5,5 5,5,1 5,5,1,1
(-4,-2) (-3,-2) (-2,-2) (-1,-2) (0,-2) (1,-2) (2,-2) (3,-2) (4,-2)
4,3,3,3 4,3,3 4,3 4 0 5 5,1 5,1,1 5,1,1,1
(-4,-1) (-3,-1) (-2,-1) (-1,-1) (0,-1) (1,-1) (2,-1) (3,-1) (4,-1)
3,3,3,3 3,3,3 3,3 3 C 1 1,1 1,1,1 1,1,1,1
(-4,0) (-3,0) (-2,0) (-1,0) (0,0) (1,0) (2,0) (3,0) (4,0)
7,3,3,3 7,3,3 7,3 7 2 6 6,1 6,1,1 6,1,1,1
(-4,1) (-3,1) (-2,1) (-1,1) (0,1) (1,1) (2,1) (3,1) (4,1)
7,7,3,3 7,7,3 7,7 7,2 2,2 6,2 6,6 6,6,1 6,6,1,1
(-4,2) (-3,2) (-2,2) (-1,2) (0,2) (1,2) (2,2) (3,2) (4,2)
7,7,7,3 7,7,7 7,7,2 7,2,2 2,2,2 6,2,2 6,6,2 6,6,6 6,6,6,1
(-4,3) (-3,3) (-2,3) (-1,3) (0,3) (1,3) (2,3) (3,3) (4,3)
7,7,7,7 7,7,7,2 7,7,2,2 7,2,2,2 2,2,2,2 6,2,2,2 6,6,2,2 6,6,6,2 6,6,6,6
(-4,4) (-3,4) (-2,4) (-1,4) (0,4) (1,4) (2,4) (3,4) (4,4)


정리결과 재미난 규칙을 발견함.. 기준좌표를 중심으로 6-2-2-2 위치의 좌표는 6방향 옵셋값인 (1,1)과 2방향 옵셋값인 (0,1)을 그 반복횟수만큼 옵셋시킨 값이 된다. (1,1) + (0,1) + (0,1) + (0,1) = (1,4).

각 방향의 '어깨걸이' 방향을 좌우 순서로 정리하면 다음과 같다.

0 - 4, 5
1 - 5, 6
2 - 6, 7
3 - 7, 4
4 - 3, 0
5 - 0, 1
6 - 1, 2
7 - 2, 3

단계 1의 방향을 추출해 보자.

1의 왼쪽 오른쪽인 5, 6까지 추출... 5번의 왼쪽 0, 6번의 오른쪽 2,
다시 0번의 왼쪽 4, 2번의 오른쪽 7. 마지막으로 4의 왼쪽 3 또는 7의 오른쪽 3.

1, 5, 6, 0, 2, 4, 7, 3

어깨걸이 좌표 테이블로부터 추출이 가능하다는 얘기가 됨.

기준좌표로부터 거리 3인 위치를 6번방향 기준으로 추출할 경우...
먼저 3개의 인자를 갖는 바이트배열 두 개를 준비한다. 각각 오른쪽, 왼쪽값이 된다.
DirArr_R, DirArr_L : TByteArray;

초기값은 두 방향 모두 6-6-6 으로 채운다. DirArr_R[0], [1], [2] := 6;
이 인자들의 값을 해당 루프만큼 바꿔가며 방향을 추출할 수 있다.
총 루프 횟수는 거리값인 3 * 8 / 2 -1 = 11 번이 된다.
(하나의 방향이 모두 치환되는 횟수는 거리값, 즉 이경우 3번이 된다.)

시작방향이 0~3이면 앞부분을 치환, 4~7이면 뒷부분을 치환한다.

RepPos: Integer;

오른쪽을 기준으로 설명 해 보자. 시작방향, 즉 DirArr_R[0] 값이 6이므로 뒷부분 치환.

RefPos:= 0;
RepPos:= 거리 -1;
IncVal := -1; // RepPos를 줄이는 참조값.

또한 6의 오른쪽 방향은 ND2CD_TBL[6][7] => 2 이므로 루프 첫번째 값은 [6-6-2] 이다.
DirArr_R[RepPos] := ND2CD_TBL[ DirArr_R[RefPos] ][7];

RepPos는 하나 줄인다.
Inc(RepPos, IncVal);

RepPos가 0보다 작아지면 RepPos를 0으로, RefPos를 거리-1로, IncVal을 1로 수정.
RepPos가 거리보다 크거나 같아지면 RepPos를 거리-1로 RefPos를 0으로, IncVal을 -1로 수정.

이 과정을 총 루프횟수만큼 반복한 후 마지막 방향은 처음방향의 역방향으로 결정.
또는 왼쪽, 오른쪽 아무쪽이나 이 과정을 한 번 더 진행시켜 결정.

얻어낸 좌표옵셋의 거리제곱이 주어진 (거리+1)의 제곱보다 작은 경우만 범위로 인정.
이렇게 추출한 점열들은 결국 원형이 된다. 이 때 원형 내부에 꽉 차는 축직교 정사각형의 범위는 굳이 거리를 재지  않아도 원 내부이며 따라서 단순 비교문만으로 처리할 수 있다. 주어진 거리에 cos(45) 값을 곱한 후 반올림하면 사각영역의 경계값을 얻게 되며 이 경계안에 포함되는 영역은 거리비교를 하지 않도록 하자.
이 경계값은 스텝에 바로 대응된다. n번째 스탭에서 (n <= 경계값)의 조건을 만족 한다면 거리값을 잴 필요없이 원형 범위 이내가 된다는 이야기..

추출좌표의 총 갯수는 ((추출하려는 거리 * 2) -1)^2 -1 이 된다. 여기서 -1은 원점을 뺐기 때문이다.



지금까지의 정리를 코딩을 통해 확인. (델파이 5)


흰색 점 : 거리를 벗어난 셸
검은 점 : 거리 이내로 판단된 셸
회색 점 : 사각영역이므로 거리판단을 생략하고 무조건 추출한 셸


LNUM 패치가 적용된 arm-wince 용 루아 5.1.4

탐구생활/Lua

LNUM 패치가 적용된 루아 5.1.4 의 arm-wince 용 바이너리 이다.
컴파일러는 CeGCC 의 arm-mingw32ce 를 사용했다.

루아의 표준 라이브러리 중 io, os, loadlib 세 가지는 빼 버렸다.
따라서 io.open(), os.date(), package.loadlib() 등의 함수는 사용할 수 없다.
wince의 C런타임에 tmpfile() 같은 표준 C함수가 없기 때문에 이리저리 고민하다가
일단은 제거 빼 버렸는데, 사실 루아를 쓸 때 표준 라이브러리 의존도는 상대적으로
떨어지고 필요하면 글루함수로 만들어 추가하는 편이 나을 것이다.

(솔직히 루아는 테이블만 있으면~~~)


이제 이 라이브러리를 라자루스에 붙여볼까나~~~



 


2009.3.20
 
실수값에 float, 정수값에 int32 를 사용하는 버전.
luac.exe 를 사용해 컴파일한 바이트코드는 서로 다른 설정을 가진 루아 모듈 사이에서 호환되지 않는다.
따라서 동일한 설정으로 컴파일된 윈도32용 바이너리도 함께 넣어 두었다.

arm-wince 에는 표준 라이브러리 중 base, table, string, debug 만 포함되었다.
가급적 math는 집어넣으려 했지만, float모드로 사용할 때 발생한 atanhf 의 링크에러를
풀지 못했다. 따라서 필요하다면 글루함수로 만들어 제공해야 한다.


달력 컨트롤

Lazarus/KOL-CE
라자루스를 어디에 쓸까 고민하다가... 음력에 대한 공부도 할 겸 "달 구경" 프로그램을 짬짬이 만들고 있다.
날짜 선택을 위해 달력 컨트롤을 올려놓으려고 했더니... 응? 델파이의 TDateTimePicker에 해당하는 TKOLDateTimePicker 는 있는데 TMonthCalendar에 해당하는 컨트롤이 없다. 허걱~~ fpc의 wince 유니트를 열어보니 DateTimePicker와 MonthCal에 사용되는 ICC_DATE_CLASSES 가 주석으로 막혀있네~ (라자루스 0.9.24 의 LCL 윈CE 인터페이스는 현재 베타버전이다.)

그렇다면 KOL의 TKOLDateTimePicker 는 뭘까 싶어 다시 KOL.pas 를 뒤져보니... 여기에 다시 재정의 되어있었다. 그러나 애석하게도 달력 컨트롤에 대한 코드는 찾아볼 수 없었다.

처음에는 간단히 LCL로 DLL을 만들어 쓰려고 했었는데... 위에서 언급했듯이 아직 윈CE 인터페이스가 베타라 arm-wince 로 달력 컨트롤을 컴파일하면 에러가 발생한다. 뭐 여차저차해서 만들기로 했고, 아직 내공부족으로 KOL 컴포넌트화 할 수는 없었지만 그럭저럭 쓸만하기에 방법을 정리 해 본다.

달력 컨트롤에 관련된 상수들은 evc와 함께 설치한 STANDARDSDK 5.0 에 정의되어 있으며 "C:\Program Files\Windows CE Tools\wce500\STANDARDSDK_500\Include\...\commctrl.h" 에서 찾을 수 있다. 이 파일을 번역하려고 슬쩍 훑어보니 다행히 상수 및 구조체가 윈도 32와 99% 동일하다. 그렇다면?? 델파이의 VCL을 가져와 쓰자.


VCL은 오픈소스가 아니므로 여기에 옮길 수 없다. 델파이 5.0을 기준으로 과정만 소개 해 본다.


1. 먼저 라자루스의 File->NewUnit 를 선택, 새로 만들 달력 컨트롤이 담길 유니트를 만들어 준다. 이름은 적당히.

2. uses 섹션에는 Windows, Messages, KOL, CommCtrl 유니트를 적어넣는다.

3. ...\Delphi5\source\rtl\win\commctrl.pas 파일을 열고 다음 내용들을 복사 해 온다.

3-1. 6371번 줄 (MONTHCAL CONTROL) 부터 6664번 줄 (MonthCal_GetUnicodeFormat) 까지 interface 섹션에 복사 해 넣는다.

3-2. 8508번 줄 (MonthCal_GetCurSel 함수) 부터 8623번 줄 까지 implementation 섹션에 복사 해 넣는다.

3-3. MonthCal_SetUnicodeFormat, MonthCal_GetUnicodeFormat 함수는 wince에서는 동작하지 않는다. 이 두 함수의 선언 및 구현부는 {$ifdef win32} 지시자로 감싸둔다.

4. KOL의 TControl 기반의 달력 컨트롤을 생성할 수 있도록 다음과 같이 NewMonthCalendar 함수를 만들어 준다.

function WndProcMonthCalendarNotify( Self_: PControl; var Msg: TMsg; var Rslt: Integer ): Boolean;
begin
  Result := false;
end;

function NewMonthCalendar( AParent: PControl): PControl;
const
  CS_OFF = {$ifdef win32}CS_OWNDC or CS_CLASSDC or {$endif}CS_PARENTDC or CS_GLOBALCLASS or
           CS_VREDRAW or CS_HREDRAW;
begin
  DoInitCommonControls( ICC_DATE_CLASSES );
  Result := _NewCommonControl( AParent, MONTHCAL_CLASS,
         WS_CHILD or WS_VISIBLE or WS_TABSTOP,
         TRUE,  nil );
  Result.SetSize( 191, 154 );
  Result.AttachProc( WndProcMonthCalendarNotify );
{$ifdef wince}
  Result.Perform(CCM_SETVERSION, COMCTL32_VERSION, 0);
{$endif wince}
end;



작업은 이것으로 끝. 달력을 생성해 폼 위에 올리는 코드는 다음과 같다.

TCalendarForm = ....
...
private
  fCalendar: PControl;
...

procedure TCalendarForm.KOLForm1FormCreate(Sender: PObj);
begin
  fCalendar := NewMonthCalendar(Form);
end;



달력에 날짜를 지정하거나 읽어올 때는 델파이에서 복사해 온 MonthCal_SetCurSel, MonthCal_GetCurSel 를 쓰면 편하다. 한가지, KOL의 SystemTime2DateTime은 델파이와 달리 1899년12월30일이 아닌 0년 0월 0일을 기준으로 한다. 아직 분석도 다 안끝났고 무엇보다 귀찮으니 그냥 SysUtils에 정의된 SystemTimeToDateTime() 을 쓰자.


function TCalendarForm.GetDateTime: TDateTime;
var
  pst: TSystemTime;
begin
  MonthCal_GetCurSel(fCalendar.Handle, pst);
  Result := SysUtils.SystemTimeToDateTime(pst);
end;

procedure TCalendarForm.SetDateTime(const AValue: TDateTime);
var
  pst: TSystemTime;
begin
  SysUtils.DateTimeToSystemTime(AValue, pst);
  MonthCal_SetCurSel(fCalendar.Handle, pst);
end;




폰트 크기를 50으로 주고 480*800 에뮬레이터에서 돌려본 화면은 짜잔~~ 멋지지 않은가!!







3월 23일 릴리즈 된 0.9.26.2 버전의 윈CE/LCL 인터페이스에 캘린더가 추가되었다고 한다.
그러나 이 팁은 KOL에서는 아직까지 유효하다~~ ^^