디자인 맞추가 빡시다….

음 알수없는 css의 세계…
위키랑 블로그 디자인 맞추가기 너무 빡시다…

가장 좋은 방법은 헤더에 해당하는 파일을 만들고 공통적으로 쓰일 스타일시트를 만들어서 사용하는 방법인데 음…. 어렵다 어려워
뭔가 알수 없는것 때문에 정상적으로 적용이 안된다. 알수 없는 곳에서 css를 막 링크하는것 같기도 하고…

조금씩 미묘하게 위치가 달라지니 -_-;

??페???리뉴??중~

?에 ?던 ?페???로보드 기반?었?데 ?제 ????에 발맞추어 ?도 블로?는?려결심?다. ???무?도 문서???업???는???어?는 ?키가 좋다?각???서 ??치?해???력

?친?고 ?도 ?자?을 비슷?게 ?는 ?도??을?다.
?단 개발??관??간단???용? ‘메모’ 기록???고 ?중???키??문서???는 방식???각?고 ?다. 근데 막상 ?터?을 깔고 블로그에 글 추는 ?을 보니 ?키보다 ???할같다???각???기???다. ??

Console Debug


1 소개 #

윈도우 프로그램 개발시 콘솔창을 띄워 출력을 할수 있다. TRACE 명령을 이용해서도 가능하지만 TRACE 명령의 경우 파라메터값에 제한이 있고 Debug 모드에서만 사용할수 있지만 콘솔창은 릴리즈 모드에서도 사용이 가능하며 C의 printf 형식으로 얼마든지 변수들의 값을 볼수 있다.

consoledebug_ss_1.JPG

대략 이런 느낌


2 사용법 #

헤더파일을 stdafx.h 에 인클루드하고 콘솔창을 띄울때 _dbgcode(AllocConsole()); 을 넣는다. 보통 프로그램 처음.

이후에는 _cdbg_cdbgi 를 사용하여 출력 두가지의 차이점은 개행을 하느냐 마느냐의 차이


#pragma once

#include <windows.h>
#include <stdio.h>

// 콘솔 디버그를 사용할경우는 디파인하고 사용하지 않을경우 주석처리
#ifdef _DEBUG // 디버그 모드에서 사용
#define _DBG_MOD_
#endif
#ifndef _DEBUG // 릴리즈 모드에서 사용
//#define _DBG_MOD_
#endif

// 콘솔 디버그를 사용할경우
#ifdef _DBG_MOD_
#define _cdbg ConsoleDebugLine
#define _cdbgi ConsoleDebug
#define _dbgcode(CODE) CODE
#endif

// 콘솔 디버그를 사용하지 않을 경우
#ifndef _DBG_MOD_
#define _cdbg //
#define _cdbgi //
#define _dbgcode(CODE)
#endif

// 콘솔디버그 함수내의 문자열 버퍼 사이즈 -> 버퍼가 모자라면 에러난다.
#define BUFSIZE 4096

//_dbgcode(AllocConsole()); 를 한후에 함수호출
void ConsoleDebugLine(char* pFormat, … );
void ConsoleDebug(char* pFormat, … );



3 Download #

Uploads:ConsoleDebug.zip

ID3DXSprite 란?


1 ID3DXSprite 란? #

d3d 내에서 2D를 표현하기 위한 인터페이스


2 ID3DXSprite 의 사용법 #
























Begin 스프라이트를 드로잉(Drawing) 할 수 있도록(듯이) 장치를 준비한다.
Draw 스크린 공간에 단순한 스프라이트를 드로잉(Drawing) 한다.
DrawTransform 지정한 행렬에 의해 변환 한 스프라이트를 드로잉(Drawing)하기 위한 구조를 제공한다.
End ID3DXSprite::Begin 가 불려 간 시점의 장치 스테이트를 복원한다.
GetDevice 스프라이트 개체에 관련지을 수 있는 Direct3D 장치를 얻어온다.
OnLostDevice 비디오 메모리 리소스에의 참조를 모두 릴리즈 해, 모든 스테이트 블록을 삭제한다.
OnResetDevice 장치가 리셋 된 후에 호출할 필요가 있다.|

위의 표는 다이렉트X 헬프에 있는 내용이다. 사용법을 간단히 말하면 IDirect3DDevice9::BeginSceneIDirect3DDevice9::EndScene 사이에 ID3DXSprite::BeginID3DXSprite::End 를 호출하고 그 사이에 그리면 된다.

IDirect3DDevice9::BeginScene
ID3DXSprite::Begin

<< 여기다가 그리는걸 호출 >>>

ID3DXSprite::End
IDirect3DDevice9::EndScene


코드가 대충 이런 형태가 된다. 그릴때는 Draw 메소드를 호출해서 사용하는데

HRESULT Draw(LPDIRECT3DTEXTURE9 pSrcTexture,
CONST RECT *pSrcRect,
CONST D3DXVECTOR2 *pScaling,
CONST D3DXVECTOR2 *pRotationCenter,
FLOAT Rotation,
CONST D3DVECTOR2 *pTranslation,
D3DCOLOR Color
);

