#include "basicTileMapShader.hpp" #include "BehaviourScripts/Renderable.hpp" #include "BehaviourScripts/TileMap.hpp" #include "Color.hpp" #include "basicTileMapShaderGLSL.hpp" #define SHADER_MESH_INDEX 0 #define SHADER_POS_INDEX 1 #define SHADER_SPRITE_INDEX 2 #define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1) TSE::OpenGL::BasicTileMapShader* TSE::OpenGL::BasicTileMapShader::instance = nullptr; TSE::OpenGL::BasicTileMapShader *TSE::OpenGL::BasicTileMapShader::Instance() { return instance; } void TSE::OpenGL::BasicTileMapShader::Destroy() { if(instance != nullptr) delete instance; instance = nullptr; } void TSE::OpenGL::BasicTileMapShader::Init(float width, float height) { std::vector> parts; parts.push_back(ShaderPart::LoadFromString(vertTile, GL_VERTEX_SHADER)); parts.push_back(ShaderPart::LoadFromString(fragTile, GL_FRAGMENT_SHADER)); instance = new BasicTileMapShader(std::move(parts)); instance->Enable(); int texIDs[] = { 0 }; instance->SetUniform("atlas", 0); instance->Disable(); } TSE::OpenGL::BasicTileMapShader::BasicTileMapShader(std::vector> &&parts) : Shader(parts) { PackageSize = SHADER_PACKAGE_SIZE; } TSE::OpenGL::BasicTileMapShader::~BasicTileMapShader() { if (meshVBO) glDeleteBuffers(1, &meshVBO); if (meshIBO) glDeleteBuffers(1, &meshIBO); } void TSE::OpenGL::BasicTileMapShader::SetMesh(const void *verts, int vertCount, int stride, int floatCountPerVertex, int posOffsetBytes, GLenum primitive, const void *indices, int indexCount, GLenum indexType) { GLint prevVAO = 0, prevArrayBuffer = 0, prevElementBuffer = 0; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &prevVAO); glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prevArrayBuffer); glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &prevElementBuffer); if (!meshVBO) glGenBuffers(1, &meshVBO); glBindBuffer(GL_ARRAY_BUFFER, meshVBO); glBufferData(GL_ARRAY_BUFFER, vertCount * stride, verts, GL_STATIC_DRAW); if (indices && indexCount > 0) { if (!meshIBO) glGenBuffers(1, &meshIBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshIBO); GLsizeiptr idxSize = (indexType == GL_UNSIGNED_INT ? 4 : indexType == GL_UNSIGNED_SHORT? 2 : 1) * indexCount; glBufferData(GL_ELEMENT_ARRAY_BUFFER, idxSize, indices, GL_STATIC_DRAW); meshIndexCount = indexCount; meshIndexType = indexType; } else { // Kein Index-Buffer if (meshIBO) { glDeleteBuffers(1, &meshIBO); meshIBO = 0; } meshIndexCount = 0; } meshVertexCount = vertCount; meshStride = stride; meshPosOffset = posOffsetBytes; meshPosSize = floatCountPerVertex; meshPrimitive = primitive; meshReady = true; glBindBuffer(GL_ARRAY_BUFFER, prevArrayBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prevElementBuffer); glBindVertexArray(prevVAO); } void TSE::OpenGL::BasicTileMapShader::OnEnable() const { if (!meshReady) { // Fallback: unit-Quad als TRIANGLE_FAN (4 Vertices, 2D Positionen) const float quad[8] = { -0.5f,-0.5f, 0.5f,-0.5f, 0.5f,0.5f, -0.5f,0.5f }; const_cast(this)->SetMesh( quad, 4, sizeof(float)*2, 2, 0, GL_TRIANGLE_FAN ); } GLint prevArrayBuffer = 0; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &prevArrayBuffer); glBindBuffer(GL_ARRAY_BUFFER, meshVBO); glEnableVertexAttribArray(SHADER_MESH_INDEX); // LOC_QUAD/pos glVertexAttribPointer(SHADER_MESH_INDEX, meshPosSize, GL_FLOAT, GL_FALSE, meshStride, (void*)meshPosOffset); glVertexAttribDivisor(SHADER_MESH_INDEX, 0); // per-vertex (Mesh) glBindBuffer(GL_ARRAY_BUFFER, prevArrayBuffer); // layout 1: position (vec3) glEnableVertexAttribArray(SHADER_POS_INDEX); glVertexAttribPointer(SHADER_POS_INDEX, 3, GL_FLOAT, GL_FALSE, PackageSize, (void*)0); glVertexAttribDivisor(SHADER_POS_INDEX, 1); // layout 2: spriteindex (float) glEnableVertexAttribArray(SHADER_SPRITE_INDEX); glVertexAttribPointer(SHADER_SPRITE_INDEX, 1, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*3)); glVertexAttribDivisor(SHADER_SPRITE_INDEX, 1); } void TSE::OpenGL::BasicTileMapShader::OnDisable() const { glDisableVertexAttribArray(SHADER_MESH_INDEX); glDisableVertexAttribArray(SHADER_POS_INDEX); glDisableVertexAttribArray(SHADER_SPRITE_INDEX); } void TSE::OpenGL::BasicTileMapShader::OnFlush() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, TextureID); } void TSE::OpenGL::BasicTileMapShader::OnDrawCall(int indexCount) { if (instanceCount <= 0) return; SetUniform("spriteCount", &SpriteCount); SetUniform("spriteScale", &SpriteScale); GLint prevElementBuffer = 0; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &prevElementBuffer); if (meshIBO && meshIndexCount > 0) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshIBO); glDrawElementsInstanced(meshPrimitive, meshIndexCount, meshIndexType, (void*)0, instanceCount); } else { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDrawArraysInstanced(meshPrimitive, 0, meshVertexCount, instanceCount); } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, (GLuint)prevElementBuffer); instanceCount = 0; } void TSE::OpenGL::BasicTileMapShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) { auto* r = dynamic_cast(t.GetBehaviourScript(RENDERABLE)); if (!r) return; auto* tm = dynamic_cast(t.GetBehaviourScript(TILE_MAP)); if (!tm) return; auto tileSet = tm->GetTileSet(); TextureID = tileSet->GetTextueID(); SpriteCount = tileSet->GetCount(); SpriteScale = tm->SpriteScale; const std::vector orderedChunks = *tm->GetChunkPositionsInOrder(); Matrix4x4 matr = t.GetLocalMatrix(); stack.Push(matr); for(auto chunkPos : orderedChunks) { auto chunk = tm->GetChunk(chunkPos); const int spriteCount = chunk->GetSpriteCount(); const std::vector spritePositions = *chunk->GetOrderedPositions(); const std::vector spriteIds = *chunk->GetOrderedSpriteIds(); int chunkSize = chunk->GetChunksize(); for (int i = 0; i < spriteCount; i++) { Matrix4x4 mat = Matrix4x4::ToTranslationMatrix((chunkPos - chunk->nextLine * chunkPos.y) + spritePositions[i]) * Matrix4x4::ToRotationMatrix(Quaternion()) * Matrix4x4::ToScaleMatrix({1,1,1}); stack.Push(mat); Vector3 pos = stack.Top() * Vector3(0,0,0); *target++ = pos.x; *target++ = pos.y; *target++ = pos.z; *target++ = spriteIds[i].x; ++instanceCount; stack.Pop(); if(instanceCount >= 20000) restartDrawcall(rnd); } } stack.Pop(); restartDrawcall(rnd); }