프로그래밍 공부
작성일
2023. 10. 19. 15:41
작성자
WDmil
728x90

게임 Object의 바닥을 설정해보자.

 

우선, 계속 반복되는 항목인 Material과 MatrixBuffer를 통합하여 관리하자.


GameObject 정의하기

#pragma once

class GameObject : public Transform
{
public:
	GameObject(wstring shaderfile = L"Tutorial.hlsl");
	~GameObject();

	void SetRender();

	Material* GetMetrial() { return material; }

protected:
	Material* material;
	MatrixBuffer* worldBuffer;
};

같은 Obejct를 만들때 같이 선언되는 부분들은 통합하여도 무방할것이다.

 

위와같이 Material과 worldBuffer를 같이 가지고있는 객체를 생성하자.

#include "Framework.h"

GameObject::GameObject(wstring shaderfile)
{
	material = new Material(shaderfile);
	worldBuffer = new MatrixBuffer();
}

GameObject::~GameObject()
{
	delete material;
	delete worldBuffer;
}

void GameObject::SetRender()
{
	worldBuffer->Set(world);
	worldBuffer->SetVS(0);

	material->Set();
}

여기에서는 그냥 일반적으로 가지게되는 Obejct의 객체들을 따로 가지고와서 생성하고 정의하는 정도로 사용된다.

 

그외에는 가져와서 수정해야할 일이 있는 데이터는 따로 Get을 통해 가져올 수 있도록 정의한다.

 

그 후 만들었던 Object의 상속관계를 변경한다.

class Cube : public GameObject
{
public:
	Cube(Float3 size = { 1, 1, 1}, Vector3 pos = {0, 0, 0});
	~Cube();

	void Render();

	Material* GetMetrial() { return material; }

private:
	Mesh<VertexUV>* mesh;
};

 

GameObject를 상속받아 처리하게 됨으로 전에 부모객체에서 생성된 데이터는 따로 다시 정의해주지 않아도 될것이다.

 

즉, 상속관계 상 Transform -> GameObject -> Cube 순서로 생성될것이다.

 

#include "Framework.h"

Cube::Cube(Float3 size, Vector3 pos)
{
    mesh = new Mesh<VertexUV>();

    SetLocalPosition(pos);
    Float3 halfSize(size.x * 0.5, size.y * 0.5, size.z * 0.5);

    vector<VertexUV>& vertices = mesh->GetVertices();

    //Front
    vertices.emplace_back(-halfSize.x, -halfSize.y, -halfSize.z, 0, 1);
    vertices.emplace_back(-halfSize.x, +halfSize.y, -halfSize.z, 0, 0);
    vertices.emplace_back(+halfSize.x, -halfSize.y, -halfSize.z, 1, 1);
    vertices.emplace_back(+halfSize.x, +halfSize.y, -halfSize.z, 1, 0);

    //Up
    vertices.emplace_back(-halfSize.x, +halfSize.y, -halfSize.z, 0, 1);
    vertices.emplace_back(-halfSize.x, +halfSize.y, +halfSize.z, 0, 0);
    vertices.emplace_back(+halfSize.x, +halfSize.y, -halfSize.z, 1, 1);
    vertices.emplace_back(+halfSize.x, +halfSize.y, +halfSize.z, 1, 0);

    //Right
    vertices.emplace_back(+halfSize.x, +halfSize.y, -halfSize.z, 0, 1);
    vertices.emplace_back(+halfSize.x, +halfSize.y, +halfSize.z, 0, 0);
    vertices.emplace_back(+halfSize.x, -halfSize.y, -halfSize.z, 1, 1);
    vertices.emplace_back(+halfSize.x, -halfSize.y, +halfSize.z, 1, 0);

    //Left
    vertices.emplace_back(-halfSize.x, +halfSize.y, -halfSize.z, 0, 1);
    vertices.emplace_back(-halfSize.x, -halfSize.y, -halfSize.z, 0, 0);
    vertices.emplace_back(-halfSize.x, +halfSize.y, +halfSize.z, 1, 1);
    vertices.emplace_back(-halfSize.x, -halfSize.y, +halfSize.z, 1, 0);

    //Back
    vertices.emplace_back(-halfSize.x, +halfSize.y, +halfSize.z, 0, 1);
    vertices.emplace_back(-halfSize.x, -halfSize.y, +halfSize.z, 0, 0);
    vertices.emplace_back(+halfSize.x, +halfSize.y, +halfSize.z, 1, 1);
    vertices.emplace_back(+halfSize.x, -halfSize.y, +halfSize.z, 1, 0);

    //Down
    vertices.emplace_back(-halfSize.x, -halfSize.y, -halfSize.z, 0, 1);
    vertices.emplace_back(+halfSize.x, -halfSize.y, -halfSize.z, 0, 0);
    vertices.emplace_back(-halfSize.x, -halfSize.y, +halfSize.z, 1, 1);
    vertices.emplace_back(+halfSize.x, -halfSize.y, +halfSize.z, 1, 0);

    vector<UINT>& indices = mesh->GetIndices();

    /*
        1        3
        ----------
        1        1
        1        1
        1        1
        ----------
        0        2
    */

    indices =
    {
        //Front
        0, 1, 2, 2, 1, 3,
        //Up
        4, 5, 6, 6, 5, 7,
        //Right
        8, 9, 10, 10, 9, 11,
        //Left
        12, 13, 14, 14, 13, 15,
        //Back
        16, 17, 18, 18, 17, 19,
        //Down
        20, 21, 22, 22, 21, 23
    };

    
    mesh->CreateMesh();
}

