높이 맵 제작 툴 만들기 (1)

Dev.Write 2010. 1. 18. 13:12 Posted by zetz

기존에 Terrain 코드를 작성하면서 느낀 점으로 높이 맵 파일을 구하기가 어렵다는 것이었다. 툴에 익숙한 고수들은 Maya나 Max 로 제작한다는 것 같기도 한데, 그 부분은 내가 잘 모르겠어서 스스로 높이 맵 제작 툴을 만들어 보기로 계획했다.

 

우선 목표 기능으로

  1. X by Y 의 격자 생성과,
  2. 격자내의 한 점의 높이를 수정하는 기능,
  3. 마지막으로 해당 높이정보와 계산된 법선벡터를 비트맵으로 저장하는 기능까지다.

 

오늘은 그 첫 번째로 X by Y 격자 생성 부분까지 완료된 내용에 대한 포스팅이다.

결과물부터 보자.

오…단순한UI

마우스 휠을 통하여 카메라의 Z 값을 조절할 수 있다.

 

View 는 이전 포스팅에서 언급한 CD3DX9Wnd 클래스를 약간 수정하여 사용하였다.

(결국은 상속이 답인듯?)

 

이번엔 다른 부분 보다 Terrain의 격자 생성 부분을 보도록 하자.

terrain.h

#pragma once

#include 
#include 
#include 

struct Terrain_Vertex
{
	float	x, y, z;	
	float	nx, ny, nz;
	float	tu, tv;
	enum { FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 };
};


class Terrain
{
public:
	Terrain(void);
	~Terrain(void);

	LPD3DXMESH				GetMesh(){	return m_pTerrainMesh;	};
	void					CreateTerrain(LPDIRECT3DDEVICE9 device, DWORD dwWidth, DWORD dwHeight);

private:
	UINT					m_dwTerrainVertices;
	UINT					m_dwTerrainPrimitives;
	UINT					m_uiTerrainX;
	UINT					m_uiTerrainY;

	LPD3DXMESH				m_pTerrainMesh;
};

terrain.cpp

