0417 CRTP 배치렌더링 디자인
CRTP써서 배치렌더링 디자인
생각해보니까, 여기 쓰이는 프리미티브 info들은
그냥 렌더링으로도 쓰일 수 있을 것 같다...
배치또는 일반으로...
아닌가? 일반 렌더링에는 월드 행렬을 받아야 한느데??
*** 이거 당연하겠지만 다이나믹 배칭임 ****
나중에 이거 쓰면 이렇게 되겠지...
참고로 이거 vec3하던지 vec2하던지,
(확장성을 위해서 vec3로 받자......제발)
(로테이션도 vec3 또는 쿼터니언으로....)
(물론 변환은 CPU에서!)
BatchRenderer<QuadBatch, Quad> m_quad_batch_renderer{};
BatchRenderer<TextureQuadBatch, TextureQuad> m_tex_quad_batch_renderer{};
BatchRenderer<TriangleBatch, Triangle> m_tri_batch_renderer{};
BatchRenderer<TextureTriangleBatch, TextureTriangle> m_tex_tri_batch_renderer{};
BatchRenderer<LineBatch, Line> m_line_batch_renderer{};
BatchRenderer<PointBatch, Point> m_point_batch_renderer{};
Quad -> vec3 aPos + vec4 aColor;
TextureQuad -> vec3 aPos, vec2 aTexCoord, int aTexIndex
Triangle -> vec3 aPos, vec4 aColor;
TextureTriangle -> vec3 aPos, vec2 aTexCoord, int aTexIndex
Line -> vec3 aPos + vec4 aColor;
Point -> vec3 aPos + vec4 aColor;
이게 의미가..있나??
일단 프리미티브 별로 렌더러 특수화를 해야 하는건 맞음.
그리고 CRTP를 쓸 거면 상위 클래스가 "구현 클래스가 뭘 먹는지" 알아야 함.
그냥 런타임 오버헤드 줄인다는 마인드로,
템플릿 인자 2개를 용서하자.
// ========== 수정 ============== //
용서가 안된다.
inner-using으로 바꾼다.
template<typename T>
class BatchRenderer {
using BatchInfo = T::BatchInfo;
public:
void submit(const BatchInfo& info) {
self().submit(info);
}
}
// ======================= //
template<typename T, typename C>
class BatchRenderer {
private:
auto VAO, VBO, EBO;
BufferData vertices, indices;
public:
T& self() { return static_cast<T&>(*this) }
const T& self() const { return static_cast<const T&>(*this) }
void submit(const C& info) {
self().submit(info);
}
void begin() {
self().begin();
}
void end() {
self().end();
}
// DO ACTUAL DRAW CALL HERE
void flush() {
self().flush();
}
};
struct Primitive {
virtual const char* str() const = 0;
};
// vec2 aPos + vec2 aTexCoord + int aTexIndex
struct TextureQuad : public Primitive {
const char* str() const {return "TextureQuad"}
public:
// 모두 in vec2 aPos로 환원되는 정보들
Vector2f Position;
Vector2f Pivot;
float Rotation;
Vector2f Scale;
Vector2f Skew;
Vector2f Size;
// 매 프레임마다 배칭할 때,
// std::unordered_map<const glw::Texture*, size_t> 를 초기화하고,
// 만약 새로운 텍스쳐가 들어오면 맵에 추가 + 아이디 발급
// 이미 있던 놈이면 그 텍스쳐의 아이디를
// aTexIndex로 보내기.
const glw::Texture* Texture;
}
// vec2 aPos + vec4 aColor
struct Quad final : public Primitive {
const char* str() const { return "Quad" }
public:
// 모두 in vec2 aPos로 환원되는 정보들
Vector2f Position;
Vector2f Pivot;
float Rotation;
Vector2f Scale;
Vector2f Skew;
Vector2f Size;
// in vec4 aColor로 환원되는 정보들 (두번째 속성)
Color4f Color;
Color4f Modulate;
}
class QuadBatch : public BatchRenderer<QuadBatch, Quad> {
public:
void submit(const Quad& info) {
//
}
void begin() {
// 배칭...
}
void end() {
// 배칭...
}
void flush() {
// 렌더콜 부르기..
}
};
// ==================================== //
-- 또는 --
struct Quad {
static const constexpr size_t
static const constexpr size_t IndexCount = 6;
};
이제 이거를 템플릿으로 전달.
BatchRenderer<Quad> m_quad_batch_renderer{};
배치렌더러는 내부에서 T의 오프셋 / 크기 등을 참조하여
배칭을 구성.
렌더서버는 draw_quad가 불리면 명시적으로
m_quad_batch_renderer.submit(pos, size)를 해서 배칭.
렌더서버는 드로우콜 끝나면 모든 배치 렌더러에 대해
flush