타일을 배치하기 전 몇개의 타일을 배치할 것인지 정해야한다.

화면에 600개의 타일을 설치한다 가정하고, XY축의 타일 갯수와 크기를 정해주자.

타일의 크기는 현재 가지고 있는 타일 리소스의 크기와 동일하게 맞춰주면 된다.

#define TILECX 130
#define TILECY 68
#define TILEX 20
#define TILEY 30

이제 타일을 배치해야하는데 어떤식으로 배치할 것인지 생각해야되는데,

일반적으로는 정사각형의 타일을 여러개 설치하는 방식을 많이 사용했을 것이다.

그런데 마름모 꼴의 타일을 배치해야한다고 했을 때

다음과 같은 형태가 나오게 된다.

여기서 그림을 확인해보면 X축의 경우 첫번째 라인 시작 위치와 두번째 라인 시작 위치가 서로 다르게 되어있다.

그런데 4번째 라인에 타일을 배치할 때 2번째 라인과 동일한 방식으로 배치가 되는 것을 확인할 수 있다. 

위의 그림을 보았을 때 1, 3번째와 2, 4번째 라인에 배치된 타일을 보면 홀수와 짝수로 나뉘어진 것을 알 수 있고

이를 이용해서 타일을 배치하면 된다.

 

타일 배치를 위한 코드는 다음과 같다.

float fX = 0.f, fY = 0.f;
TILE* pTile = nullptr; 
m_vecTile.reserve(TILEX * TILEY);

for (int i = 0 ; i < TILEY; ++i)
{
	for (int j = 0 ; j < TILEX; ++j)
	{
		pTile = new TILE; 
		pTile->vSize = { 1.f , 1.f, 0.f }; 
		pTile->byDrawID = 10; 
		pTile->byOption = 0;
		fX = (j * TILECX) + ((i % 2) * (TILECX >> 1));
		fY = i * (TILECY >> 1);
		pTile->vPos = { fX, fY, 0.f }; 
		m_vecTile.emplace_back(pTile); 
	}
}

우선 타일을 만들기위해 vector로 타일을 저장할 변수를 하나 만들어준다.

그리고 vector안에 들어갈 타일의 객체를 생성하고,

vector의 사이즈를 reserve함수를 통해 배치할 타일의 갯수만큼 미리 예약을 해놓는다.

그리고 이중포문을 통해서 타일을 알맞게 배치를 하면된다.

 

여기서 타일은 보통 X축의 타일이 먼저 배치가 된 후에 Y축이 깔리는 형태가 일반적이기 때문에 위와 같이 작성하였다.

타일을 배치할 때 가장 먼저 봐야될 것은 타일간의 간격이다.

 

첫번 째 타일이 배치되고 그 다음 배치 될 타일이 일정한 간격에 맞게 배치를 시켜야하는데,

fX = (j * X축 타일의 크기) + ((i % 2) * (X축 타일의 크기 / 2));

fY = i * (Y축 타일의 크기 / 2);

이런식으로 되어있다.

 

X축 부터 보자면 첫번째 라인에 64 크기를 가진 타일을 배치할 경우

fX = (j * X축 타일의 크기) + ((0 % 2) * (X축 타일의 크기 / 2)) 가 되면서

첫 번째 라인 * 64 + 0 * 32 만큼의 위치 기준으로 타일을 배치하게 된다.

 

그리고 두번 째 라인을 그리게 되면

fX = (j * X축 타일의 크기) + ((1 % 2) * (X축 타일의 크기 / 2)) 가 되면서

두 번째 라인 * 64 + 1 * 32 즉 X축의 위치가 32만큼 더 떨어진 위치 기준으로 타일을 배치하게된다.

 

이런 식으로 fY 위치는 일정한 간격으로 유지한 상태로 fY가

홀수 일 때는 라인 * 64 + 0 * 32 기준

짝수 일 때는 라인 * 64 + 1 * 32 기준으로 타일이 배치되는 것을 확인할 수 가 있다.

 

결과물

 

