1.05k likes | 1.21k Views
11 장 메쉬 파트 II. 11 장 내용. D3DX 라이브러리가 제공하는 메쉬 관련 인터페이스 , 구조체 , 함수들 Xfile 데이터를 ID3DXMesh 객체로 읽어들이는 방법 프로그레시브 메쉬를 이용하여 얻을 수 있는 이점 및 프로그레시브 메쉬 인터페이스 ID3DXPMesh 이용 방법 경계볼륨의 정체와 용도 및 D3DX 함수. ID3DXBuffer.
E N D
11장 메쉬 파트II 컴퓨터게임(DirectX)
11장 내용 • D3DX 라이브러리가 제공하는 메쉬 관련 인터페이스, 구조체, 함수들 • Xfile 데이터를 ID3DXMesh 객체로 읽어들이는 방법 • 프로그레시브 메쉬를 이용하여 얻을 수 있는 이점 및 프로그레시브 메쉬 인터페이스 ID3DXPMesh 이용 방법 • 경계볼륨의 정체와 용도 및 D3DX 함수 컴퓨터게임(DirectX)
ID3DXBuffer • ID3DXBuffer 인터페이스는 D3DX가 연속적인 메모리 블록에 데이터를 저장하기 위해 이용하는 범용 데이터 구조체로, 다음과 같은 두 개의 메서드를 가짐 • LPVOID GetBufferPointer(); - 데이터의 시작을 가리키는 포인터를 리턴 • DWORD GetBufferSize(); - 버퍼 크기를 바이트 수로 리턴 • 구조체의 범용성을 유지하기 위해 포인터를 한다는 것은 저장되는 데이터의 형을 우리가 관리해야 함을 의미 • 예를 들어, D3DXLoadMeshFromX는 • 메쉬의 인접 정보를 리턴하기 위해 ID3DXBuffer를 이용하는데, • 인접 정보는 DWORD 배열이므로 버퍼의 인접 정보를 이용하기 위해서는 우선 버퍼를 DWORD 배열로 형 변환해야 함 컴퓨터게임(DirectX)
예: • DWORD* info = (DWORD*)adjacencyInfo->GetBufferPointer(); D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); • ID3DXBuffer는 COM객체이므로 이용이 끝난 뒤에는 메모리 누출을 막기 위해 반드시 객체를 풀어주어야 함 • adjacencyInfo->Release(); mtrlBuffer->Release(); • 다음과 같은 함수를 이용하면 빈 ID3DXBuffer를 만들 수 있음 • HRESULT D3DXCreateBuffer( DWORD NumBytes, //버퍼의 크기, 바이트 단위 LPD3DXBUFFER *ppBuffer //생성된 버퍼를 리턴 ); 컴퓨터게임(DirectX)
다음의 예는 네 개의 정수를 보관할 수 있는 버퍼를 만드는 것 • ID3DXBuffer* buffer=0; • D3DXCreateBuffer(4*sizeof(int),&buffer); 컴퓨터게임(DirectX)
Xfile • 3D 물체 데이터를 구성하는 지루한 작업을 도와주기 위해 3D 모델러라 불리는 특수한 애플리케이션들이 개발되었는데, 이 모델러들은 다양한 툴셋과 시각적이고 인터랙티브한 환경을 통해 더욱 쉽고 빠르게 모델링을 수행할 수 있도록 해줌 • 게임 개발에 자주 이용되는 모델러로는 3DS MAX나 LightWave 3D, Maya 등이 있음 • 이 모델러들은 만들어진 메쉬 데이터는 파일로 저장할 수 있으며,따라서 메쉬 데이터를 추출하는 파일 리더를 만든다면 이 데이터를 우리의 3D 애플리케이션에서 이용할 수 있음 컴퓨터게임(DirectX)
Xfile 포맷이라 불리는 특수한 메쉬 파일 포맷은 대부분의 3D 모델러들이 이 포맷으로 저장하는 기능을 가지고 있으며, 기존의 메쉬 파일 포맷을 .X로 변환하는 변환기도 만들어져 있음 • Xfile의 장점은 DirectX가 이 파일 포맷을 정의하고 있다는 것. 이 때문에 Xfile은 별다른 준비 과정 없이 D3DX 라이브러리에서 이용이 가능하며, 라이브러리 자체에서 Xfile을 읽고 저장하는 함수들을 제공 컴퓨터게임(DirectX)
Xfile 읽어들이기 • 다음의 함수를 이용해 Xfile에 보관된 메쉬 데이터를 읽어들일 수 있음 • 이 메서드는 ID3DXMesh 객체를 만들고 여기에 Xfile의 기하정보 데이터를 읽음 • HRESULT D3DXLoadMeshFromX( LPCSTR pFilename, DWORD Options, LPDIRECT3DDEVICE9 pDevice, LPD3DXBUFFER *ppAdjacency, LPD3DXBUFFER *ppMaterials, LPD3DXBUFFER *ppEffectInstances, PDWORD pNumMaterials, LPD3DXMESH *ppMesh); 컴퓨터게임(DirectX)
pFilename –읽어들이고자 하는 Xfile의 파일명 • Options –메쉬를 만드는 데 이용될 하나 이상의 생성 플래그 • D3DXMESH_32BIT –메쉬는 32비트 인덱스를 이용하게 됨 • D3DXMESH_MANAGED –메쉬는 관리 메모리 풀 내에 보관 • D3DXMESH_WRITEONLY –메쉬 데이터에는 쓰기만 허용 • D3DXMESH_DYNAMIC –메쉬 버퍼는 동적으로 만들어짐 컴퓨터게임(DirectX)
pDevice –메쉬와 연계될 장치 • ppAdjacency –메쉬 접근 정보를 위한 DWORD 배열을 포함하는 ID3DXBuffer를 리턴 • ppMaterials –메쉬 재질 정보를 위한 D3DXMATERIAL 구조체 배열을 포함하는 ID3DXBuffer를 리턴 • ppEffectInstances – D3DXEFFECTINSTANCE 구조체 배열을 포함하는 ID3DXBuffer를 리턴. 현재로서는 0을 지정하여 이 인자를 무시 • pNumMaterials –메쉬의 재질 수를 리턴 • ppMesh – Xfile 기하정보로 채워진 ID3DXMesh 객체를 리턴 컴퓨터게임(DirectX)
Xfile 재질 • D3DXLoadMeshFromX 함수의 일곱 번째 인자는 메쉬가 포함하는 재질의 수를 리턴하며 다섯 번째 인자는 재질 데이터를 포함하는 D3DXMATERIAL 구조체 배열을 리턴 • D3DXMATERIAL 구조체는 다음과 같이 정의 typedef struct D3DXMATERIAL { D3DMATERIAL9 MatD3D; LPSTR pTextureFilename; } D3DXMATERIAL; • 이는 비교적 단순한 구조체로, 기본적인 D3DMATERIAL9 구조체와 연결된 텍스처 파일명을 지정하는 null-종료 문자열로의 포인터를 포함 컴퓨터게임(DirectX)
Xfile은 텍스처 데이터를 직접 포함하지 않으며 실제 텍스처 데이터를 포함하는 이미지 파일로의 파일명을 포함. 즉, D3DXLoadMeshFromX를 이용해 Xfile을 일어들인 다음에는 알아낸 텍스처 파일명으로 텍스처 데이터를 읽어들여야 함 • D3DXLoadMeshFromX함수는 리턴된 D3DXMATERIAL 배열의 i번째 항목이 i번째 서브셋과 대응되도록 Xfile 데이터를 읽어들임 컴퓨터게임(DirectX)
예제 애플리케이션: Xfile • 이 예제는 다음과 같은 전역 변수를 이용 • ID3DXMesh* Mesh = 0; std::vector<D3DMATERIAL9> Mtrls(0); std::vector<IDirect3DTexture9*> Textures(0); • 첫 번째 변수인 ID3DXMesh 객체는 Xfile에서 읽어들인 메쉬 데이터를 보관하는데 이용. 두 번째와 세 번째 변수는 각각 재질과 텍스처의 벡터이며, 메쉬의 재질과 텍스처를 보관하는데 이용 컴퓨터게임(DirectX)
예제는 먼저 표준인 Setup 함수를 구현하였는데 여기서 Xfile을 읽어들이는 작업이 이루어짐 • HRESULT hr = 0; // Load the XFile data. ID3DXBuffer* adjBuffer = 0; ID3DXBuffer* mtrlBuffer = 0; DWORD numMtrls = 0; hr = D3DXLoadMeshFromX( "bigship1.x", D3DXMESH_MANAGED, Device, &adjBuffer, &mtrlBuffer, 0, &numMtrls, 컴퓨터게임(DirectX)
&Mesh); if(FAILED(hr)) { ::MessageBox(0, "D3DXLoadMeshFromX() - FAILED", 0, 0); return false; } • 이어 Xfile의 실제 데이터를 읽어들임. 여기에서는 D3DXMATERIAL 배열을 대상으로 반복하여 메쉬가 참조하는 텍스처를 읽어들여야 함 • // Extract the materials, and load textures. if( mtrlBuffer != 0 && numMtrls != 0 ) { 컴퓨터게임(DirectX)
D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(int i = 0; i < numMtrls; i++) { // the MatD3D property doesn't have an ambient value set // when its loaded, so set it now: mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse; // save the ith material Mtrls.push_back( mtrls[i].MatD3D ); // check if the ith material has an associative texture if( mtrls[i].pTextureFilename != 0 ) { // yes, load the texture for the ith subset IDirect3DTexture9* tex = 0; 컴퓨터게임(DirectX)
D3DXCreateTextureFromFile( Device, mtrls[i].pTextureFilename, &tex); // save the loaded texture Textures.push_back( tex ); } else { // no texture for the ith subset Textures.push_back( 0 ); } } } d3d::Release<ID3DXBuffer*>(mtrlBuffer); // done w/ buffer 컴퓨터게임(DirectX)
. .//단원과 관련이 없는 코드는 생략 . return 0; }//Setup() 종료 • Display 함수에서는 매 프레임마다 메쉬를 조금씩 회전하도록 함. 메쉬 내의 서브셋들은 0, 1, 2, …, n-1 형식으로 번호가 매겨져 있으므로 매우 간단한 루프를 이용해 전체 메쉬를 렌더링할 수 있음 • bool Display(float timeDelta) { if( Device ) { static float y = 0.0f; D3DXMATRIX yRot; 컴퓨터게임(DirectX)
D3DXMatrixRotationY(&yRot, y); y += timeDelta; if( y >= 6.28f ) y = 0.0f; D3DXMATRIX World = yRot; Device->SetTransform(D3DTS_WORLD, &World); // Render Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); 컴퓨터게임(DirectX)
for(int i = 0; i < Mtrls.size(); i++) { Device->SetMaterial( &Mtrls[i] ); Device->SetTexture(0, Textures[i]); Mesh->DrawSubset(i); } Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; } 컴퓨터게임(DirectX)
#include "d3dUtility.h" #include <vector> IDirect3DDevice9* Device = 0; const int Width = 640; const int Height = 480; ID3DXMesh* Mesh = 0; std::vector<D3DMATERIAL9> Mtrls(0); std::vector<IDirect3DTexture9*> Textures(0); bool Setup() { HRESULT hr = 0; ID3DXBuffer* adjBuffer = 0; 컴퓨터게임(DirectX)
ID3DXBuffer* mtrlBuffer = 0; DWORD numMtrls = 0; hr = D3DXLoadMeshFromX( "bigship1.x", D3DXMESH_MANAGED, Device, &adjBuffer, &mtrlBuffer, 0, &numMtrls, &Mesh); if(FAILED(hr)) { ::MessageBox(0, "D3DXLoadMeshFromX() - FAILED", 0, 0); return false; } 컴퓨터게임(DirectX)
if( mtrlBuffer != 0 && numMtrls != 0 ) { D3DXMATERIAL* mtrls = (D3DXMATERIAL*)mtrlBuffer->GetBufferPointer(); for(int i = 0; i < numMtrls; i++) { // the MatD3D property doesn't have an ambient value set // when its loaded, so set it now: mtrls[i].MatD3D.Ambient = mtrls[i].MatD3D.Diffuse; // save the ith material Mtrls.push_back( mtrls[i].MatD3D ); // check if the ith material has an associative texture if( mtrls[i].pTextureFilename != 0 ) { 컴퓨터게임(DirectX)
// yes, load the texture for the ith subset IDirect3DTexture9* tex = 0; D3DXCreateTextureFromFile( Device, mtrls[i].pTextureFilename, &tex); // save the loaded texture Textures.push_back( tex ); } else { // no texture for the ith subset Textures.push_back( 0 ); } } } d3d::Release<ID3DXBuffer*>(mtrlBuffer); // done w/ buffer 컴퓨터게임(DirectX)
hr = Mesh->OptimizeInplace( D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_COMPACT | D3DXMESHOPT_VERTEXCACHE, (DWORD*)adjBuffer->GetBufferPointer(), 0, 0, 0); d3d::Release<ID3DXBuffer*>(adjBuffer); // done w/ buffer if(FAILED(hr)) { ::MessageBox(0, "OptimizeInplace() - FAILED", 0, 0); return false; } 컴퓨터게임(DirectX)
Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); // // Set Lights. // D3DXVECTOR3 dir(1.0f, -1.0f, 1.0f); D3DXCOLOR col(1.0f, 1.0f, 1.0f, 1.0f); D3DLIGHT9 light = d3d::InitDirectionalLight(&dir, &col); 컴퓨터게임(DirectX)
Device->SetLight(0, &light); Device->LightEnable(0, true); Device->SetRenderState(D3DRS_NORMALIZENORMALS, true); Device->SetRenderState(D3DRS_SPECULARENABLE, true); // // Set camera. // D3DXVECTOR3 pos(4.0f, 4.0f, -13.0f); D3DXVECTOR3 target(0.0f, 0.0f, 0.0f); D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); 컴퓨터게임(DirectX)
D3DXMATRIX V; D3DXMatrixLookAtLH( &V, &pos, &target, &up); Device->SetTransform(D3DTS_VIEW, &V); D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI * 0.5f, // 90 - degree (float)Width / (float)Height, 1.0f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj); 컴퓨터게임(DirectX)
return true; } void Cleanup() { d3d::Release<ID3DXMesh*>(Mesh); for(int i = 0; i < Textures.size(); i++) d3d::Release<IDirect3DTexture9*>( Textures[i] ); } bool Display(float timeDelta) { if( Device ) { static float y = 0.0f; D3DXMATRIX yRot; 컴퓨터게임(DirectX)
D3DXMatrixRotationY(&yRot, y); y += timeDelta; if( y >= 6.28f ) y = 0.0f; D3DXMATRIX World = yRot; Device->SetTransform(D3DTS_WORLD, &World); Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene(); for(int i = 0; i < Mtrls.size(); i++) { Device->SetMaterial( &Mtrls[i] ); 컴퓨터게임(DirectX)
Device->SetTexture(0, Textures[i]); Mesh->DrawSubset(i); } Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; } LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_DESTROY: ::PostQuitMessage(0); break; 컴퓨터게임(DirectX)
case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); break; } return ::DefWindowProc(hwnd, msg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL, &Device)) { ::MessageBox(0, "InitD3D() - FAILED", 0, 0); 컴퓨터게임(DirectX)
return 0; } if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); return 0; } 컴퓨터게임(DirectX)
버텍스 법선 생성하기 • Xfile에 만약 법선 데이터가 없다면 조명을 위해 직접 버텍스 법선 데이터를 계산해내야 함. 다음과 같은 함수를 이용해 메쉬의 버텍스 법선을 만들어낼 수 있음 • HRESULT D3DXComputeNormals( LPD3DXBASEMESH pMesh, //법선을 계산하려는 메쉬 const DWORD *pAdjacency //입력 인접 정보 ); • 이 함수는 법선 평균을 이용해 버텍스 법선을 계산해냄. 만약 제공된 인접 정보가 있다면 중복된 버텍스를 제거하며, 인접 정보가 제공되지 않는다면 중복된 버텍스도 참조한 면의 평균으로 법선을 가짐 컴퓨터게임(DirectX)
pMesh로 전달할 메쉬는 D3DFVF_NORMAL 플래그를 포함하는 버텍스 포맷을 가져야 함 • 만약 Xfile이 버텍스 법선 데이터를 포함하지 않는다면 D3DXLoadMeshFromX으로 만든 ID3DXMesh객체도 버텍스 포맷 내에 정의된D3DFVF_NORMAL을 가지지 않는다는데 주의. • 따라서 D3DXComputeNormals를 이용하기 전에 먼저 메쉬를 복제하고 복제한 메쉬의 버텍스 포맷을 지정. 다음의 코드는 이 같은 작업 과정을 보여줌 컴퓨터게임(DirectX)
//메쉬가 버텍스 포맷으로 Dd3dfvf_normal를 가지고 있는가? if(!(pMesh->GetFVF() & D3DFVF_NORMAL)) { //아니다. 따라서 새 메쉬를 복제하고 포맷으로 D3DFVF_NORMAL를 추가 ID3DXMesh* pTempMesh = 0; pMesh->CloneMeshFVF( D3DXMESH_MANAGED, pMesh->GetFVF()|D3DFVF_NORMAL,//이곳에 추가 Device, &pTempMesh); //법선을 계산 D3DXComputeNormals(pTempMesh,0); pMesh->Release(); //기존의 메쉬를 제거 pMesh=pTempMesh; //새로운 메쉬를 법선과 함께 저장 } 컴퓨터게임(DirectX)
프로그레시브 메쉬 • ID3DXPMesh 인터페이스에 의해 표현되는 프로그레시브 메쉬는 경계 상실 변환(ECT: edge collapse transformation) 시퀀스를 적용하여 메쉬를 단순화할 수 있도록 함 • 각각의 ECT는 하나의 버텍스와 하나 혹은 두 개의 면을 제거하는데, 각각의 ECT를 되돌릴 수가 있으므로(되돌리는 과정은 버텍스 분할이라 부름) 단순화 과정을 되돌려 원래 상태의 메쉬로 돌아갈 수 있음 • 프로그레시브 메쉬의 작동 원리는 텍스처에서 밉맵을 이용하는 것과 유사. 텍스처링을 수행할 때, 작고 먼 기본형에 복잡한 고해상도의 텍스처를 이용하는 것이 낭비가 된다는 것을 잘 알고 있는데, 메쉬에 있어서도 마찬가지 개념이 적용 • 작고 먼 메쉬는 크고 가까운 메쉬만큼 많은 삼각형이 필요치 않음 • 작은 메쉬에서는 부가적인 삼각형에 의한 세부적인 면이 유실될 것임 • 프로그레시브 메쉬를 이용하는 한 가지 방법은 카메라와의 거리에 따라 메쉬의 LOD를 조정하는 방법이 있는데, 다시 말해서 카메라와의 거리가 감소하면 메쉬의 세부(삼각형)를 더하고 거리가 증가하면 세부를 감소시킴 컴퓨터게임(DirectX)
프로그레시브 메쉬 생성하기 • 다음과 같은 함수를 이용하면 ID3DXPMesh 객체를 만들 수 있음 • HRESULT D3DXGeneratePMesh( LPD3DXMESH pMesh, CONST DWORD *pAdjacency, CONST LPD3DXATTRIBUTEWEIGHTS pVertexAttributeWeights, CONST FLOAT *pVertexWeights, DWORD MinValue, DWORD Options, LPD3DXPMESH *ppPMesh ); 컴퓨터게임(DirectX)
pMesh –프로그레시브 메쉬를 생성하고자 하는 입력 원본 메쉬 • pAdjacency – pMesh의 인접 정보를 포함하는 DWORD 배열로의 포인터 • pVertexAttributeWeights – i번째 배열이 pMesh 내의 i번째 버텍스와 대응되며 속성의 영향력을 지정하는 D3DXATTRIBUTEWEIGHTS 배열(Pmesh->GetNumVertices() 크기)로의 포인터 • pVertexWeight – i번째 항목이 pMesh 내의 i번째 버텍스와 대응되며 pMesh->GetNumVertices() 크기를 가지는 float 배열 • MinValue –단순화의 결과로 얻어질 최소한의 버텍스나 면 • Options – D3DXMESHSIMP 열거형 중 하나를 사용 • D3DXMESHSIMP_VERTEX –앞서의 MinValue 인자가 버텍스에 적용됨을 지정 컴퓨터게임(DirectX)
D3DXMESHSIMP_FACE - 앞서의 MinValue 인자가 면에 적용됨을 지정 • ppMesh –생성된 프로그레시브 메쉬를 리턴 컴퓨터게임(DirectX)
버텍스 속성 영향력 • typedef struct _D3DXATTRIBUTEWEIGHTS { FLOAT Position; FLOAT Boundary; FLOAT Normal; FLOAT Diffuse; FLOAT Specular; FLOAT Texcoord[8]; FLOAT Tangent; FLOAT Binormal; } D3DXATTRIBUTEWEIGHTS; • 버텍스 영향력 구조체는 버텍스 각 요소의 영향력을 지정할 수 있도록 해줌. 0.0 값은 요소가 전혀 영향력을 가지고 있지 않음을 지정하며, 값이 높을수록 버텍스의 영향력이 높아져 단순화 과정에서 제거될 가능성이 낮아짐 컴퓨터게임(DirectX)
디폴트 영향력은 다음과 같음 • D3DXATRIBUTEWIGHTS AttributeWeights; • AttributeWeights.Position = 1.0; • AttributeWeights.Boundary = 1.0; • AttributeWeights.Normal = 1.0; • AttributeWeights.Diffuse = 0.0; • AttributeWeights.Specular = 0.0; • AttributeWeights.Tex[8] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; 컴퓨터게임(DirectX)
ID3DXPMesh 메서드 • ID3DXPMesh 인터페이스는 ID3DXBaseMesh 인터페이스를 상속받음. 따라서 앞서 공부한 ID3DXMesh의 모든 기능을 가지고 있으며, 다음과 같은 추가 메서드들을 포함 • DWORD GetMaxFaces(VOID); - 프로그레시브 메쉬가 가질 수 있는 최대 면 수를 리턴 • DWORD GetMaxVertices(VOID); - 프로그레시브 메쉬가 가질 수 있는 최대 면 수를 리턴 • DWORD GetMinFaces(VOID); - 프로그레시브 메쉬가 가질 수 있는 최소 면 수를 리턴 • DWORD GetMinVertices(VOID); - 프로그레시브 메쉬가 가질 수 있는 최소 버텍스 수를 리턴 • HRESULT SetNumFaces(DWORD faces); - 이 메서드는 단순화/복잡화 하려는 메쉬의 면 수를 지정할 수 있게 해줌. 예를 들어 현재 50면을 가진 메쉬가 있다고 할 때 이를 30면으로 줄이고자 한다면 다음과 같은 코드 라인을 이용 • pmesh->SetNumFaces(30); 컴퓨터게임(DirectX)
HRESULT SetNumVertices(DWORD Vertices); - 이 메서드는 단순화/복잡화하려는 메쉬의 버텍스 수를 지정할 수 있게 해줌. 예를 들어 현재 20개의 버텍스를 가진 메쉬가 있고 버텍스의 수를 40개로 늘리고자 한다면 다음과 같은 코드 라인을 이용 • pmesh->SetNumVertices(40); • HRESULT TrimByFaces(DWORD NewFacesMin, DWORD NewFaceMax, DWORD *rgiFaceRemap, DWORD *rgiVertexRemap); - 이 메서드는 NewFacesMin과 NewFacesMax 인자를 통해 새로운 최소 면 수와 최대 면 수를 지정할 수 있도록 해줌. 새로운 최소/최대 값은 반드시 현재의 최소/최대 면 수 범위에 있어야 함 컴퓨터게임(DirectX)
HRESULT TrimByVertices(DWORD NewVerticesMin, DWORD NewVerticesMax, DWORD *rgiFaceRemap, DWORD *rgiVertRemap); - 이 메서드는 NewVerticesMin과 NewVerticesMax 인자를 통해 새로운 최소 버텍스 수와 최대 버텍스 수를 지정할 수 있도록 해줌. 새로운 최소/최대 값은 반드시 현재의 최소/최대 버텍스 수 범위에 있어야 함 • 가장 중요한 메서드는 메서드의 LOD를 직접 지정할 수 있도록 해주는 SetNumFaces와 SetNumVertices 두 가지임 컴퓨터게임(DirectX)
예제 애플리케이션: 프로그레시브 메쉬 • 프로그레시브 메쉬를 이용해 메쉬를 만들고 렌더링하므로 ID3DXPMesh 인터페이스를 이용해 메쉬를 표현 • 예제에 이용된 전역 변수는 Xfile 예제에 이용된 것과 거의 동일하지만 프로그레시브 메쉬를 보관하기 위한 부가적인 변수를 추가했다는 점이 다름 • ID3DXMesh* SourceMesh = 0; ID3DXPMesh* PMesh = 0; // progressive mesh std::vector<D3DMATERIAL9> Mtrls(0); std::vector<IDirect3DTexture9*> Textures(0); 컴퓨터게임(DirectX)
프로그레시브 메쉬를 만들기 위해서는 프로그레시브 메쉬를 생성할 원본 메쉬 데이터를 전달해야 하는데, • 먼저 Xfile 데이터를 ID3DXMesh객체인 SourceMesh에 로드하고 이를 프로그레시브 메쉬 데이터를 생성해야 함 컴퓨터게임(DirectX)
원본 메쉬를 얻은 뒤에는 다음과 같은 코드를 이용해 프로그레시브 메쉬를 만들어낼 수 있음 • //프로그레시브 메쉬를 생성 hr = D3DXGeneratePMesh( SourceMesh, (DWORD*)adjBuffer->GetBufferPointer(), // 인접 0, // 디폴트 버텍스 속성 영향력 0, // 디폴트 버텍스 영향력 1, // 최대한 단순화 D3DXMESHSIMP_FACE, // 면의 수로 단순화 &PMesh); d3d::Release<ID3DXMesh*>(SourceMesh);//원본메쉬정리 d3d::Release<ID3DXBuffer*>(adjBuffer); //버퍼정리 컴퓨터게임(DirectX)
if(FAILED(hr)) { ::MessageBox(0, "D3DXGeneratePMesh() - FAILED", 0, 0); return false; } • 이 코드는 하나의 면으로 메쉬를 단순화하도록 요청하고 있지만, 버텍스/속성 영향력에 의해 실제로 하나의 면만 남는 일은 일어나지 않음. • 이제 프로그레시브 메쉬가 만들어졌지만 현재 시점에서 이를 렌더링하면 가장 낮은 해상도로 렌더링 됨. 최대 해상도로 메쉬를 렌더링할 것이므로 다음과 같은 코드를 이용해 해상도를 지정 • // 원본 (최대)으로 지정 DWORD maxFaces = PMesh->GetMaxFaces(); PMesh->SetNumFaces(maxFaces); 컴퓨터게임(DirectX)