드로우 함수의 인자들 설명을 하자면

  • 첫번째 인자는 스프라이트에 사용할 텍스쳐이다. 스프라이트는 2D 게임프로그래밍을 해본사람이라면 느낌일 팍 올테니…
  • 두번째 RECT 구조체의 인자는 덱스쳐중에 어느 부분을 사용할것이가를 의미한다.
  • 세번째 D3DXVECTOR2 는 스케일링 인자라고하는데 x, y가 각각 가로 세로로 얼마나 늘려서 아니면 줄여서 사용할 것인지를 나타낸다. 뭐 기본값은 1.0f , 1.0f 다. 두배로 늘릴려면 2.0f, 2.0f 로 쓸수 있다. 음 RECT로 한픽셀 범위만 잡은뒤 가로 세로로 늘려서 원하는 길이의 선분을 얻을수도 있다. 뭐 대충 그런식…
  • 네번째 D3DXVECTOR2 는 회전중심이라고 하는데 아직 사용해보지는 않아서 모르겠지만 다섯번째 인자로 회전을 할때 어디를 중심으로 할것인가를 나타낸다. 기본값은 0.0f, 0.0f 이다.
  • 다섯번째 FLOAT 는 회전값인데.. 1.0f 를 넣어주니 90도가 회전했다. 음 정확한건 모르겠다.
  • 여섯번째 D3DCOLOR값에 의해 컬러 채널과 알파 채널이 곱셈된다 라고 써있다.. 그냥 0xFFFFFFFF 를 넣어주면 원래 이미지 그래도 찍힌다. 흐음 마스킹값을 넣어주는 건가?
인자를 설명하면 대충이렇다.(아아 정말 대충이다.) 그런데 이 스프라이트객체를 사용할때는 꼭 2의 X승 크기의 이미지를 사용해야 한다. 안그러면 RECT 구조체로 범위를 지정할때 엉망징창이 된다. 어떤 사람은 메모리도 센다고 하더라…

그리고 당연한 소리같지만 스프라이트 역시 순서에 따러 덥힌다. 즉 스프라이트를 찍고 오브젝트를 렌더링 하면 오브젝트에 가려 스프라이트가 안보일수고 있다는 것이다. 스프라이트도 D3D에서는 프리미티브로 처리할줄 알았는데… 조금 다른가보다.


3 스크린샷 #


http://lagoons.net/docdata/minimap_sprite.jpg

위의 이미지에서 우측상단에 그려진 격자모양의 미니맵이 스프라이트를 이용해서 구현되었다.

DirectX9 에서 DC를 얻어 2D를 간단히 그리는 방법


1 DirectX9 에서 DC를 얻어 2D를 간단히 그리는 방법 #

다이렉트X에서 2D를 찍는 방법은 여러가지가 있다. 일단 프리미트브 2개를 사용해 평면을 만들어 텍스쳐를 입히는 방법과 ID3DXSprite 를 사용하는 방법 그리고 DC를 얻어서 그리는 방법이다. 음 이 외에도 방법이 있을지도 모르지만 내가 모르니 패스

일단 DC를 얻기위해서 초기화를 해주어야 하는 부분이 있는데
D3DPRESENT_PARAMETERS d3dpp;
를 이용해 D3DDevice 를 초기화 시킬때
d3dpp.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER
를 설정해준다.

위의 플래그는 어플리케이션이 직접 백 버퍼를 설정할 필요가 있을때 설정한다고 하는데 음 결과적으로는 퍼포먼스가 별로 안좋다고 한다. 하지만 간단한 테스트등을 할대는 퍼포먼스 보다는 편의성!

 HDC hdc;
LPDIRECT3DSURFACE9 BackBuffer;
g_pd3dDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &BackBuffer )
BackBuffer->GetDC(&hdc);

<< 여기서 DC를 이용한 출력문을 사용하면 된다. >>

BackBuffer->ReleaseDC(hdc);


http://lagoons.net/docdata/GetdcSample.jpg

개인적인 생각으로 아직 시도해보지는 않았지만 여기에도 winapi.co.kr 에서 볼수있는 dc에 적용하는 더블버퍼링을 사용한다면 점더 효율적이지 않을까 한다.









방금 구현해본 바로는 졸라 꼬리다 이건 사용할 방법이 못된다. 더블버퍼링이 꼬리다는 소리가 아니고 DC를 얻어 쓰는 방법이 꼬리다. 음 웬지 깜밖 거리기도 하고 카메라를 돌리면 어째서 인지 출력이 안될때도 있다. by yoway 2004/6/4

AsyncSocket 클래스 – 소켓당 스레드 버젼 (ver 0.01)


1 소개 #