'Programming > MFC' 카테고리의 다른 글

MFC - 기본 이론 , 계층 구조  (0) 2020.11.29
MFC - 타일 픽킹(내적)  (0) 2020.11.29
MFC - 타일 픽킹(직선의 방정식)  (0) 2020.11.25
MFC - 싱글 텍스처, 멀티 텍스처  (0) 2020.11.22
MFC - 디바이스 초기화  (0) 2020.11.22

Texture 클래스

virtual const TEXINFO* Get_TexInfo(const wstring& wstrFilePath,
		const wstring& wstrObjectKey,
		const wstring& wstrStateKey = L"",
		const int& iIndex = 0)PURE;

virtual HRESULT Insert_Texture(const wstring& wstrFilePath,
		const wstring& wstrObjectKey,
		const wstring& wstrStateKey = L"",
		const int& iIndex = 0)PURE;

먼저 그림을 보관할때 방법은 오브젝트 키 안에 스테이트 키가 존재해야 한다.

단, 이건 멀티 텍스처 일 경우에만


싱글텍스처는 그림이 한장 뿐이라서 오브젝트키는 필요하더라도 스테이트키는 필요가 없다.

그러므로 인자 값에 스테이트 키는 디폴트 값으로 정의한다.

 

SingleTexture 클래스 와 MultiTexture 클래스 모두 Texture 클래스를 상속 받는다.

 

SingleTexture 클래스

if (FAILED(D3DXGetImageInfoFromFile(wstrFilePath.c_str(), &m_tTexInfo.tImageInfo)))
{
	ERR_MSG(L"Loading Image Info Failed"); 
	return E_FAIL;
}
if (FAILED(D3DXCreateTextureFromFileEx(CGraphic_Device::Get_Instance()->Get_Device(),
	wstrFilePath.c_str(),
	m_tTexInfo.tImageInfo.Width,
	m_tTexInfo.tImageInfo.Height,
	m_tTexInfo.tImageInfo.MipLevels, 
	0,
	m_tTexInfo.tImageInfo.Format, 
	D3DPOOL_MANAGED, 
	D3DX_DEFAULT, 
	D3DX_DEFAULT, 
	0, 
	nullptr, 
	nullptr, 
	&m_tTexInfo.pTexture)))
{
	wstring wstrErr = wstrFilePath + L"Loading Failed"; 
	ERR_MSG(wstrErr.c_str()); 
	return E_FAIL; 
}
return S_OK;

D3DXGetImageInfoFromFile()

-> 주어진 이미지 파일에 대한 정보를 검색하는 함수이다.

 

D3DXCreateTextureFromFileEx()

-> 파일에서 텍스처를 만드는 것.

-> D3DXCreateTextureFromFile 보다 고급 기능.

 

여기서 눈여겨봐야 될 것은 MipLevels 와 D3DPOOL_MANAGED 이다.

 

Miplevels

-> 동일한 그림을 다양한 사이즈로 만드는 것이다.

-> 예를 들어 다음과 같이 동일한 이미지를 다양한 사이즈로 만들어 둔다.

-> Miplevels는 사이즈 별로 단계가 존재한다.

1단계 : 512 * 512

2단계 : 256 * 256

3단계 : 128 * 128

4단계 : 64 * 64

5단계 : 32 * 32

6단계 : 16 * 16

-> 단계는 가장 가까운 곳 부터 순서대로 1단계부터 증가한다.

 

텍스처를 쓰기 위해 그림파일을 불러오는 함수

D3DXCreateTextureFromFile() 또는 D3DXCreateTextureFromFileEx()를 사용하는데,

위의 두 개의 함수는 서로 다르다.

D3DXCreateTextureFromFile()는 Mip Levels가 자동으로 여러 단계로 만들어지고,

D3DXCreateTextureFromFileEx()는 위의 함수의 확장형이며 Mip Levels의 단계를 입력한 만큼만 설정이 가능하다.

함수 인자 값 UNIT MipLevels에 2라고 쓰면 2단계까지만 Mip Levels를 만든다는 것이다.

 

