이전에 직선의 방정식을 통하여 타일 픽킹을 해보았는데,

이번에는 조금 더 편하게 내적을 통해 타일 픽킹을 해보자.

 

#1. 마름모꼴 구하기

D3DXVECTOR3 vVertex[4] = 
{
	{m_vecTile[iIndex]->vPos.x, m_vecTile[iIndex]->vPos.y + (TILECY * 0.5f), 0.f},
	{m_vecTile[iIndex]->vPos.x + (TILECX * 0.5f), m_vecTile[iIndex]->vPos.y, 0.f},
	{ m_vecTile[iIndex]->vPos.x, m_vecTile[iIndex]->vPos.y - (TILECY * 0.5f), 0.f },
	{ m_vecTile[iIndex]->vPos.x - (TILECX * 0.5f), m_vecTile[iIndex]->vPos.y, 0.f },
}; 

 

#2. 마름모꼴의 방향벡터

D3DXVECTOR3 vVertexDir[4] = 
{		
	vVertex[1] - vVertex[0],
	vVertex[2] - vVertex[1],
	vVertex[3] - vVertex[2],
	vVertex[0] - vVertex[3],
};

 

#3. 법선벡터 뽑아내기

D3DXVECTOR3 vNormalVector[4] = 
{	
	{ -vVertexDir[0].y, vVertexDir[0].x, 0.f },
	{ -vVertexDir[1].y, vVertexDir[1].x, 0.f },
	{ -vVertexDir[2].y, vVertexDir[2].x, 0.f },
	{ -vVertexDir[3].y, vVertexDir[3].x, 0.f }
}; 

 

#4. 마지막으로 마름모꼴 정점에서 마우스를 바라보는 방향벡터를 구하기

D3DXVECTOR3 vMouseDir[4]; 
for (int i = 0 ; i < 4 ; ++i)
	vMouseDir[i] = vPos - vVertex[i];

for (int i = 0 ; i < 4 ; ++i)
{
	D3DXVec3Normalize(&vNormalVector[i], &vNormalVector[i]); 
	D3DXVec3Normalize(&vMouseDir[i], &vMouseDir[i]);
}

for (int i = 0 ; i < 4 ; ++i )
{
	if (0 < D3DXVec3Dot(&vMouseDir[i], &vNormalVector[i]))
		return false; 
}

return true; 

 

타일 픽킹이란 타일을 클릭하였을 때를 말한다.

예를 들어 클릭한 타일이 교체된다던지 해당 타일에 충돌 처리 시킨다는 등의 옵션을 넣을 수도 있다.

 

이번에는 해당 타일을 클릭하였을 때 타일의 그림을 교체해보자.

먼저 마우스 포인터가 마름모 꼴로 되어있는 타일의 안에 있는지 확인해야한다.

 

확인하는 방법은 여러가지가 있다.

그 중에서 직선의 방정식을 이용하여 판별하는 방법과 내적을 통해서 판별하는 방법을 알아보겠다.

 

직선의 방정식을 통해 판별하는 방법부터 알아보자.

직선의 방정식 공식을 다음과 같다.

y = ax + b

 

먼저 기울기부터 구해보자.

기울기(a)는 (y의 증가량) / (x의 증가량) 이며, 기울기는 수평선과 평행하면 0이고,

오른쪽 끝이 올라가면 양의 기울기이고 내려가면 음의 기울기를 가진다.

 

즉 x가 증가할 때 그에따른 y값이 증가하면 양의 기울기,

x가 증가할 때 그에따른 y값이 감소하면 음의 기울기,

x값이 증가할 때, y의 값이 변화가 없다면 기울기는 0이 나오게 된다.

float fSlope[4] = 
{
	(TILECY * 0.5f) / (TILECX * 0.5f), 
	-(TILECY * 0.5f) / (TILECX * 0.5f),
	(TILECY * 0.5f) / (TILECX * 0.5f),
	-(TILECY * 0.5f) / (TILECX * 0.5f)
};

 

타일의 Y축 크기 * 0.5 / 타일의 X축 크기 * 0.5

-타일의 Y축 크기 * 0.5 / 타일의 X축 크기 * 0.5

타일의 Y축 크기 * 0.5 / 타일의 X축 크기 * 0.5

-타일의 Y축 크기 * 0.5 / 타일의 X축 크기 * 0.5

 

이렇게 4개의 정점의 기울기를 구하였고,

이제 마름모꼴 정점 4개를 구해보자.

ax = -b x y

 

D3DXVECTOR3 vVertex[4] = 
{
	{m_vecTile[iIndex]->vPos.x, m_vecTile[iIndex]->vPos.y + (TILECY * 0.5f), 0.f},
	{m_vecTile[iIndex]->vPos.x + (TILECX * 0.5f), m_vecTile[iIndex]->vPos.y, 0.f},
	{ m_vecTile[iIndex]->vPos.x, m_vecTile[iIndex]->vPos.y - (TILECY * 0.5f), 0.f },
	{ m_vecTile[iIndex]->vPos.x - (TILECX * 0.5f), m_vecTile[iIndex]->vPos.y, 0.f },
}; 

마름모꼴의 정점은 해당 타일의 중점과 타일의 사이즈를 알고있다는 점을 응용해서 찾아낼 수 있다.

 

 

이제 기울기와, 4개의 정점를 모두 알고 있으므로 마지막으로 b(y 절편)을 구하면된다. 

b = y - ax;

float fY_Intercept[4] = 
{
	vVertex[0].y - fSlope[0] * vVertex[0].x, 
	vVertex[1].y - fSlope[1] * vVertex[1].x,
	vVertex[2].y - fSlope[2] * vVertex[2].x,
	vVertex[3].y - fSlope[3] * vVertex[3].x,
};

0 = ax + b - y

if (0 < fSlope[0] * vPos.x  + fY_Intercept[0] - vPos.y &&
    0 < fSlope[1] * vPos.x + fY_Intercept[1] - vPos.y &&
    0 > fSlope[2] * vPos.x + fY_Intercept[2] - vPos.y &&
    0 > fSlope[3] * vPos.x + fY_Intercept[3] - vPos.y )
{
	return true; 
}

return false;

식이 계산되었으므로, 부등호로 직선보다 위 또는 아래에 있는지 판단하여

위에 있으면 음수 아래에 있으면 양수가 된다.

즉, 클릭한 좌표가 직선 위에 있으면 범위 밖이기 때문에 false가 되고 아래에 있으면 범위 안이므로 true가 된다.

'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

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

화면에 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

+ Recent posts