간만의 연휴를 이용해서 비동기 소켓클래스를 만들어 봤다… 잘 되는지는 별로 태스를 안해봐서 모르겠다. 음 비동기 모드로 만들때 WSAEventSelect 를 이용해서 비동기 모드로 전화시키고 각 소켓과 WSAEvent를 맵핑한뒤 스래드에서 WaitForMultipleObject 를 이용해서 발생한 이벤트를 얻고 해당소켓을 찾아 어떤 소켓이벤트가 발생했는지 판단해 해당 이벤트 함수를 호출하는 형식인데 문제는 WaitForMultipleObject 의 인자중 이벤트배열을 집어넣는것이 한번에 64개 밖에는 안된다는 것! 이 문제로 인해 스레드당 처리할수 있는 소켓의 개수는 최대 64개(스레드 종료 이벤트 등을 생각하면 60개정도) 밖에 되지 않는다는것.. 서버로 사용될경우 처리할 소켓이 늘어나면 스래드를 늘려줘야 한다는 단점이 있다. 아직 위의 스레드 관리에 관한건 생각해보지 않아서 소켓당 스레드 하나의 버젼으로 간단히 만들었다.(스레드 관리뿐 아니라 스레드 마다의 소켓을 관리하는것도) 나중에 꼭 수정을 하리라!


2 Code #


#ifndef _ASYNC_CONNECTOR_
#define _ASYNC_CONNECTOR_

#include <iostream.h>
#include <winsock2.h>

class CAsyncConnector
{
public:
CAsyncConnector();
virtual ~CAsyncConnector();

// 소켓초기화 및 종료화 함수 // 뽀대나게 스태틱으로
static int Cleanup();
static int Startup();

// 소켓초기화 함수 : 언제나 TCP 비동기소켓을 생성한다.
SOCKET InitializeSocket();
// 소켓을 닫는 함수
int CloseSocket();

// 소켓 연결함수
int ConnectToIP(LPCSTR lpcIP, UINT uPort);
int ConnectToDomain(LPCSTR lpcDomain, UINT uPort);

// send, recv API 함수
int Receive(byte* pDataBuf, int nBufSize);
int Send(byte* pSendData, int nDataSize);

// bind, listen API 함수 :: 서버만 사용
int Binding(UINT uPort);

// 클래스 맴버변수를 얻는 함수
WSAEVENT* GetEvenHandle();
SOCKET GetSocket();

// domain 으로 부터 IP를 얻는 함수
BOOL GetIPfromDomain(LPCSTR lpcDomain);

// 소켓이벤트 처리 스레드
HANDLE StartThreadEventSelect();
int StopThreadEventSelect();
static DWORD WINAPI EventSelectThread(LPVOID lpArg);

private:
// 클래스 인스턴스의 개수
static int m_nCountInstance;

// 이 연결자가 사용하는 소켓
SOCKET m_Socket;
sockaddr_in m_SockAddr;

// 소켓 이벤트핸들 0=소켓이벤트 1=스레드증료
WSAEVENT m_hEvent[2];

// EventSelect 스레드를 관리하는 변수
HANDLE m_hEventSelectThreadHandle;
DWORD m_dwEventSelectThreadId;

protected:
// 오버라이딩용 함수들
virtual void OnReceive(int nErrorCode);
virtual void OnSend(int nErrorCode);
virtual void OnOutOfBandData(int nErrorCode);
virtual void OnAccept(int nErrorCode);
virtual void OnConnect(int nErrorCode);
virtual void OnClose(int nErrorCode);
};

#endif



3 소스 #

비주얼스튜디오 6.0에서 작업

Profile

Profile

  1. 이름 :
    김용환
  2. E-mail :

  3. 주소 :
    서울시 강서구
  4. 직업 :
    프로그래머 (바람의 에이젼트)
  5. 학력 :
    우장초등학교 졸업 – 염창중학교 졸업 – 영일고등학교 졸업 – 광운대학교 졸업
  6. 목표 :
    고민중

Career

날자 내용 비고
2017년 1월
2014년 2월
2009년 5월
넥슨지티 모바일 사업쪽이 넥슨레드로 분사
넥스토릭이 게임하이와 합병 넥슨지티가됨
넥슨에서 넥스토릭으로 분사
2008년 4월 국방부 산하 인더스트리 테크니컬 에이젼트 소집해제
2006년 9월 넥슨 입사
2006년 8월 ECMiner 퇴사
2005년 6월 국방부 산하 인더스트리 테크니컬 에이젼트 편입
2004년 12월 ECMiner 솔루션 개발팀 입사
2004년 9월 SmartWind 에서 WinCE.NET(모바일) 용 어플리케이션 개발 계산기, 녹음기
2003년 7월 SKYDATA(하늘데이타)에서 리커버리매직 네트워크 모듈개발

Height Map Editor

1 Height Map Editor #

하이트맵의 랜더링을 할때 좀도 복잡한 하이트맵의 파일 포멧이 필요하게 되어 직접 에디터와 랜더러를 구현

  • 2004년 7월 29일
    현재의 진척도는 MFC View에다가 랜더링을 한다는 정도?

하다가 삽질한점은 RPG책에서 구현한 마우스 인풋이 MFC에서 안먹힌다는 점과 현재 랜더링을 APP::OnIdle 에서 하고 있는데 가만히 있으면 이게 호출이 안된다는 점이다. 마우스 인풋은 View의 마우스 이벤트로 처리 (그리고 기타 UI쪽에서 개삽질… 이제 제일 짜증난다!)