이렇게 하면 기본형을 사용할 때보다 확장형을 사용할 때 로딩 속도를 더 빠르게 할 수 있게된다.

이 값이 0 또는 D3DX_DEFAULT 일 경우, 완전한 밉맵 체인이 생성 된다.

 

D3DPOOL

-> 리소스에 대한 버퍼를 보유하는 메모리 클래스를 뜻한다.

-> D3DPOOL에는 다음과 같은 열거형 값이 존재한다.

typedef enum D3DPOOL { 
  D3DPOOL_DEFAULT      = 0,
  D3DPOOL_MANAGED      = 1,
  D3DPOOL_SYSTEMMEM    = 2,
  D3DPOOL_SCRATCH      = 3,
  D3DPOOL_FORCE_DWORD  = 0x7fffffff
} D3DPOOL, *LPD3DPOOL;

D3DPOOL_DEFAULT

->  주어진 리소스에 대해 요청 된 사용 집합에 가장 적합한 메모리 풀에 배치된다.

-> 그래픽카드에 있는 메모리만 사용 
-> 속도가 제일 빠름
-> 안전성 최악 

 

D3DPOOL_MANAGED

->  필요에 따라 장치에서 액세스 할 수있는 메모리에 자동으로 복사된다.

-> 그래픽카드에 있는 메모리를 사용하다가 부족하면 램의 메모리 사용 
-> 속도와 안전성이 모두 보장되어 있음. 

 

D3DPOOL_SYSTEMMEM

-> 일반적으로 Direct3D 장치에서 액세스 할 수없는 메모리에 배치된다.

-> 현재 사용하고 있는 램의 메모리 사용 
-> 속도 최악

 

D3DPOOL_SCRATCH

->  시스템 RAM에 배치되며 장치가 손실되었을 때 다시 만들 필요가 없다.

 

위에 네 가지 중에서 주로 MANAGED 타입을 많이 사용한다고 한다.

 

MultiTexture 클래스

auto& iter_Find = m_mapTexture.find(wstrStateKey); 
	if (iter_Find != m_mapTexture.end())
		return E_FAIL; 

	TCHAR szBuf[MAX_PATH] = L"";
	TEXINFO* pTexInfo = nullptr; 
	for (int i = 0 ; i < iIndex; ++i)
	{
		swprintf_s(szBuf, wstrFilePath.c_str(), i);
		pTexInfo = new TEXINFO;
		if (FAILED(D3DXGetImageInfoFromFile(szBuf, &pTexInfo->tImageInfo)))
		{
			ERR_MSG(L"Loading Image Info Failed");
			return E_FAIL;
		}
		if (FAILED(D3DXCreateTextureFromFileEx(CGraphic_Device::Get_Instance()->Get_Device(),
			szBuf,
			pTexInfo->tImageInfo.Width,
			pTexInfo->tImageInfo.Height,
			pTexInfo->tImageInfo.MipLevels,
			0,
			pTexInfo->tImageInfo.Format,
			D3DPOOL_MANAGED,
			D3DX_DEFAULT,
			D3DX_DEFAULT,
			0,
			nullptr,
			nullptr,
			&pTexInfo->pTexture)))
		{
			wstring wstrErr = wstrFilePath + L"Loading Failed";
			ERR_MSG(wstrErr.c_str());
			return E_FAIL;
		}
		m_mapTexture[wstrStateKey].emplace_back(pTexInfo); 
	}
	return S_OK;

 

virtual const TEXINFO * Get_TexInfo(
const wstring & wstrStateKey,
const int & iIndex) override;

virtual HRESULT Insert_Texture(
const wstring & wstrFilePath,
const wstring & wstrStateKey,
const int & iIndex) override;

 

멀티 텍스처는 싱글 텍스처와는 반대로 스테이트 키를 따로 지정해주기 때문에 디폴트 값이 아닌 상태로 정의한다.

map<wstring, vector<TEXINFO*>> m_mapTexture;