D3DVERTEXELEMENT9 VERTEX_DECL[] =
{
	{ 0,  0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_POSITION, 0},
	{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_NORMAL,   0},
	{ 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT,  D3DDECLUSAGE_TEXCOORD, 0},
	D3DDECL_END()
};
void	Terrain::CreateTerrain(LPDIRECT3DDEVICE9 device, DWORD dwWidth, DWORD dwHeight)
{
	assert(device != NULL);
	assert(dwWidth != 0);
	assert(dwHeight!= 0);
	//////////////////////////////////////////////////////////////////////////	
	m_uiTerrainX	= dwWidth;
	m_uiTerrainY	= dwHeight;

	m_dwTerrainVertices = (m_uiTerrainX + 1) * (m_uiTerrainY + 1);
	m_dwTerrainPrimitives = m_uiTerrainX * m_uiTerrainY * 2;	
	//////////////////////////////////////////////////////////////////////////
	Terrain_Vertex*		pVertexData = NULL;
	DWORD*				pIndexData	= NULL;
	LPVOID				pData		= NULL;	
	
	//////////////////////////////////////////////////////////////////////////
	pVertexData =  new Terrain_Vertex[m_dwTerrainVertices];

	for(unsigned int y = 0;y < (m_uiTerrainY + 1);++y)
	{
		for(unsigned int x = 0;x < (m_uiTerrainX + 1);++x)
		{
			pVertexData[x + y * (m_uiTerrainX + 1)].x = (float)x - m_uiTerrainX / 2;			
			pVertexData[x + y * (m_uiTerrainX + 1)].y = 0;			
			pVertexData[x + y * (m_uiTerrainX + 1)].z = (float)y - m_uiTerrainY / 2;
			// 노말 정보는 아래에서계산
			pVertexData[x + y * (m_uiTerrainX + 1)].nx = 0.0f;
			pVertexData[x + y * (m_uiTerrainX + 1)].ny = 1.0f;
			pVertexData[x + y * (m_uiTerrainX + 1)].nz = 0.0f;

			pVertexData[x + y * (m_uiTerrainX + 1)].tu = (float)x / (float)m_uiTerrainX;
			pVertexData[x + y * (m_uiTerrainX + 1)].tv = (float)y / (float)m_uiTerrainX;
		}
	}
	//////////////////////////////////////////////////////////////////////////
	pIndexData = new DWORD[m_dwTerrainPrimitives * 3];

	for(unsigned int  y = 0;y < m_uiTerrainY;++y)
	{
		for(unsigned int  x = 0;x < m_uiTerrainX;++x)
		{
			unsigned int index = (x + y * m_uiTerrainY) * 6;

			pIndexData[index + 0] = x + y * (m_uiTerrainX + 1);
			pIndexData[index + 1] = x + 1 + y * (m_uiTerrainX + 1);
			pIndexData[index + 2] = x + 1 + (y + 1) * (m_uiTerrainX + 1);

			pIndexData[index + 3] = x + y * (m_uiTerrainX + 1);
			pIndexData[index + 4] = x + 1 + (y + 1) * (m_uiTerrainX + 1);
			pIndexData[index + 5] = x + (y + 1) * (m_uiTerrainX + 1);
		}
	}

	//////////////////////////////////////////////////////////////////////////
	// Normal Calc : step 1, per face
	D3DXVECTOR3*		pFaceNorm = new D3DXVECTOR3[m_dwTerrainPrimitives];
	ZeroMemory(pFaceNorm, sizeof(D3DXVECTOR3)* m_dwTerrainPrimitives);
	unsigned int idx = 0;
	for ( unsigned int i=0; i< m_dwTerrainPrimitives*3; i+=6)
	{
		D3DXVECTOR3 p1, p2, p3, p4, p5, p6, norm;
		p1.x=pVertexData[pIndexData[i+0]].x;	p1.y=pVertexData[pIndexData[i+0]].y;	p1.z=pVertexData[pIndexData[i+0]].z;
		p2.x=pVertexData[pIndexData[i+1]].x;	p2.y=pVertexData[pIndexData[i+1]].y;	p2.z=pVertexData[pIndexData[i+1]].z;
		p3.x=pVertexData[pIndexData[i+2]].x;	p3.y=pVertexData[pIndexData[i+2]].y;	p3.z=pVertexData[pIndexData[i+2]].z;

		p4.x=pVertexData[pIndexData[i+3]].x;	p4.y=pVertexData[pIndexData[i+3]].y;	p4.z=pVertexData[pIndexData[i+3]].z;
		p5.x=pVertexData[pIndexData[i+4]].x;	p5.y=pVertexData[pIndexData[i+4]].y;	p5.z=pVertexData[pIndexData[i+4]].z;
		p6.x=pVertexData[pIndexData[i+5]].x;	p6.y=pVertexData[pIndexData[i+5]].y;	p6.z=pVertexData[pIndexData[i+5]].z;

		// vector calc
		D3DXVECTOR3 v1 = p2 - p1;
		D3DXVECTOR3 v2 = p2 - p3;
		D3DXVec3Cross(&pFaceNorm[idx], &v1, &v2);
		D3DXVec3Normalize(&pFaceNorm[idx], &pFaceNorm[idx]);
		idx++;

		D3DXVECTOR3 v3 = p6 - p5;
		D3DXVECTOR3 v4 = p6 - p4;
		D3DXVec3Cross(&pFaceNorm[idx], &v3, &v4);
		D3DXVec3Normalize(&pFaceNorm[idx], &pFaceNorm[idx]);
		idx++;
	}
	//////////////////////////////////////////////////////////////////////////
	// Normal Calc : step 2, per vertex
	// 자신(점)을 참조하는 모든 페이스를 (최대 6개) 찾아서 노말을 평균낸다

	for (unsigned int i=0; i< m_dwTerrainVertices; i++)
	{
		D3DXVECTOR3 sum;
		ZeroMemory(&sum, sizeof(D3DXVECTOR3));

		for (unsigned int j=0; j< m_dwTerrainPrimitives * 3; j++)
		{
			if (i == pIndexData[j])
			{
				sum += pFaceNorm[(int)j/3];
			}
		}
		D3DXVec3Normalize(&sum, &sum);
		pVertexData[i].nx = sum.x;
		pVertexData[i].ny = sum.y;
		pVertexData[i].nz = sum.z;
	}
	//////////////////////////////////////////////////////////////////////////

	//////////////////////////////////////////////////////////////////////////
	LPD3DXMESH	pMesh;
	D3DXCreateMesh( m_dwTerrainPrimitives * 3, m_dwTerrainVertices,
					D3DXMESH_MANAGED | D3DXMESH_32BIT, VERTEX_DECL,
					device, &pMesh);

	pMesh->LockVertexBuffer( 0, (void**)&pData);
	memcpy(pData, pVertexData, sizeof(Terrain_Vertex) * m_dwTerrainVertices);
	pMesh->UnlockVertexBuffer();

	pMesh->LockIndexBuffer(0, (void**)&pData );
	memcpy( pData, pIndexData, sizeof(DWORD) * m_dwTerrainPrimitives * 3 );
	pMesh->UnlockIndexBuffer();

	DWORD* pSubset;
	pMesh->LockAttributeBuffer(0, &pSubset);
	memset( pSubset, 0, sizeof(DWORD) * m_dwTerrainPrimitives);
	pMesh->UnlockAttributeBuffer();

	DWORD* aAdjacency = new DWORD[pMesh->GetNumFaces() * 3];
	pMesh->GenerateAdjacency( 1e-6f, aAdjacency );
	pMesh->OptimizeInplace( D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE, aAdjacency, NULL, NULL, NULL );
	delete aAdjacency;

	if (m_pTerrainMesh != NULL)
	{
		m_pTerrainMesh->Release();
		m_pTerrainMesh = NULL;
	}
	m_pTerrainMesh = pMesh;
	//////////////////////////////////////////////////////////////////////////
	// pointer free
	delete[] pVertexData;
	delete[] pIndexData;
	delete[] pFaceNorm;

}


'Dev.Write' 카테고리의 다른 글

My Simple Camera  (0) 2010.02.14
높이 맵 제작 툴 만들기(2)  (0) 2010.01.20
MFC 기반의 Direct3DX Window  (0) 2010.01.15
MFC Custom_Control 연습  (0) 2010.01.15
DX로 만든 첫번째 게임 프로젝트  (0) 2010.01.08