[DirectX] 2D Capsule 만들기 (정점 배열, 인덱스 배열)

<DirectXCollision.h>
헤더에서 캡슐형의 바운딩 박스를 지원하지 않아 캡슐을 직접 그려보고 충돌에 사용하기 위해 캡슐을 만드는 MeshGenerator
클래스를 만들고 내부 함수로 캡슐과 박스, 원 및 반원을 그리는 함수들을 만들어야 했다. 이 중 캡슐을 만드는 방법이 필자 입장에서는 특이했으므로 따로 기록해둔다.
이 글에서 정점의 정보는 Position, Normal, Texcoord 를 기준으로 한다.
캡슐을 그리려면 먼저 반원 두개와 직사각형이 필요하다.
반원(Half-Circle)
반원을 그리는 함수는 아래와 같다.
void MeshGenerator::Create2DHalfCircle(float radius, int segmentCount, std::vector<Vertex::Basic32>& vertices, std::vector<UINT>& indices)
{
vertices.push_back({ XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT3(0.0f, 0.0f, -1.0f), XMFLOAT2(0.5f, 0.5f)});
float angleStep = XM_PI / segmentCount;
for (int i = 0; i <= segmentCount; ++i)
{
float angle = i * angleStep;
float x = radius * cosf(angle);
float y = radius * sinf(angle);
vertices.push_back({ XMFLOAT3(x, y, 0.0f),
XMFLOAT3(0.0f, 0.0f, -1.0f),
XMFLOAT2((x / radius) * 0.5f + 0.5f, 1.0f - ((y / radius) * 0.5f + 0.5f)) });
}
for (int i = 1; i <= segmentCount; ++i)
{
indices.push_back(0);
indices.push_back(i + 1);
indices.push_back(i);
}
}
원을 그릴 때에는 angleStep = 2 * XM_PI / segmentCount
지만 반원을 그려야 하기 때문에 XM_PI / segmenetCount
로 그린다.
이 외에는 원을 그릴 때와 동일하다.
캡슐(Capsule)
void MeshGenerator::Create2DCapsule(float width, float height, int segmentCount, std::vector<Vertex::Basic32>& vertices, std::vector<UINT>& indices)
{
float radius = width * 0.5f;
float halfHeight = height * 0.5f;
std::vector<Vertex::Basic32> topCircleVertices;
std::vector<UINT> topCircleIndices;
Create2DHalfCircle(radius, segmentCount, topCircleVertices, topCircleIndices);
// Top half-circle
for (auto& vertex : topCircleVertices)
{
vertex.Position.y += halfHeight;
vertices.push_back(vertex);
}
for (auto& i : topCircleIndices)
{
indices.push_back(i);
}
// Bottom half-circle
for (auto& vertex : topCircleVertices)
{
Vertex::Basic32 newVertex = vertex;
newVertex.Position.y = -newVertex.Position.y;
vertices.push_back(newVertex);
}
for (int i = 1; i <= segmentCount; ++i)
{
indices.push_back(static_cast<UINT>(topCircleVertices.size()));
indices.push_back(static_cast<UINT>(topCircleVertices.size()) + i);
indices.push_back(static_cast<UINT>(topCircleVertices.size()) + i + 1);
}
// Top Triangle
indices.push_back(1 + segmentCount);
indices.push_back(1);
indices.push_back(3 + segmentCount * 2);
// Bottom Triangle
indices.push_back(3 + segmentCount);
indices.push_back(3 + segmentCount * 2);
indices.push_back(1);
}
인자로 캡슐의 너비, 높이, 원의 삼각형 분할 개수, 저장할 정점 배열, 저장할 인덱스 배열을 받는다.
반원을 먼저 만든 다음에 윗 반원을 먼저 배열에 순차적으로 저장한 뒤, 윗 반원 정점의 y 좌표를 반전시켜 아래 반원을 만든다.
아래 반원의 인덱스는 윗 반원 인덱스와는 달리 시계 방향으로 다시 그려야 하기 때문에 (size, size + i, size + i + 1)
순서로 저장한다.
마지막으로 직사각형을 그려야 하는데, 직사각형의 정점은 윗 반원의 시작 정점과 끝 정점 그리고 아래 반원의 시작 정점과 끝 정점을 연결하여 위와 같이 좌삼각형, 우삼각형을 만들어 직사각형을 만들면 된다.
1 = 우상단, 1
21 = 좌상단, 1 + segmentcount
23 = 우하단, 3 + segmentcount
43 = 좌하단, 3 + segmentcount * 2
21 ---- ---1
| / |
| / |
| / |
| / |
43 ------- 23
좌삼각형 21 1 43
우삼각형 23 43 1
직사각형을 그리는 수식은 위 그림을 참고하면 좋을 것 같다.
이렇게 캡슐을 만든 뒤 정점 버퍼와 인덱스 버퍼를 올바르게 생성하면 캡슐 모양의 메쉬가 생성된다.