http://lagoons.net/docdata/Editor_1.jpg

  • 2004년 7월 31일
    이제는 조금 맵에디터 같은 분위기가 쪼오오오금 풍긴다. 여러개의 텍스쳐를 샥샥 랜더링이 가능하고 텍스쳐의 추가도 자유롭다. 당근 삭제도 자유로워야 하지만 지금은 에러가 있어서 한번이라도 사용했던 텍스쳐는 삭제할 시점에 사용한 타일이 다른 텍스쳐로 교체하더라도 에러가 꽃피게 된다. 음 어째서 -_-;;;

http://lagoons.net/docdata/Editor_3.jpg

1.1 구현요약 #

계속 개발 중이기 때문에 계속 바뀌겠지만 현재 맵에디터를 구하면 구현한 기반 클래스를 정리해보자

CIndexBufferManager
클래스명이 너무 거창하다. 사실 이 클래스가 관리할수 있는건 내가 만든 하이트 맵뿐이라서… 전혀 범용적이지 못하게 구현되어있다. 어쨋건간에 이름처럼 하이트맵에 관한 인덱스버퍼를 관리한다. 뭐 관리라고 해도 텍스쳐와 해당 인덱스버퍼를 연관시켜 백터에 담고 있다가 랜더링시 호출순서대로 찍어주는 역할뿐이 하지 않는다. 내부에는 타일매니져와 텍스쳐매니져를 가지고 있어서 타일에 매핑된 텍스쳐를 정렬하여 인덱스버퍼를 재구성하는 기능이있다. 문제점은 컬링이나 프러스트럼을 구현해 본적이 없어서 이쪽으로 확장성이 거이 없다는 것…
CHeightMapTileManager
뭐 클래스명 그대로 하이트맵에서 하나하나의 타일을 관리하는 클래스. 타일에 해당하는 이차원 배열을 가지고 타일당 각 텍스쳐의 매핑 정보를 가지고 있으며 각 텍스쳐들의 사용된 개수등등을 알수 있다.
CHeightMapTextureManager
역시나 클래스명 그대로 하이트맵에서 사용하는 텍스쳐들을 관리하는 클래스. UI쪽과 연계되서 파일에서 텍스쳐들을 추가할수 있고 각 텍스쳐의 이벤트코드와 파일패스 그리고 코맨트 정보도 함께 매핑해서 관리한다. (이벤트코드와 코맨트는 미구현)

위의 세가지 클래스들의 문제점은 서로 간의 인터페이스가 너무 난잡하다는 점. 3개 정도는 괜찮겠지 하고 그냥 짰는데 막상 짜다보니 좀 지지분한 생각이 들었다. CHeightMapTileManager , CHeightMapTextureManager 를 하나로 합쳐서 관리하는 하는 클래스를 만드는게 좋을지도 모르겠다.

  • 2004년 7월 31일 오후 9시 15분전
    후아… 버그를 잡았다. 상당히 짜증나는 버그 였지만 원인은 내가 호출을 안해줬을 뿐이었다. 인덱스버퍼매니져의 인덱스 버퍼 재구성은 하나의 플래그를 주어 그 플래그가 TRUE이 되면 한번 재구성하고 다시 FALSE로 바꿔놓는 방식으로 바꿨놨다. 매번 하이트맵의 랜더링마다 그 플래그의 값에 따라 함수 호출을 결정하므로 어디서든 요 플래그를 TRUE로 하면 재구성이 된다. 즉 텍스쳐를 삭제할때 재구성하는 방식.. 이제는 브러시 기능을 구현하면… 뭐 고저차자체는 간단하지 않을까? 둥글둥글하게 고저차를 정리하기 위해서는 곡면함수 이런거를 알아야 겠지만…

http://lagoons.net/docdata/Editor_4.jpg

http://lagoons.net/docdata/Editor_5.jpg

  • 2004년 8월 20일

너무 오랜만의 업데이트… 그동안 열심히 구현하고 싶었지만 여러가지 다른 사정이 겹쳐서 근 20일간 스크린샷을 못올리다니… 사실 내부적으로 구현은 했지만 외부적으로 보일게 별로 없어서 안한것도 있다. 그나저나 하이트맵을 구현하기 시작했을때 부터 한달 후에 지금 이정도면 꽤 열심히 한것 같기도 하고… 처음 10일간 한게 이후 20일동안 한것보도 많은것 같아서 아쉽기도 하고,…

이제 구현할건 둥글게 지형 변화를 비롯한 “지형변화 부분” 파일 저장 (맵과 타일셋을 따로) 그리고…짜잘한 버그들…

지금 어째서 인지 맵이 반투명 하게 되는 부분이 있는데 그 이유를 모르겠다 -_-;;;;;

흠 스크린샷의 우측하단 부분에 푸르스름한것이 브러쉬!

http://lagoons.net/docdata/map_5.jpg

  • 2004년 9월 즈음

사실 지금 정리하고 있는 시점이 2005년 4월 말이라 -_-; 기억이 안난다.

http://lagoons.net/docdata/HMapEditor_1s.jpg

http://lagoons.net/docdata/HMapEditor_1s_w.jpg