멀티 텍스처는 여러 장의 이미지를 관리하기 때문에 map 과 vector를 함께 사용하여 관리한다.

폴더 내의 이미지를 몇장이 있는지 확인하거나 그 이미지 들을 불러오기 위해 인자 값에 Index를 추가한다.

 

MultiTexture 삭제

	for (auto& rPair : m_mapTexture)
	{
		for (auto& rTexInfo : rPair.second)
		{
			Safe_Delete(rTexInfo);
		}
		rPair.second.clear(); 
		//rPair.second.swap(vector<TEXINFO*>()); 
		rPair.second.shrink_to_fit(); 
	}

map 과 vector 둘 다 사용하였기 때문에 지울 때도 map 과 vector 따로 삭제해야 한다.

vector를 삭제 할 때는 스왑 함수를 사용하여 임시 객체와 교체를 통해서 삭제해도 되고,

shrink_to_fit()이라는 함수를 사용하여 삭제해도 된다.

shrink_to_fit() 함수는 C++11 부터 생긴 함수이다.

'Programming > MFC' 카테고리의 다른 글

MFC - 기본 이론 , 계층 구조  (0) 2020.11.29
MFC - 타일 픽킹(내적)  (0) 2020.11.29
MFC - 타일 픽킹(직선의 방정식)  (0) 2020.11.25
MFC - 타일 배치  (0) 2020.11.25
MFC - 디바이스 초기화  (0) 2020.11.22

LPDIRECT3D9 m_pSDK; 

-> 사용자가 사용하고자 하는 장치를 조사하고 해당 장치를 제어하기 위한 객체의 할당을 담당하는 com 객체


LPDIRECT3DDEVICE9 m_pDevice;

-> 실질적으로 장치를 제어하는 com 객체. 

 

LPD3DXSPRITE m_pSprite;

 

com 객체란? 

component Object Model 의 약자로 우리가 배우는 다이렉트X에서 제공하는 부품단위의 객체들을 뜻한다. 

개발환경에 상관없이 호환이 가능한 컴포넌트들의 사용을 위해

마이크로 소프트 사가 규정한 표준화된 방법이 바로 com 객체이다. 

 
1. 장치를 제어하기 위한 컴객체를 생성을 먼저 하고. 
2. 이 제어하기 위한 SDK를 이용하여 그래픽카드의 지원 수준을 조사하고. 
3. 그래픽 카드의 지원 수준에 맞춰 장치를 제어하기 위한 컴객체를 생성한다.

 

초기화 부분 함수

D3DCAPS9 DeviceCap; 
ZeroMemory(&DeviceCap, sizeof(D3DCAPS9)); 
m_pSDK = Direct3DCreate9(D3D_SDK_VERSION); 

if (FAILED(m_pSDK->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &DeviceCap)))
{
	AfxMessageBox(L"Create SDK Failed"); 
	return E_FAIL; 
}
DWORD vp = 0;

if (DeviceCap.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
	vp |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
    vp |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;

 

D3DCAPS9 DeviceCap 

-> 장치의 지원 수준을 조사해서 데이터를 보관할 구조체.

 

VERTEXPROCESSING

-> 정점 변환 + 조명연산을 합쳐 버텍스 프로세싱이라 부름. 

조명연산과 정점 변환을 하드웨어(장치에서)에서 처리할 것인지,

아니면 개발자가 소프트웨어에서 처리할 것인지를 뜻한다. 

 

 

D3DPRESENT_PARAMETERS d3dpp; 
ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS)); 

d3dpp.BackBufferWidth = 800;
d3dpp.BackBufferHeight = 600;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferCount = 1;

d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.MultiSampleQuality = 0;

d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = g_hWND;
d3dpp.Windowed = TRUE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
	
d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;


if (FAILED(m_pSDK->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWND, vp, &d3dpp, &m_pDevice)))
{
	ERR_MSG(L"Device Creating Failed");
	return E_FAIL; 
}
return S_OK;

 

BackBufferWidth 

-> 백 버퍼 너비

 

BackBufferHeight 

