Cześć! Chciałbym móc przycinać np. kwadraty, które wychodzą z jednej klasy sprite batch. Próbowałem zrobić to w ten sposób:
struct VertexData
{
glm::vec2 position = glm::vec2(0.0f);
glm::vec4 color = glm::vec4(0.0f);
};
struct DrawCmd
{
glm::vec4 clipRect = glm::vec4(0.0f);
GLuint elemCount = 0;
};
class SpriteBatch
{
public:
SpriteBatch();
void AddDrawCmd();
void UpdateClipRect();
void PushClipRect(glm::vec4 clip);
void PopClipRect();
void Prepare();
void Draw(glm::vec2 pos, glm::vec2 size, glm::vec4 color);
void Flush();
GLuint vao, vbo, ebo;
std::vector<VertexData> vertex;
std::vector<GLuint> element;
std::vector<glm::vec4> clipRect;
std::vector<DrawCmd> drawCmd; // 1 komenda = 1 polecenie rysowania GPU
};
SpriteBatch::SpriteBatch()
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (const void*)offsetof(VertexData, position));
glVertexAttribPointer(1, 4, GL_FLOAT, GL_TRUE, sizeof(VertexData), (const void*)offsetof(VertexData, color));
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
}
void SpriteBatch::AddDrawCmd()
{
DrawCmd draw;
draw.clipRect = clipRect.size() ? clipRect.back() : glm::vec4(0.0f, 0.0f, 1280.0f, 720.0f);
drawCmd.push_back(draw);
}
void SpriteBatch::UpdateClipRect()
{
const glm::vec4 currentClip = clipRect.size() ? clipRect.back() : glm::vec4(0.0f, 0.0f, 1280.0f, 720.0f);
DrawCmd* currentDraw = drawCmd.size() > 0 ? &drawCmd.back() : nullptr;
// 1. Jeśli nie ma żadnego polecenia rysowania
// 2. liczba elementów nie jest równa 0
// 1. Musimy dodać polecenie rysowania żeby nie było błędu związanego z dostaniem się do tego polecenia
// 2. Musimy dodać polecenie rysowania żeby używać nowego clip rect
if (!currentDraw || currentDraw->elemCount != 0) {
AddDrawCmd();
return;
}
DrawCmd* prevDraw = drawCmd.size() > 1 ? currentDraw - 1 : nullptr;
// Jeśli liczba elementów jest 0, to znaczy nie mamy co rysować, usuwamy polecenie rysowania, bo nie używamy go
// Musimy sprawdzić czy jeśli usuniemy polecenie rysowania to czy nie będzie błędu, bo nie chcemy usuwać polecenia jeśli jest tylko jedno
if (currentDraw->elemCount == 0 && prevDraw) {
drawCmd.pop_back();
} else { currentDraw->clipRect = currentClip; }
}
void SpriteBatch::PushClipRect(glm::vec4 clip)
{
clipRect.push_back(clip);
UpdateClipRect();
}
void SpriteBatch::PopClipRect()
{
clipRect.pop_back();
UpdateClipRect();
}
void SpriteBatch::Prepare()
{
vertex.clear();
element.clear();
clipRect.clear();
drawCmd.clear();
}
void SpriteBatch::Draw(glm::vec2 pos, glm::vec2 size, glm::vec4 color)
{
GLuint index = vertex.size();
VertexData vertexData;
vertexData.position = glm::vec2(pos.x, pos.y);
vertexData.color = color;
vertex.push_back(vertexData);
vertexData.position = glm::vec2(pos.x, pos.y + size.y);
vertexData.color = color;
vertex.push_back(vertexData);
vertexData.position = glm::vec2(pos.x + size.x, pos.y + size.y);
vertexData.color = color;
vertex.push_back(vertexData);
vertexData.position = glm::vec2(pos.x + size.x, pos.y);
vertexData.color = color;
vertex.push_back(vertexData);
element.push_back(index);
element.push_back(index + 1);
element.push_back(index + 2);
element.push_back(index + 2);
element.push_back(index + 3);
element.push_back(index);
DrawCmd& draw = drawCmd.back();
draw.elemCount += element.size();
}
void SpriteBatch::Flush()
{
shader.UseProgram();
glEnable(GL_SCISSOR_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
const int windowW = 1280;
const int windowH = 720;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertex.size() * sizeof(VertexData), vertex.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, element.size() * sizeof(GLuint), element.data(), GL_DYNAMIC_DRAW);
for (GLuint i = 0; i < drawCmd.size(); ++i) {
const DrawCmd drawCall = drawCmd[i];
glm::vec4 clip = drawCall.clipRect;
if (clip.x < windowW && clip.y < windowH && clip.z >= 0.0f && clip.w >= 0.0f) {
glScissor(static_cast<int>(clip.x), static_cast<int>(windowH - clip.w), static_cast<int>(clip.z - clip.x), static_cast<int>(clip.w - clip.y));
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, drawCall.elemCount, GL_UNSIGNED_INT, nullptr);
glBindVertexArray(0);
}
}
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
}
int main()
{
OpenWindow("Name", 1280.0f, 720.0f);
SpriteBatch sb;
bool done = false;
while(!done)
{
glClear(GL_COLOR_BUFFER_BIT);
sb.Prepare();
sb.PushClipRect(glm::vec4(0.0f, 150.0f, 1280.0f, 720.0f));
sb.Draw(glm::vec2(100.0f, 100.0f), glm::vec2(100.0f, 100.0f), glm::vec4(0.0f, 255.0f, 0.0f, 255.0f));
sb.PopClipRect();
sb.PushClipRect(glm::vec4(150.0f, 300.0f, 1280.0f, 720.0f));
sb.Draw(glm::vec2(100.0f, 250.0f), glm::vec2(100.0f, 100.0f), glm::vec4(0.0f, 255.0f, 0.0f, 255.0f));
sb.PopClipRect();
tr.Flush();
SwapBuffers();
}
}
Chciałbym żeby pierwszy kwadrat był przycięty o 50 jednostek z góry i rzeczywiście tak się dzieje,
następnie chciałbym żeby drugi kwadrat był przycięty z lewej strony, więc dodaje nowe polecenie rysowania, nowy clip rect i powinno działać...
No niekoniecznie :/
Na jeden i drugi kwadrat oddziałuje pierwszy clip rect glm::vec4(0.0f, 150.0f, 1280.0f, 720.0f)
. Prostym inputem z klawiatury zmieniając pozycję Y drugiego kwadratu sprawdziłem, to, że pierwszy clip rect też oddziałuje na drugi.
Dlaczego tylko pierwszy clip rect jest w działaniu?
W jaki sposób mogę to poprawić?