일단 8월달 과의 가장큰 차이점은 지형에 변화를 줄때 둥글둥글하게 변화를 준다는것. 사실 뭐 거창한 곡면 함수 이런게 필요할줄 알았지만 그냥 2차함수로 해결했다. X축으로 한번 적용해주고 Y축으로 한번 적용해주면 적당하게 둥글둥글한 지형변환 효과를 줄수 있다.

일단은 여기서 하이트맵 에디터는 대충 마무리를 지었다. 더 추가하기 위해서는 최적화 코드를 넣어야 하고 현재 구조가 별로 맘에도 안들고. 후…지금 당장 여건이 이걸 붙잡고 있기에 허락하지도 않고. 영천이형은 이것 보다는 일단 맥스익스포터와 그거 랜더러를 구현해 보라고 하셔서 나중에 다시 공부하게 된다면 그쪽으로 생각하고 있다.

2 소스 및 실행파일 #

실행에 필요한 DLL이 포함되어 있지 않다 -_-; 비주얼스튜디오 6.0을 깔도록…..

Picking

1 Picking #

2004년 7월 22일

  • 맵에디터에서 마우스 입력을 위해서 가장 기초적인 Picking을 구현!

카메라가 바라보는 방향이 화면에 나오고 있을때 마우스의 위치로 화면안쪽으로 향하는 반직선을 만들고 그 반직선과 교차하는 폴리곤을 선택하는 것을 Picking 이라고 한다.

이후 설명하는 대부분의 것이 제가 잘 모르는 관계로 틀린 내용일수 있습니다 -_-

즉 간단히 마우스로 가져다가 댔을때 그 바로 아래있는 폴리곤이나 오브젝트를 선택할수 있는 방법이다. 음 위의 Picking을 구현하기 위해서는 현재 자신이 바라보는 화면에 랜더링 파이프라인에서 프로젝션 단계를 거친 화면임을 이해해야 한다. 으음 나도 잘 모르니 넘어가도록 하자… 사실 나는 설명할게 없다. 일단 간단하게 내가 구현한 Picking화면을 보자

http://lagoons.net/docdata/Height_3.jpg

위의 스크린샷중에 Picking 이라고 되고 재대로 텍스쳐가 입혀져 있는 곳에 원래는 마우스가 있다. 흐음 딱히 설명할게 없으니 내 코드좀 올리고… 전체 프로젝트 파일올리고 여기저기 소스 배껴온 Reference 주소를 올리고 끝내자..

IntersectTriangle

  • 이 함수는 하나의 직선을 의미하는 백터 2개와 하나의 폴리곤을 나타내는 백터 3개를 넣어 충돌의 여부와 충돌지점까지의 거리를 얻을수 있다. 물론 내가 구현한게 아니라… DX 샘플중 Picking 에 보면 이미 구현되어 있는 함수를 후려와서 사용했다.
    이 함수는 감자닷넷의 주인장이신 감자님이 어떤식으로 구현되어 있는지 올린 글이 있다. 보고 이해는 하겠는데… 기억하지는 못한다. 쿨럭…

참고문서

BOOL IntersectTriangle( const D3DXVECTOR3& orig, const D3DXVECTOR3& dir,
                       D3DXVECTOR3& v0, D3DXVECTOR3& v1, D3DXVECTOR3& v2, FLOAT* t, FLOAT* u, FLOAT* v )
{
    // Find vectors for two edges sharing vert0
    D3DXVECTOR3 edge1 = v1 - v0;
    D3DXVECTOR3 edge2 = v2 - v0;

    // Begin calculating determinant - also used to calculate U parameter
    D3DXVECTOR3 pvec;
    D3DXVec3Cross( &pvec, &dir, &edge2 );

    // If determinant is near zero, ray lies in plane of triangle
    FLOAT det = D3DXVec3Dot( &edge1, &pvec );

    D3DXVECTOR3 tvec;
    if( det > 0 )
    {
        tvec = orig - v0;
    }
    else
    {
        tvec = v0 - orig;
        det = -det;
    }

    if( det < 0.0001f )
        return FALSE;

    // Calculate U parameter and test bounds
    *u = D3DXVec3Dot( &tvec, &pvec );
    if( *u < 0.0f || *u > det )
        return FALSE;

    // Prepare to test V parameter
    D3DXVECTOR3 qvec;
    D3DXVec3Cross( &qvec, &tvec, &edge1 );

    // Calculate V parameter and test bounds
    *v = D3DXVec3Dot( &dir, &qvec );
    if( *v < 0.0f || *u + *v > det )
        return FALSE;

    // Calculate t, scale parameters, ray intersects triangle
    *t = D3DXVec3Dot( &edge2, &qvec );
    FLOAT fInvDet = 1.0f / det;
    *t *= fInvDet;
    *u *= fInvDet;
    *v *= fInvDet;

    return TRUE;
}

Picking

  • 위의 충돌검색 함수를 사용하기 위해 언프로젝션과 기타 준비등등을 해주는 함수 위의 충돌 검색함수의 경우 하나의 폴리곤씩 검색을 하기 때문에 하이트맵에 적용하기 위해서 폴리곤 하나 단위로 루프를 도는 구문등이 있다. 실제로 피킹에서 가장 중요한것은 언프로젝션IntersectTriangle 이다.

