#ifndef PARTICLE_CLASS #define PARTICLE_CLASS #include #include #include #include #include "common.h" #include "Engine.h" #include "Object.h" #include "Mesh.h" #include "QuadVertices.h" #include "t.h" #include "VAO.h" #include "VBO.h" #include "EBO.h" #include "shaderClass.h" #include "Vertex.h" #include "texture.h" #include "Camera.h" #include "RenderSystem.h" class Particle : public t_package { public: Particle() { } void Render() { } void Step(float dt) { t.RotateByEulerAnglesCumulate(angularvelocity * dt); t.TranslateBy(linearvelocity*dt); } glm::vec3 linearvelocity; glm::vec3 angularvelocity; float lifespan; private: }; class ParticleEmitter : public Renderable, public t_package, public Object { public: enum EmitDirection { Perpendicular, Aligned, Outward }; EmitDirection emitdirection = Outward; //PARTICLE SETTINGS glm::vec3 size = {1.0f,1.0f,1.0f}; glm::vec3 emitangle = {0.0f,0.0f,0.0f}; float lifespan = 5.0f; float speed = 1.0f; glm::vec3 angularvelocity = {0.0f,0.0f,0.0f}; glm::vec3 gravity = {0.0f,0.0f,0.0f}; // m/s^2 every particle speeds up by, like blood dripping bool facecamera = false; bool upright = false; // with facecamera, only turns around the world's up to face it, so tall particles stay vertical glm::vec4 color = {1.0f,1.0f,1.0f,1.0f}; public: std::vector particles; SurfaceTexture surface; Mesh* mesh; unsigned int instanceBuffer = 0; ParticleEmitter() { shadertype = ParticleShader; mesh = new Mesh(quadVertices3D, quadIndices3D); } ParticleEmitter(const std::vector& meshvertices, const std::vector& meshindices) { shadertype = ParticleShader; mesh = new Mesh(meshvertices, meshindices); } ~ParticleEmitter() { if (instanceBuffer != 0) glDeleteBuffers(1, &instanceBuffer); mesh->Delete(); } void Emit() { Particle* np = new Particle(); glm::quat rot = glm::quat(glm::vec3({ (float)rand() / (float)RAND_MAX * emitangle[0] - emitangle[0] / 2.0f,(float)rand() / (float)RAND_MAX * emitangle[1] - emitangle[1] / 2.0f,(float)rand() / RAND_MAX * emitangle[2] - emitangle[2] / 2.0f })); np->t.TranslateTo(t.GetTranslation()); np->linearvelocity = (t.GetFrontVector() * speed) * rot; np->angularvelocity = angularvelocity; np->t.ScaleTo(size); if (emitdirection == Perpendicular) { np->t.RotateToQuaternion(glm::conjugate(rot) * glm::quat({ glm::radians(90.0f),0.0f,0.0f }) * t.GetRotationQuaternion()); } else if (emitdirection == Outward) { np->t.RotateToQuaternion(glm::conjugate(rot) * t.GetRotationQuaternion()); } else { np->t.RotateToQuaternion(t.GetRotationQuaternion()); } np->lifespan = lifespan; particles.push_back(np); } void Step(float dt) { for (auto it = particles.begin(); it != particles.end(); ) { Particle* p = *it; p->lifespan -= dt; if (p->lifespan <= 0.0f) { delete p; it = particles.erase(it); } else { p->linearvelocity += gravity * dt; p->Step(dt); ++it; } } } glm::mat4 GetParticleMatrix(Particle& particle) { if (!facecamera) return particle.t.GetMatrix(); if (upright) { // Turned around y so the quad's front faces the camera, its up staying the world's const glm::vec3 toCamera = engine->camera->t.GetTranslation() - particle.t.GetTranslation(); const float yaw = std::atan2(toCamera.x, toCamera.z); return particle.t.GetTranslationMatrix() * glm::rotate(glm::mat4(1.0f), yaw, glm::vec3(0.0f, 1.0f, 0.0f)) * particle.t.GetScaleMatrix(); } return particle.t.GetTranslationMatrix() * engine->camera->t.GetRotationMatrix() * particle.t.GetScaleMatrix(); } void Render(Shader& ShaderProgram) override { if (particles.size() <= 0) return; ShaderProgram.Activate(); mesh->vao.Bind(); ShaderProgram.Set4F("color",color); surface.Apply(ShaderProgram, 0, size); std::vector matrices; if (color.w != 1.0f) { std::map sorted; for (unsigned int i = 0; i < particles.size(); i++) { sorted[Magnitude2(engine->camera->t.GetTranslation() - particles[i]->t.GetTranslation())] = particles[i]; } for (std::map::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it) { matrices.emplace_back(GetParticleMatrix(*it->second)); } } else { for (int i = 0; i < particles.size(); i++) { matrices.emplace_back(GetParticleMatrix(*particles[i])); } } if (instanceBuffer == 0) glGenBuffers(1, &instanceBuffer); glBindBuffer(GL_ARRAY_BUFFER, instanceBuffer); glBufferData(GL_ARRAY_BUFFER, particles.size() * sizeof(glm::mat4), &matrices[0], GL_STATIC_DRAW); mesh->vao.Bind(); // vertex attributes std::size_t vec4Size = sizeof(glm::vec4); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)0); glEnableVertexAttribArray(4); glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(1 * vec4Size)); glEnableVertexAttribArray(5); glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(2 * vec4Size)); glEnableVertexAttribArray(6); glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, 4 * vec4Size, (void*)(3 * vec4Size)); glVertexAttribDivisor(3, 1); glVertexAttribDivisor(4, 1); glVertexAttribDivisor(5, 1); glVertexAttribDivisor(6, 1); glDrawElementsInstanced(GL_TRIANGLES, mesh->indices.size(), GL_UNSIGNED_INT, 0, particles.size()); mesh->vao.Unbind(); } private: }; #endif