모델데이터를 불러와보자
X파일은 다각형의 정점데이터나, 면의 데이터 등을 정리해둔 파일
이제는 모델데이터를 코드로 만드는게 아니라, X파일로부터 불러들이는 작업을 할 것이다
다각형이 복잡하다면 통상 X파일을 읽어서 하는게 일반적인데... (2011.02.19)
- X파일 외에도 Obj, 3ds, stl 등 다양한 포맷이 사용됩니다 (2014.06.09)
using System;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Windows.Forms;
using System.Drawing;
namespace MDXSample
{
/// <summary>
/// 메인 샘플 클래스
/// </summary>
public partial class MainSample : IDisposable
{
/// <summary>
/// 정점 버퍼
/// </summary>
private ExtendedMaterial[] _materials = null;
private Mesh _mesh = null;
private Texture[] _textures = null;
public bool InitializeApplication(MainForm topLevelForm)
{
// 폼의 참조를 보관 유지
this._form = topLevelForm;
PresentParameters pp = new PresentParameters();
pp.Windowed = true;
pp.SwapEffect = SwapEffect.Discard;
pp.EnableAutoDepthStencil = true;
pp.AutoDepthStencilFormat = DepthFormat.D16;
topLevelForm.MouseMove += new MouseEventHandler(this.form_MouseMove);
try
{
// Direct3D 디바이스 작성
this.CreateDevice(topLevelForm, pp);
// 폰트의 작성
this.CreateFont();
}
catch (DirectXException ex)
{
// 예외 발생
MessageBox.Show(ex.ToString(), "에러", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
try
{
this._mesh = Mesh.FromFile("01.x", MeshFlags.Managed, this._device, out this._materials);
}
catch (DirectXException ex)
{
MessageBox.Show(ex.ToString(), "에러", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
if ((this._mesh.VertexFormat & VertexFormats.Normal) == 0)
{
Mesh tempMesh = this._mesh.Clone(this._mesh.Options.Value, this._mesh.VertexFormat | VertexFormats.Normal, this._device);
tempMesh.ComputeNormals();
this._mesh.Dispose();
this._mesh = tempMesh;
}
if (this._materials.Length >= 1)
{
this._textures = new Texture[this._materials.Length];
for (int i = 0; i < this._materials.Length; i++)
{
this._textures[i] = null;
if (this._materials[i].TextureFilename != null && this._materials[i].TextureFilename.Length >= 1)
{
try
{
this._textures[i] = TextureLoader.FromFile(this._device, this._materials[i].TextureFilename);
}
catch (DirectXException ex)
{
MessageBox.Show(ex.ToString(), "에러", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
}
}
this._device.Lights[0].Type = LightType.Directional;
this._device.Lights[0].Direction = new Vector3(1.0f, -1.5f, 2.0f);
this._device.Lights[0].Diffuse = Color.White;
this._device.Lights[0].Ambient = Color.FromArgb(255, 128, 128, 128);
this._device.Lights[0].Enabled = true;
this._device.Lights[0].Update();
return true;
}
/// <summary>
/// 메인 루프 처리
/// </summary>
public void MainLoop()
{
this.SettingCamera();
// 화면을 단색(파랑색)으로 클리어
this._device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkBlue, 1.0f, 0);
// 「BeginScene」와「EndScene」의 사이에 화면에 출력할 내용을 코딩
this._device.BeginScene();
for (int i = 0; i < this._materials.Length; i++)
{
this._device.SetTexture(0, this._textures[i]);
this._device.Material = this._materials[i].Material3D;
this._mesh.DrawSubset(i);
}
this._font.DrawText(null, "θ:" + this._lensPosTheta, 0, 0, Color.White);
this._font.DrawText(null, "φ:" + this._lensPosPhi, 0, 12, Color.White);
this._font.DrawText(null, "마우스 위치:" + this._oldMousePoint, 0, 24, Color.White);
// 화면출력은 여기까지
this._device.EndScene();
// 실제의 디스플레이에 출력
this._device.Present();
}
private void form_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
this._lensPosTheta -= e.Location.X - this._oldMousePoint.X;
this._lensPosPhi += e.Location.Y - this._oldMousePoint.Y;
}
this._oldMousePoint = e.Location;
}
/// <summary>
/// 자원의 파기
/// </summary>
public void Dispose()
{
if (this._textures != null)
{
foreach (Texture i in this._textures)
{
if (i != null)
{
i.Dispose();
}
}
}
if (this._mesh != null)
{
this._mesh.Dispose();
}
// 폰트 자원을 해제
if (this._font != null)
{
this._font.Dispose();
}
// Direct3D 디바이스의 자원 해제
if (this._device != null)
{
this._device.Dispose();
}
}
}
}
private ExtendedMaterial[] _materials = null;
확장메테리얼로, 메테리얼과 별 차이가 없지만, 추가적으로 파일이름이라는 속성이 들어갔다,, 텍스쳐(재질감)를 읽어들일 때 사용되구 텍스쳐를 읽어들인 이후에는 필요없다, 그래서 메모리의 낭비를 피하구싶을땐 이걸 안쓰기도한다
private Mesh _mesh = null;
정점버퍼, 인덱스버퍼, 면정보, 속성테이블등의 복수의 데이터를 정리한 클래스, 우리가 지금까지 박스를 만들때는 정점버퍼랑 인덱스버퍼를 써서 했는데, 이걸 한군데다가 요약한게 바로 이 매쉬
private Texture[] _textures = null;
텍스쳐랑 확장메테리얼이 배열의 형태를 취하고 있는 이유는, X파일에는 복수의 텍스쳐와 메테리얼을 가지고 있는 경우가 많아서 그렇다
try
{
this._mesh = Mesh.FromFile("01.x", MeshFlags.Managed, this._device, out this._materials);
}
catch (DirectXException ex)
{
MessageBox.Show(ex.ToString(), "에러", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
X파일로부터 매쉬 데이터를 읽어들인다
Mesh.FromFile 메서드
filename : 읽어들일 X파일 명, 절대경로나 현재디렉토리로부터 상대경로, bin-debug
options : 매쉬의 작성 옵션, 별거없으면 Mesh.FlagsManaged
device : Direct3D 디바이스
materials : 메테리얼 데이터를 포함한 배열을 받는다, 요고를 써서 텍스쳐를 읽어들인다
if ((this._mesh.VertexFormat & VertexFormats.Normal) == 0)
{
Mesh tempMesh = this._mesh.Clone(this._mesh.Options.Value, this._mesh.VertexFormat | VertexFormats.Normal, this._device);
tempMesh.ComputeNormals();
this._mesh.Dispose();
this._mesh = tempMesh;
}
간혹 X파일에는 위치나 UV좌표같은 데이터는 있는데, 법선데이터가 없는 경우도 있다,
그래서 여기서 법선데이터가 없을 경우 법선데이터를 추가시켜주는 것
이걸 왜 하냐면, 법선이 없는 X파일을 읽어서 라이트를 비추면 법선정보가 없어서 색이 이상하게 나올 수도 있다
정점의 사이즈가 변화하게 되면, 법선에 덧붙인 메쉬를 복제해야한다,
왜 복제해야하냐면, 정점의 사이즈가 변화되어버리면 무조건 복사를 해야돼, 안그러면 안된다
Mesh.Clone 메서드
option : 메쉬의 작성 옵션, 복제원의 메쉬 옵션을 건네준다
vertexFormat : 복제원의 메쉬의 정점 포맷에 법선데이터를 덧붙인다.
device : Direct3D 디바이스
tempMesh.ComputeNormals(); 메서드를 통해 복제된 메쉬의 법선데이터를 자동으로 설정해 준다.
if (this._materials.Length >= 1) // 속성의 수를 확인해 읽어들일지 결정한다
{
this._textures = new Texture[this._materials.Length];
for (int i = 0; i < this._materials.Length; i++)
{
this._textures[i] = null;
if (this._materials[i].TextureFilename != null && this._materials[i].TextureFilename.Length >= 1) // 메테리얼이 없으면 재질감(텍스쳐)도 없기 때문에 읽지 않는다.
{
try
{
this._textures[i] = TextureLoader.FromFile(this._device, this._materials[i].TextureFilename); // 그 다음 텍스쳐를 읽어들인다
}
catch (DirectXException ex)
{
MessageBox.Show(ex.ToString(), "에러", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
}
}
메쉬로 사용할 텍스쳐를 읽어들일거야, 속성의 수를 확인해 읽어들일지 결정한다
메테리얼이 없으면 텍스쳐도 없기 때문에 읽지 않는다.
속성의 수라기 보다 메테리얼과 텍스쳐의 수 라고 생각하면 된다
속성테이블의 엔트리 수
그 다음 텍스쳐를 읽어들인다
for (int i = 0; i < this._materials.Length; i++)
{
this._device.SetTexture(0, this._textures[i]);
this._device.Material = this._materials[i].Material3D;
this._mesh.DrawSubset(i);
}
읽기가 끝나면 이제 출력을 해야지요?
메쉬의 수 만큼 루프를 돌려서 다각형을 그린다, 왜 한번에 그리지 않느냐
속성의 인덱스에 따라 사용하는 텍스쳐가 다르기때문에 인덱스가 바뀌면 다시 세트해야하는 경우가 있기 때문,
그래서 for루프를 돌면서 계속 텍스쳐와 메테리얼을 읽고있는 것이다
SetTexture 메서드로 제2 인수에 Null을 주게 되면 텍스쳐를 안쓰는거랑 똑같다. 첫번째 인수는 0인데, 일단 지금은 0으로
마지막에는 DrawSubset으로 그리고있다 이 안에는 정점버퍼, 인덱스버퍼 다있기 때문에 그래서 코드가 간결해진 것
그래서 결국은
'Graphics > DirectX' 카테고리의 다른 글
Direct X - 17. 키보드와 마우스를 이용한 모델 이동 (0) | 2014.06.09 |
---|---|
Direct X - 16. X, Y, Z 좌표 그려넣기 (0) | 2014.06.09 |
Direct X - 14. 조명효과 사용하기 (0) | 2014.06.09 |
Direct X - 13. IndexBuffer를 이용한 상자 만들기 (0) | 2014.06.09 |
Direct X - 12. 마우스로 카메라 시점 움직이기 (0) | 2014.06.09 |