참고문서 [http]http://www.gamza.net/ez2000/ezboard.exe?db=Algorithm&action=read&dbf=14&page=0&depth=1

BOOL Picking(HWND hWnd)
{
        POINT ptCursor;
        D3DXVECTOR3 vPickRayDir;
        D3DXVECTOR3 vPickRayOrig;

        INTERSECTION_HEIGHTMAP Intersection;
        ZeroMemory(&Intersection, sizeof(INTERSECTION_HEIGHTMAP));
        Intersection.m_fDist = FAR_DISTANCE;


        _cdbg("Picking Start");

        // 마우스 포인터로 부터 3D객체를 향해서 뻗는 3D직선의 백터를 얻는다. 언프로젝션부분
        D3DVIEWPORT9 vp;
        m_pGraphics->GetDeviceCOM()->GetViewport(&vp);
        D3DXMATRIXA16 matProj;
        m_pGraphics->GetDeviceCOM()->GetTransform( D3DTS_PROJECTION, &matProj );

        GetCursorPos( &ptCursor );
        ScreenToClient( hWnd, &ptCursor );

        D3DXVECTOR3 v;
        v.x = ((  (((ptCursor.x-vp.X)*2.0f/vp.Width ) - 1.0f)) - matProj._31 ) / matProj._11;
        v.y = ((- (((ptCursor.y-vp.Y)*2.0f/vp.Height) - 1.0f)) - matProj._32 ) / matProj._22;
        v.z = 1.0f;

        // Get the inverse view matrix
        D3DXMATRIXA16 matView, m;
        m_pGraphics->GetDeviceCOM()->GetTransform( D3DTS_VIEW, &matView );
        D3DXMatrixInverse( &m, NULL, &matView );

        // Transform the screen space pick ray into 3D space
        vPickRayDir.x  = v.x*m._11 + v.y*m._21 + v.z*m._31;
        vPickRayDir.y  = v.x*m._12 + v.y*m._22 + v.z*m._32;
        vPickRayDir.z  = v.x*m._13 + v.y*m._23 + v.z*m._33;
        vPickRayOrig.x = m._41;
        vPickRayOrig.y = m._42;
        vPickRayOrig.z = m._43;

        // 위에서 얻는 백터와 Picking의 대상이 되는 오브젝트의 충돌 체크를 한다.
        FVF_HEIGHTMAP*  pVerHeightMap;
        WORD*                   pIndexHeightMap;
        GetVertexBuffer()->Lock();
        pVerHeightMap = (FVF_HEIGHTMAP*)m_pVB->GetPtr();

        GetIndexBuffer()->Lock();
        pIndexHeightMap = (WORD*)m_pIB->GetPtr();

        FLOAT fBary1, fBary2, fDist;
        for( DWORD i=0; i<m_dwPrimitives; i++ )
        {
                D3DXVECTOR3 v0 = pVerHeightMap[pIndexHeightMap[3*i+0]].p;
                D3DXVECTOR3 v1 = pVerHeightMap[pIndexHeightMap[3*i+1]].p;
                D3DXVECTOR3 v2 = pVerHeightMap[pIndexHeightMap[3*i+2]].p;

                // Check if the pick ray passes through this point
                // 충돌이 있다면 
                if( IntersectTriangle( vPickRayOrig, vPickRayDir, v0, v1, v2, &fDist, &fBary1, &fBary2 ) )
                {
                        _cdbg("Intersection occour");

                        // 일단 가장 가까운 값을 유지 해야 한다
                        if(Intersection.m_fDist > fDist)
                        {
                                Intersection.m_fDist = fDist;

                                Intersection.m_verHeightMap[0].p = v0;
                                Intersection.m_verHeightMap[1].p = v1;
                                Intersection.m_verHeightMap[2].p = v2;

                                Intersection.m_dwPrimitiveIndex = i;
                        }
                }
        }

        m_pIB->Unlock();
        m_pVB->Unlock();

        // Picking 된 버텍스로 출력할 것 설정
        WORD Indexs[3] = {0,1,2};
        m_pVBIntersect->Set(0,3,Intersection.m_verHeightMap);
        m_pIBIntersect->Set(0,3,Indexs);

        // Debug ///////////////////////////////////
        sprintf(m_szDebugBuff,
                "Primitive Index : %d, Picking Distance : %f\nMouse Position X : %d, Mouse Position Y : %d",
                Intersection.m_dwPrimitiveIndex, Intersection.m_fDist, ptCursor.x, ptCursor.y);
        // Debug ///////////////////////////////////

        return TRUE;
}

아 그러고 보니 위의 Picking 소스는 카메라의 위치가 0,0,0 이 아니면 재대로 돌아가지 않는데 흐음… 어떤 행렬값이나 백터값에 보정을 해줘야 할것 같다. 아마도 v 백터값에 카메라 포지션을 더해주는게 아닐까 하는데… 사실 정확히는 모르겠다. -_-;;

현재는 실행시키면 돌아가기는 하지만… 카메라의 위치를 바꾸거나 윈도우의 크기를 변화시키면 안된다. 이 문제에 대해서는 Picking에서 사용하는 수학적 지식을 완전히 익혀야 해결할수 있을듯…