Cube::~Cube()
{
    delete mesh;
}

void Cube::Render()
{
    SetRender();
    mesh->Draw();
}

그럼으로 코드 상 소멸자를 따로 정의하지 않아도 부모Class에서 이미 소멸자가 호출됨으로 따로 이 객체에서 소멸시키지 않아도 된다.

 

위 코드를 이용해서 Terrain을 생성해보자.


Terrain(지역)

#pragma once

class Terrain : public GameObject
{
private:
	typedef VertexUV VertexType;
public:
	Terrain();
	~Terrain();

	void Render();

private:
	void MakeMesh();

private:
	UINT width = 10, height = 10;
	Mesh<VertexType>* mesh;

	Texture* heightMap;
};

이미 다른 Matrix들은 GameObject에서 생성하여 사용함으로. 필요한 데이터만 기입하도록 한다.

 

Render()함수와 Mesh를 생성하기 위한 MakeMesh()함수.

 

그리고 가로 세로값, 지형의 높이를 받아오기위해. Texture타입의 heightMap을 가져온다.

#include "Framework.h"

Terrain::Terrain()
{
	material->SetDiffuseMap(L"Textures/Landscape/Dirt2.png");

	heightMap = Texture::Add(L"Textures/HeightMaps/HeightMap.png");
	mesh = new Mesh<VertexType>();
	MakeMesh();
	mesh->CreateMesh();
}

Terrain::~Terrain()
{
	delete mesh;
}

void Terrain::Render()
{
	SetRender();

	mesh->Draw();
}

void Terrain::MakeMesh()
{
	width = heightMap->GetSize().x;
	height = heightMap->GetSize().x;

	vector<Float4> pixels;
	heightMap->ReadPixels(pixels);

	vector<VertexType>& vertices = mesh->GetVertices();

	vertices.reserve(width * height);
	for (UINT z = 0; z < height; z++) {
		for (UINT x = 0; x < width; x++) {
			VertexType vertex;
			vertex.pos = { (float)x, 0.0f, (float)z };
			vertex.uv.x = x / (float)(width - 1);
			vertex.uv.y = z / (float)(height - 1);

			UINT index = width * z + x;
			vertex.pos.y = pixels[index].x * 20.0f;

			vertices.push_back(vertex);
		}
	}

	// Indices
	vector<UINT>& indices = mesh->GetIndices();

	indices.reserve((width - 1) * (height - 1) * 6);

	for (UINT z = 0; z < height - 1; z++) {
		for (UINT x = 0; x < width - 1; x++) {
			indices.push_back(width * z + x);	// 0
			indices.push_back(width * (z + 1) + x);	// 1
			indices.push_back(width * z + x + 1);	// 2

			indices.push_back(width * z + x + 1);	// 2
			indices.push_back(width * (z + 1) + x);	// 1
			indices.push_back(width * (z + 1) + x + 1);	// 3
		}
	}

}

cpp파일에서는 생성자에서

material과 heightMap, mesh를 생성하고 정의한다.

 

소멸자에서는 여기서 생성한 mesh를 소멸시켜주고,

 

Render에서는 부모class에서 정읳나 SetRender()를 호출, mesh에서 Draw해준다.

 

MakeMesh()에서는 

 

heightmap기준으로 사이즈를 정의하고.

 

heightmap기준으로 pixels를 가져온뒤에.

 

Vectex를 사용할 vector의 사이즈를 초기화시켜 사용한다.

 

이중for문으로 가로세로를 돌려서 각 사각형 기준으로 pos와 uv좌표를 잡아서 지정해준다.

 

지정해준뒤에 pixels의 x값.Float4임으로 Red값만큼 y를 위로 올려준다.

변형된 vertice를 vertices에 push_back 해준다.

 

indices에서는

생성된 mesh값의 indices를 정의해준다.

 

각 사각형 한개마다 indices가 6개임으로 전체 사각형 개수에 6을 곱해준다.

 

이중for문으로 indices를 삽입해주는데,

각 사각형의 시계방향 회전을 위해서 vertices의 순서를 정의해준다.

결과


View_State

DIrectX에서는 SamplerStateRasterizerState가 존재한다.

 

각각 View의 상태를 결정해주는 Sate변환 함수인데, 저 지형을 F1을 눌렀을때 외곽선만 보이게 만들어보자.

 

class Environment : public Singleton<Environment>
{
private:
    friend class Singleton;

    Environment();
    ~Environment();

public:
    void Update();
    void Set();

    void SetViewport(UINT width = WIN_WIDTH, UINT height = WIN_HEIGHT);
    void SetPerspective();

    void CreateProjection();
    void CreateState();
private:
    MatrixBuffer* viewBuffer;
    MatrixBuffer* projectionBuffer;

    SamplerState* samplerState;
    RasterizerState* rasterizerSate[2];

    bool isWireMode = false;
};

전에 만들었던 View를 담당하는 Environment에 isWireMode와 RasterizerState를 정의한다.

void Environment::Update()
{
	if(KEY->Down(VK_F1))
		isWireMode = !isWireMode;
}

void Environment::Set()
{
	rasterizerSate[isWireMode]->SetState();
}

그리고, F1이 Down되면 isWireMode를 false면 true true면 false로 변환해준다.

그 후, rasterizerState에서

void RasterizerState::SetState()
{
    DC->RSSetState(state);
}

현재 state를 정의하여 실행해주면 된다.

F1을 눌렀을 때의 모습

 

728x90