-> 백 버퍼 높이

 

BackBufferFormat 

-> 백 버퍼 형식

-> 현재 창을 어떤형식으로 렌더링 할 것인지.

-> A8R8G8B8은 알파값 8비트, 빨강 8비트, 파랑 8비트, 초록 8비트를 사용한다는 의미.

 

BackBufferCount = 1;

-> 여기서 1은 총 2개의 백버퍼를 사용하겠다. 즉 디폴트로 한개 생김. 거기에 +1 이 되는 것이다.

 

MultiSampleType = D3DMULTISAMPLE_NONE;

-> D3DMULTISAMPLE_TYPE 열거 형의 멤버

-> SwapEffect가 D3DSWAPEFFECT_DISCARD로 설정되지 않은 경우 값은 NONE이어야 한다.

 

MultiSampleQuality

-> 품질 수준.

-> 범위는 CheckDeviceMultiSampleType에서 사용하는 pQualityLevels에서 반환 한 수준보다 0에서 1 사이 이다.

-> 더 큰 값을 전달하면 D3DERR_INVALIDCALL 오류가 반환된다.

-> 렌더링 대상 또는 깊이 스텐실 표면과 샘플 타입의 쌍 값이 일치해야한다.

 

SwapEffect = D3DSWAPEFFECT_DISCARD;

-> 스왑 체인 방식을 사용하겠다는 의미.

-> 더블버퍼링의 상위 호환 버전.

 

GPU는 비디오 메모리에 존재하는 프레임 버퍼에 계속해서 이미지를 지우고 그리는 과정을 반복한다.

 

그리고 비디오 컨트롤러는 이 프레임 버퍼를 참조해서 스크린에 이미지를 출력하게 되는데,

이 속도가 GPU가 프레임 버퍼에 이미지를 그리는 속도보다 훨씬 빠르다.

 

때문에 프레임 버퍼가 갱신되는 과정이 스크린에 모두 그려지게 되면서,

이미지 전체가 사라졌다 다시 그려지는 것처럼 깜빡거리는 현상이 생길 수 있다.

 

이런 현상을 방지하기 위해 더블 버퍼링 형식을 사용해왔지만,

전면 버퍼와 후면 버퍼의 포인터만 교환하도록해서 더블 버퍼링보다 더 효율적으로 사용할 수 있는

스왑 체인 방식을 사용한다고 한다.

 

hDeviceWindow = g_hWND;

-> 현재 디바이스의 윈도우

 

Windowed = TRUE;

-> TRUE일 경우 창모드, FALSE일 경우 전체화면 모드.

-> MFC에선 창모드로 사용해야한다.

 

EnableAutoDepthStencil = TRUE;

-> Direct3D에서 깊이 버퍼를 관리하면 true이고, 그렇지 않으면 false 

 

AutoDepthStencilFormat = D3DFMT_D24S8;

-> 장치에서 만드는 깊이 스텐실 표면의 DepthFormat을 검색하거나 설정합니다.

 

FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

 

D3DDEVTYPE_HAL

-> 하드웨어 래스터 화. 셰이딩은 소프트웨어, 하드웨어 또는 혼합 변환 및 조명으로 수행된다.

 

#2. 디바이스 삭제

if (m_pDevice)
    m_pDevice->Release(); 
if (m_pSDK)
	m_pSDK->Release(); 

Device 안에서 내부적으로 LPDIRECT3D9을 참조해서 사용하고 있기 때문에,

반드시 Device를 먼저 제거 후 LPDIRECT3D9를 제거해야 메모리 누수가 발생하지 않는다.

 

'Programming > MFC' 카테고리의 다른 글

MFC - 기본 이론 , 계층 구조  (0) 2020.11.29
MFC - 타일 픽킹(내적)  (0) 2020.11.29
MFC - 타일 픽킹(직선의 방정식)  (0) 2020.11.25
MFC - 타일 배치  (0) 2020.11.25
MFC - 싱글 텍스처, 멀티 텍스처  (0) 2020.11.22

+ Recent posts