1.1 오차의 해결 #

2004년 7월 27일 카메라의 위치와 윈도우 크기를 바꿀때 생기는 오차를 해결

  • 상당히 간단했다. 일단 윈도우 크기를 바꿀때 생기는 오차는 GetviewPort로 윈도우 크기를 얻어올때 이 함수가 실제 윈도우 크기를 얻어오는게 아니라 DX에서 설정한 해상도..랄까.. 처음 설정한 크기를 얻어오기 때문에 발생했다. 사실 윈도우 크기가 바뀌더라도 윈도우에 찍히는 그림은 계속 그 해상도를 유지한다. 어쨋든 이 값보다는 실제 윈도우 크기가 필요하기 때문에서 GetClientrect 를 사용해야 한다. 코드를 보면
        D3DVIEWPORT9 vp;
        m_pGraphics->GetDeviceCOM()->GetViewport(&vp);
        D3DXMATRIXA16 matProj;
        m_pGraphics->GetDeviceCOM()->GetTransform( D3DTS_PROJECTION, &matProj );

        GetCursorPos( &ptCursor );
        ScreenToClient( hWnd, &ptCursor );

        D3DXVECTOR3 v;
        v.x = ((  (((ptCursor.x-vp.X)*2.0f/vp.Width ) - 1.0f)) - matProj._31 ) / matProj._11;
        v.y = ((- (((ptCursor.y-vp.Y)*2.0f/vp.Height) - 1.0f)) - matProj._32 ) / matProj._22;
        v.z = 1.0f;

이 부분을

        RECT rt;
        GetClientRect( hWnd, &rt );
        D3DXMATRIXA16 matProj;
        m_pGraphics->GetDeviceCOM()->GetTransform( D3DTS_PROJECTION, &matProj );

        GetCursorPos( &ptCursor );
        ScreenToClient( hWnd, &ptCursor );

        D3DXVECTOR3 v;
        v.x = ((  (((ptCursor.x-rt.left)*2.0f/rt.right ) - 1.0f)) - matProj._31 ) / matProj._11;
        v.y = ((- (((ptCursor.y-rt.top)*2.0f/rt.bottom) - 1.0f)) - matProj._32 ) / matProj._22;
        v.z = 1.0f;

으로 바꿔 줘야 한다. 그리고 카메라의 위치가 바꼈을때의 오차 해결은 vPickRayOrig 값의 오차를 보정해 줘야 하는데 보정값은 당연히 카메라의 위치가 된다. 위의 백터값에서 카메라의 위치값을 빼주면 된다!!

1.2 Reference #

2 소스 #

비주얼 스튜디오 6.0 으로 작업했는데 코드는 그다지 아름답지 못하다. 2003에서 컴파일이 안될정도니..

HeightMap Using Texture Matrix ( using one Texture )

1 Make HeightMap Vertex,Index Buffer #

2004년 7월 18일 쯤

  • 현재 하이트 맵의 가장 간단한건 구현… 와이어프레임으로 랜더링하고 있다.

raw 파일에서 64*64 크기의 높이 정보를 읽어와서 버텍스들을 생성하고 인덱스버퍼를 만들어 랜더링한다. 랜더링 상태를 SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME) 으로 설정해서 와이어프레임이 꽉꽉차서 보인다.(과거 이 옵션을 몰라서 랜더링 옵션을 D3DPT_LINESTRIP 으로 하고 랜더링된 와이어프래임을 보고 “어째서 나의 와이어 프레임은 부실하게 나올까” 하고 고민한적이있다….)

음 하이트맵의 데이타를 생성하는 코드를 써보면

         unsigned char TmpHeight;

        // 버텍스버퍼와 인덱스버퍼를 위한 중간 버퍼들
        FVF_HEIGHTMAP*  pVertexData = new FVF_HEIGHTMAP[m_dwVertices];
        WORD*                   pIndexData  = new WORD[m_dwPrimitives*3];

        //파일에서 높이 정보를 읽어오고 버텍스정보를 삽입한다.
        FILE* pFile;
        UINT nIndex = 0;
        pFile = fopen(szFilename, "rb");
        for(unsigned y = 0;y < (HEIMAP_Y + 1); y++)
        {
                for(unsigned x = 0;x < (HEIMAP_X + 1); x++)
                {
                        TmpHeight = fgetc(pFile);

                        // 버텍스 데이타 구성
                        pVertexData[x + y * (HEIMAP_X+1)].fX = (float)x;
                        pVertexData[x + y * (HEIMAP_X+1)].fY = (float)TmpHeight / 15.0f - 40;
                        pVertexData[x + y * (HEIMAP_X+1)].fZ = (float)y;
                        pVertexData[x + y * (HEIMAP_X+1)].dwColor = 0xffffffff;

                        if( (y < HEIMAP_Y) && (x < HEIMAP_X) )
                        {
                                // 인덱스 데이타 구성
                                pIndexData[(x+y*(HEIMAP_X))*6 +0] = x   + y     * (HEIMAP_X+1);
                                pIndexData[(x+y*(HEIMAP_X))*6 +1] = x+1 + y     * (HEIMAP_X+1);
                                pIndexData[(x+y*(HEIMAP_X))*6 +2] = x   + (y+1) * (HEIMAP_X+1);

                                pIndexData[(x+y*(HEIMAP_X))*6 +3] = x   + y     * (HEIMAP_X+1);
                                pIndexData[(x+y*(HEIMAP_X))*6 +4] = x+1 + (y+1) * (HEIMAP_X+1);
                                pIndexData[(x+y*(HEIMAP_X))*6 +5] = x   + (y+1) * (HEIMAP_X+1);
                        }
                }
        }
        fclose(pFile);

        // 버텍스버퍼를 구성한다. 
        m_pVB->Create(m_pGraphics, m_dwVertices, sizeof(FVF_HEIGHTMAP), D3DFVF_HEIGHTMAP);
        m_pVB->Set(0, m_dwVertices, pVertexData);

        //인덱스데이타를 구성한다.
        m_pIB->Create(m_pGraphics, m_dwPrimitives*3);
        m_pIB->Set(0, m_dwPrimitives*3, pIndexData);

        // 버덱스데이타, 인덱스데이타 메모리를 해재한다.
        delete [] pVertexData;
        delete [] pIndexData;

