#include "basicParticleShader.hpp" #include "basicParticleShaderGLSL.hpp" #include "BehaviourScripts/Renderable.hpp" #include "BehaviourScripts/ParticleSystem.hpp" #include "Color.hpp" #define SHADER_MESH_INDEX 0 #define SHADER_POS_INDEX 1 #define SHADER_SIZE_INDEX 2 #define SHADER_ROT_INDEX 3 #define SHADER_COLOR_INDEX 4 #define SHADER_PACKAGE_SIZE sizeof(float) * (3 + 1 + 1 + 4) TSE::OpenGL::BasicParticleShader* TSE::OpenGL::BasicParticleShader::instance = nullptr; TSE::OpenGL::BasicParticleShader *TSE::OpenGL::BasicParticleShader::Instance() { return instance; } void TSE::OpenGL::BasicParticleShader::Destroy() { if(instance != nullptr) delete instance; instance = nullptr; } void TSE::OpenGL::BasicParticleShader::Init(float width, float height) { std::vector> parts; parts.push_back(ShaderPart::LoadFromString(vertPart, GL_VERTEX_SHADER)); parts.push_back(ShaderPart::LoadFromString(fragPart, GL_FRAGMENT_SHADER)); instance = new BasicParticleShader(std::move(parts)); } TSE::OpenGL::BasicParticleShader::BasicParticleShader(std::vector> &&parts) : Shader(parts) { PackageSize = SHADER_PACKAGE_SIZE; } TSE::OpenGL::BasicParticleShader::~BasicParticleShader() { if (meshVBO) glDeleteBuffers(1, &meshVBO); if (meshIBO) glDeleteBuffers(1, &meshIBO); } void TSE::OpenGL::BasicParticleShader::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::BasicParticleShader::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: size (float) glEnableVertexAttribArray(SHADER_SIZE_INDEX); glVertexAttribPointer(SHADER_SIZE_INDEX, 1, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*3)); glVertexAttribDivisor(SHADER_SIZE_INDEX, 1); // layout 3: rotationRad (float) glEnableVertexAttribArray(SHADER_ROT_INDEX); glVertexAttribPointer(SHADER_ROT_INDEX, 1, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*4)); glVertexAttribDivisor(SHADER_ROT_INDEX, 1); // layout 4: color (vec4) glEnableVertexAttribArray(SHADER_COLOR_INDEX); glVertexAttribPointer(SHADER_COLOR_INDEX, 4, GL_FLOAT, GL_FALSE, PackageSize, (void*)(sizeof(float)*5)); glVertexAttribDivisor(SHADER_COLOR_INDEX, 1); } void TSE::OpenGL::BasicParticleShader::OnDisable() const { glDisableVertexAttribArray(SHADER_MESH_INDEX); glDisableVertexAttribArray(SHADER_POS_INDEX); glDisableVertexAttribArray(SHADER_SIZE_INDEX); glDisableVertexAttribArray(SHADER_ROT_INDEX); glDisableVertexAttribArray(SHADER_COLOR_INDEX); } void TSE::OpenGL::BasicParticleShader::OnFlush() { } void TSE::OpenGL::BasicParticleShader::OnDrawCall(int indexCount) { if (instanceCount <= 0) return; 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::BasicParticleShader::OnSubmit(const Transformable &t, float *&target, TransformationStack &stack, void (*restartDrawcall)(IRenderer &), IRenderer &rnd) { auto* r = dynamic_cast(t.GetBehaviourScript(RENDERABLE)); if (!r) return; auto* ps = dynamic_cast(t.GetBehaviourScript(PARTICLESYSTEM)); if (!ps) return; const std::vector& particles = ps->GetParticles(); for(auto particle : particles) { *target++ = particle->position.x; *target++ = particle->position.y; *target++ = particle->position.z; *target++ = particle->size; *target++ = particle->rotationRad; *target++ = particle->color.r; *target++ = particle->color.g; *target++ = particle->color.b; *target++ = particle->color.a; ++instanceCount; } }