이정도… 간단하다. 밑은 스크린샷 이제는 텍스쳐 매트릭스를 적용하여 한 중류의 텍스쳐로 덥어보자

http://lagoons.net/docdata/Height_1.jpg

2 Using Texture Matrix #

http://myevan.net/phpBB/viewtopic.php?t=1445 (빗자루님의 포럼) 에 텍스쳐 메트릭스에 관한 내용이 있다.

음 위의 내용을 이해하면 텍스쳐매트릭스를 사용하는데는 지장이 없을듯 하다. 문제는 다이렉트X SDK인데… 나만 그러는지 모르겠지만 D3DXMatrixDeterminant 를 사용했을때 런타임에러가 발생한다. 그래서 2003 Summer Update로 업데이트를 했는데 일단 위의 문제는 해결됐다. 다만 인테페이스가 바뀐게 좀 있어서 짜증이 난다. -_-;;; 쓰던 코드를 좀 손봐야 할듯하다.

2004년 7월 22일

http://lagoons.net/docdata/Height_2.jpg

위의 화면처럼 나오는데… 여러종류의 텍스쳐를 입히는것도 아주 쉬울듯하다. 그냥 텍스쳐별로 따로따로 인덱스버퍼를 구성하면 되니… 그런데 그렇게 할려면 맵의 파일 포멧이 좀 바껴야 할태고 그럴려면 이왕에 전용 에디터를 만드는쪽이 좆타… 라는 결론이 나와서 -_-;;; 좀 귀찮다. 일단 위의 하이트맵을 랜더링하는 코드를 보면

void CHeightMap::Rendering(HEIGHTMAP_DRAW_OPTION hdOption)
{
        D3DXMATRIX matrixView;
        m_pGraphics->GetDeviceCOM()->GetTransform(D3DTS_VIEW, &matrixView);
        FLOAT fDeterminantMatView = D3DXMatrixDeterminant(&matrixView);

        D3DXMATRIX matViewInv;
        D3DXMatrixInverse(&matViewInv, &fDeterminantMatView, &matrixView);

        D3DXMATRIX matScale;
        ZeroMemory(&matScale, sizeof(D3DXMATRIX));
        matScale._11=1.0f/1.0f;
        matScale._32=1.0f/1.0f;

        D3DXMATRIX matTex;
        D3DXMatrixMultiply(&matTex, &matViewInv, &matScale);

        // 텍스쳐 좌표 자동생성을 위한 세팅
        m_pGraphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
        m_pGraphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);
        m_pGraphics->GetDeviceCOM()->SetTransform(D3DTS_TEXTURE0, &matTex);

        // 랜더링 옵션이 WIRE이면 랜더링 상태를 설정
        if(hdOption == RENDER_WIRE)
                m_pGraphics->GetDeviceCOM()->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);

        m_pIB->Render( D3DPT_TRIANGLELIST, 0, 0, m_pVB->GetNumVertices(), 0, m_dwPrimitives, m_pVB, m_pTextureDefault );

        // 랜더링 옵션이 WIRE이면 랜더링 상태를 해제
        if(hdOption == RENDER_WIRE)
                m_pGraphics->GetDeviceCOM()->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);


        // 텍스쳐 좌표 자동생성을 위한 세팅 해제
        m_pGraphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU);
        m_pGraphics->GetDeviceCOM()->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE);
}

이정도… 자세한 내용은 빗자루님의 글을 참고하는게 좋다. 간단히 설명하면 카메라의 위치와 버텍스의 위치값으로 UV값을 자동 생성해서 텍스쳐를 입히는 것이다.

자 그럼 다음은 맵에디터다 -_-

2.1 Texture Matrix Reference #

  • http://myevan.net/
    에서 texture matrix 로 검색을 한뒤 링크지식/여치님의 하드웨어가속 타일링 을 찾아보면 있다.