From 61a7f6f9ba83d8e870479252ad7a6d4c9fc76db8 Mon Sep 17 00:00:00 2001 From: Andreas Ortmann Date: Wed, 28 Sep 2016 21:26:11 +0200 Subject: [PATCH] added working particle engine. needs improvement. --- game/CMakeLists.txt | 3 + game/game.cpp | 6 +- game/renderer_polygon_3d/particle_batch.cpp | 181 ++++++++ game/renderer_polygon_3d/particle_batch.hpp | 44 ++ game/renderer_polygon_3d/polygon_model.hpp | 389 +++++++++--------- .../renderer_polygon_3d.cpp | 27 +- .../renderer_polygon_3d.hpp | 8 +- 7 files changed, 457 insertions(+), 201 deletions(-) create mode 100644 game/renderer_polygon_3d/particle_batch.cpp create mode 100644 game/renderer_polygon_3d/particle_batch.hpp diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index bf6965b..cbb5605 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -13,12 +13,15 @@ set(GAME_SRC renderer.cpp renderer_polygon_2d/renderer_polygon_2d.cpp renderer_polygon_3d/renderer_polygon_3d.cpp + renderer_polygon_3d/polygon_model.cpp + renderer_polygon_3d/particle_batch.cpp renderer_ray_tracer/renderer_ray_tracer.cpp network/session.cpp util.cpp game.cpp + state/object.cpp state/explosion.cpp state/trace.cpp diff --git a/game/game.cpp b/game/game.cpp index bf2c093..b26bf61 100644 --- a/game/game.cpp +++ b/game/game.cpp @@ -6,7 +6,7 @@ #include -#define QUICK_TEST +//#define QUICK_TEST Game::Game() { @@ -44,12 +44,12 @@ bool Game::cycle(float dt) acc += dt; total += dt; - float spawnInterval = 1.0; + float spawnInterval = 0.1; while (acc > spawnInterval) { acc -= spawnInterval; float angle = 360.0 * util::randf_0_1(); - float speed = 0.005; + float speed = 0.01; m_state->players.back()->addCommand(new game::SetSpeedCommand(speed)); m_state->players.back()->addCommand(new game::ShootCommand(angle)); diff --git a/game/renderer_polygon_3d/particle_batch.cpp b/game/renderer_polygon_3d/particle_batch.cpp new file mode 100644 index 0000000..7d0a59d --- /dev/null +++ b/game/renderer_polygon_3d/particle_batch.cpp @@ -0,0 +1,181 @@ +#include "particle_batch.hpp" + +#include + +namespace endofthejedi { + ParticleBatch::ParticleBatch(size_t numParticles, float particleSize) + : m_numParticles(numParticles) + , m_particleRadius(particleSize) + { + std::cout<<"[ParticleBatch] create for " << numParticles << " num particles" << std::endl; + + size_t s = m_numParticles*4; + + m_data_velocity.resize(s); + m_data_position.resize(s); + m_data_kind.resize(s); + m_data_max_age.resize(s); + + std::string vss_particles = + "#version 120\n" + "varying vec3 vertex;\n" + "uniform float time;\n" + "uniform float size;\n" + "uniform vec2 velocity;\n" + "void main()\n" + "{\n" + " vec3 p = size*gl_Vertex.xyz;\n" + //" vec3 p = gl_Vertex.xyz;\n" + " p.xy += velocity*time;\n" + " gl_Position = vec4(p, 1.0);\n" + " vertex = p;\n" + "}\n" + ; + + std::string fss_particles = + "#version 120\n" + "varying vec3 vertex;\n" + "void main()\n" + "{\n" + " gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);\n" + "}\n" + ; + + m_shader.init(); + m_shader.load(vss_particles.c_str(), GL_VERTEX_SHADER); + m_shader.load(fss_particles.c_str(), GL_FRAGMENT_SHADER); + } + + ParticleBatch::~ParticleBatch() + { + // TODO: find out if stuff must be deallocated + } + + void ParticleBatch::setParticle(size_t index, const glm::vec2 &p, const glm::vec2 &v, float kind, float maxAge) + { + if (index >= m_numParticles) { + return; + } + + //std::cout<<"[ParticleBatch] setParticle " << index << std::endl; + + // 4 for use as tri / quad + const static glm::vec2 triangles[4] = { + glm::vec2( 1.0f, -1.0f), + glm::vec2( 1.0f, 1.0f), + glm::vec2(-1.0f, 1.0f), + glm::vec2(-1.0f, -1.0f), + }; + + + for (size_t i=0; i<4; i++) { + const size_t data_index = 4*index+i; + + m_data_position[data_index] = p + triangles[i]; + + m_data_velocity[data_index] = v; + m_data_kind[data_index] = kind; + m_data_max_age[data_index] = maxAge; + } + } + + void ParticleBatch::bind() + { + //std::cout<<"[ParticleBatch] bind" << std::endl; + + for (size_t i=0; i<4; i++) { + //std::cout<<"vbo #" << i << ": " << m_data_vbos[i] << std::endl; + glEnableVertexAttribArray(i); + glBindBuffer(GL_ARRAY_BUFFER, m_data_vbos[i]); + + // TODO: i or index? at first argument? + glVertexAttribPointer( + i, + dataSizeForIndex(i), + GL_FLOAT, // Data is floating point type + GL_FALSE, // No fixed point scaling + 0, // stride: no + NULL); // No offset + } + } + + void ParticleBatch::upload() + { + std::cout<<"[ParticleBatch] upload" << std::endl; + + glGenBuffers(4, m_data_vbos); // Generate buffer + + for (size_t i=0; i<4; i++) { + glEnableVertexAttribArray(i); + glBindBuffer(GL_ARRAY_BUFFER, m_data_vbos[i]); + + // fill buffer with the loaded mesh position data + glBufferData( + GL_ARRAY_BUFFER, // Buffer target + dataSizeForIndex(i) * 4 * m_numParticles * sizeof(float), // Buffer data size + dataSourceForIndex(i), // Buffer data pointer + GL_STATIC_DRAW); // Usage - Data never changes; + } + } + + void ParticleBatch::render() + { + //std::cout<<"[ParticleBatch] render " << std::endl; + + m_shader.bind(); + + static float time = 0.0f; + time += 1.0/50.0; + + glUniform1f(m_shader.location("time"), time); + glUniform1f(m_shader.location("size"), m_particleRadius); + glUniform2f(m_shader.location("velocity"), m_data_velocity[0].x, m_data_velocity[0].y); + + bind(); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4*m_numParticles); + +#if 0 + glBegin(GL_QUADS); + for (size_t index=0; index + +#include + +#include "glclasses.hpp" + +namespace endofthejedi { + class ParticleBatch { + public: + ParticleBatch(size_t numParticles, float particleRadius); + + // deallocate opengl stuff on destroy + ~ParticleBatch(); + + size_t numParticles() const { return m_numParticles; } + + void setParticle(size_t index, const glm::vec2 &p, const glm::vec2 &v, float kind, float maxAge); + + void bind(); + void upload(); + void render(); + + Shader *shader() { return &m_shader; } + + private: + size_t dataSizeForIndex(int i); + void *dataSourceForIndex(int i); + + private: + size_t m_numParticles; + GLuint m_data_vbos[4]; + + std::vector m_data_position; + std::vector m_data_velocity; + std::vector m_data_kind; + std::vector m_data_max_age; + + float m_particleRadius; + + Shader m_shader; + }; +} diff --git a/game/renderer_polygon_3d/polygon_model.hpp b/game/renderer_polygon_3d/polygon_model.hpp index 70e751b..7138026 100644 --- a/game/renderer_polygon_3d/polygon_model.hpp +++ b/game/renderer_polygon_3d/polygon_model.hpp @@ -10,220 +10,221 @@ #include #include -class PolygonModel { - public: - PolygonModel(const std::string &filename) : m_filename(filename) - { - clear(); - } - - void clear() - { - // TODO: delete buffers if there's data - - m_loaded_from_file = false; - m_loaded_to_opengl = false; - - m_data_position.clear(); - m_data_normal.clear(); - - m_vbo_id_position = 0; - m_vbo_id_normal = 0; - m_numVertices = 0; - } - - bool import() - { - clear(); - - // Create an instance of the Importer class - Assimp::Importer importer; - // And have it read the given file with some example postprocessing - // Usually - if speed is not the most important aspect for you - you'll - // propably to request more postprocessing than we do in this example. - const aiScene* scene = importer.ReadFile(m_filename, - aiProcess_CalcTangentSpace | - aiProcess_Triangulate | - aiProcess_JoinIdenticalVertices | - aiProcess_SortByPType); - - // If the import failed, report it - if (!scene) { - std::cout<<"[polygonmodel] loading file " - << m_filename << " failed with: " - << importer.GetErrorString() << std::endl; - - return false; +namespace endofthejedi { + class PolygonModel { + public: + PolygonModel(const std::string &filename) : m_filename(filename) + { + clear(); } - // Now we can access the file's contents. - copyVertices(scene); + void clear() + { + // TODO: delete buffers if there's data - m_loaded_from_file = true; + m_loaded_from_file = false; + m_loaded_to_opengl = false; - // We're done. Everything will be cleaned up by the importer destructor - return true; - } + m_data_position.clear(); + m_data_normal.clear(); - bool uploadToOpenGl() - { - if (!m_loaded_from_file) { - std::cerr<<"[polygonmodel] warning: try to upload model data " - << "to OpenGL but no data is loaded!" << std::endl; - - exit(-1); - return false; + m_vbo_id_position = 0; + m_vbo_id_normal = 0; + m_numVertices = 0; } - glEnableVertexAttribArray(0); + bool import() + { + clear(); - glGenBuffers(1, &m_vbo_id_position); // Generate buffer - glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id_position); // Bind buffer + // Create an instance of the Importer class + Assimp::Importer importer; + // And have it read the given file with some example postprocessing + // Usually - if speed is not the most important aspect for you - you'll + // propably to request more postprocessing than we do in this example. + const aiScene* scene = importer.ReadFile(m_filename, + aiProcess_CalcTangentSpace | + aiProcess_Triangulate | + aiProcess_JoinIdenticalVertices | + aiProcess_SortByPType); - glVertexAttribPointer(0, - 3, // three floats per vertex - GL_FLOAT, // Data is floating point type - GL_FALSE, // No fixed point scaling - 0, // stride: no - NULL); // No offset - - // fill buffer with the loaded mesh position data - glBufferData( - GL_ARRAY_BUFFER, // Buffer target - 3 * m_numVertices * sizeof(float), // Buffer data size - m_data_position.data(), // Buffer data pointer - GL_STATIC_DRAW); // Usage - Data never changes; - - // TODO -#if 0 - // 2 holds normal data - glEnableVertexAttribArray(2); - glGenBuffers(1, &(model->vbo_normal)); // Generate buffer - glBindBuffer(GL_ARRAY_BUFFER, model->vbo_normal); // Bind buffer - - glVertexAttribPointer(2, - 3, // three floats per normal - GL_FLOAT, // Data is floating point type - GL_FALSE, // No fixed point scaling - 0, // stride: no - NULL); // No offset - - // Fill bound buffer - glBufferData( - GL_ARRAY_BUFFER, // Buffer target - 3*model->numVertices*sizeof(float), // Buffer data size - model->normals, // Buffer data pointer - GL_STATIC_DRAW); // Usage - Data never changes; -#endif - m_loaded_to_opengl = true; - - return true; - } - - bool bind() - { - // TODO: check whether this vbo is bound! - if (!m_loaded_to_opengl) { - std::cout<<"[polygonmodel] warning: try to bind model vbo " - << "which was not uploaded to OpenGL!" << std::endl; - - exit(-1); - return false; - } - - // bind position vbo - glEnableVertexAttribArray(0); - glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id_position); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); - - // TODO -#if 0 - // bind normal vbo - GLuint normalLoc = glGetAttribLocation(program, "attr_normal"); - glEnableVertexAttribArray(normalLoc); - glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo_normal); - glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL); -#endif - - m_binding_active = true; - - return true; - } - - bool render() - { - if (!m_binding_active || !m_loaded_to_opengl) { - std::cout<<"[polygonmodel] warning: try to render model without bind()" << std::endl; - exit(-1); - return false; - } - - glDrawArrays(GL_TRIANGLES, 0, m_numVertices); - return true; - } - - private: - bool copyVertices(const aiScene *scene) - { - if (scene->mMeshes == 0) { - std::cout << "[polygonmodel : no meshes loaded for " << m_filename << std::endl; - return false; - } - - aiNode *node = scene->mRootNode; - const aiMesh* mesh = scene->mMeshes[node->mMeshes[0]]; - - // 3 vertices per face, 3 floats per vertex - m_numVertices = mesh->mNumFaces*3; - - m_data_position.reserve(m_numVertices); - m_data_normal.reserve(m_numVertices); - - size_t t, i; - for (t=0; tmNumFaces; ++t) { - const aiFace* face = &mesh->mFaces[t]; - if (face->mNumIndices != 3) { - std::cout << "[polygonmodel] need triangles, got something different with: " - << face->mNumIndices << " vertices" << std::endl; + // If the import failed, report it + if (!scene) { + std::cout<<"[polygonmodel] loading file " + << m_filename << " failed with: " + << importer.GetErrorString() << std::endl; return false; } - for (i=0; imNumIndices; i++) { - const size_t index = face->mIndices[i]; - m_data_position.push_back(mesh->mVertices[index].x); - m_data_position.push_back(mesh->mVertices[index].y); - m_data_position.push_back(mesh->mVertices[index].z); + // Now we can access the file's contents. + copyVertices(scene); - m_data_normal.push_back(mesh->mNormals[index].x); - m_data_normal.push_back(mesh->mNormals[index].y); - m_data_normal.push_back(mesh->mNormals[index].z); - } + m_loaded_from_file = true; + + // We're done. Everything will be cleaned up by the importer destructor + return true; } - size_t totalBytes = 3*m_numVertices*sizeof(float); - std::cout<<"[polygonmodel] loaded file " << m_filename - << " with " << m_numVertices << " vertices (" - << totalBytes << " bytes)" << std::endl; + bool uploadToOpenGl() + { + if (!m_loaded_from_file) { + std::cerr<<"[polygonmodel] warning: try to upload model data " + << "to OpenGL but no data is loaded!" << std::endl; - return true; - } + exit(-1); + return false; + } - const std::string &filename() const { return m_filename; } + glEnableVertexAttribArray(0); - private: - std::string m_filename; - bool m_loaded_from_file; - bool m_loaded_to_opengl; - bool m_binding_active; + glGenBuffers(1, &m_vbo_id_position); // Generate buffer + glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id_position); // Bind buffer - // both will hold 3 * numVertices floats - std::vector m_data_position; - std::vector m_data_normal; + glVertexAttribPointer(0, + 3, // three floats per vertex + GL_FLOAT, // Data is floating point type + GL_FALSE, // No fixed point scaling + 0, // stride: no + NULL); // No offset - size_t m_numVertices; + // fill buffer with the loaded mesh position data + glBufferData( + GL_ARRAY_BUFFER, // Buffer target + 3 * m_numVertices * sizeof(float), // Buffer data size + m_data_position.data(), // Buffer data pointer + GL_STATIC_DRAW); // Usage - Data never changes; - GLuint m_vbo_id_position; - GLuint m_vbo_id_normal; -}; + // TODO +#if 0 + // 2 holds normal data + glEnableVertexAttribArray(2); + glGenBuffers(1, &(model->vbo_normal)); // Generate buffer + glBindBuffer(GL_ARRAY_BUFFER, model->vbo_normal); // Bind buffer + glVertexAttribPointer(2, + 3, // three floats per normal + GL_FLOAT, // Data is floating point type + GL_FALSE, // No fixed point scaling + 0, // stride: no + NULL); // No offset + + // Fill bound buffer + glBufferData( + GL_ARRAY_BUFFER, // Buffer target + 3*model->numVertices*sizeof(float), // Buffer data size + model->normals, // Buffer data pointer + GL_STATIC_DRAW); // Usage - Data never changes; +#endif + m_loaded_to_opengl = true; + + return true; + } + + bool bind() + { + // TODO: check whether this vbo is bound! + if (!m_loaded_to_opengl) { + std::cout<<"[polygonmodel] warning: try to bind model vbo " + << "which was not uploaded to OpenGL!" << std::endl; + + exit(-1); + return false; + } + + // bind position vbo + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id_position); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL); + + // TODO +#if 0 + // bind normal vbo + GLuint normalLoc = glGetAttribLocation(program, "attr_normal"); + glEnableVertexAttribArray(normalLoc); + glBindBuffer(GL_ARRAY_BUFFER, mesh->vbo_normal); + glVertexAttribPointer(normalLoc, 3, GL_FLOAT, GL_FALSE, 0, NULL); +#endif + + m_binding_active = true; + + return true; + } + + bool render() + { + if (!m_binding_active || !m_loaded_to_opengl) { + std::cout<<"[polygonmodel] warning: try to render model without bind()" << std::endl; + exit(-1); + return false; + } + + glDrawArrays(GL_TRIANGLES, 0, m_numVertices); + return true; + } + + private: + bool copyVertices(const aiScene *scene) + { + if (scene->mMeshes == 0) { + std::cout << "[polygonmodel : no meshes loaded for " << m_filename << std::endl; + return false; + } + + aiNode *node = scene->mRootNode; + const aiMesh* mesh = scene->mMeshes[node->mMeshes[0]]; + + // 3 vertices per face, 3 floats per vertex + m_numVertices = mesh->mNumFaces*3; + + m_data_position.reserve(m_numVertices); + m_data_normal.reserve(m_numVertices); + + size_t t, i; + for (t=0; tmNumFaces; ++t) { + const aiFace* face = &mesh->mFaces[t]; + if (face->mNumIndices != 3) { + std::cout << "[polygonmodel] need triangles, got something different with: " + << face->mNumIndices << " vertices" << std::endl; + + return false; + } + + for (i=0; imNumIndices; i++) { + const size_t index = face->mIndices[i]; + m_data_position.push_back(mesh->mVertices[index].x); + m_data_position.push_back(mesh->mVertices[index].y); + m_data_position.push_back(mesh->mVertices[index].z); + + m_data_normal.push_back(mesh->mNormals[index].x); + m_data_normal.push_back(mesh->mNormals[index].y); + m_data_normal.push_back(mesh->mNormals[index].z); + } + } + + size_t totalBytes = 3*m_numVertices*sizeof(float); + std::cout<<"[polygonmodel] loaded file " << m_filename + << " with " << m_numVertices << " vertices (" + << totalBytes << " bytes)" << std::endl; + + return true; + } + + const std::string &filename() const { return m_filename; } + + private: + std::string m_filename; + bool m_loaded_from_file; + bool m_loaded_to_opengl; + bool m_binding_active; + + // both will hold 3 * numVertices floats + std::vector m_data_position; + std::vector m_data_normal; + + size_t m_numVertices; + + GLuint m_vbo_id_position; + GLuint m_vbo_id_normal; + }; +} diff --git a/game/renderer_polygon_3d/renderer_polygon_3d.cpp b/game/renderer_polygon_3d/renderer_polygon_3d.cpp index 839b132..9b63feb 100644 --- a/game/renderer_polygon_3d/renderer_polygon_3d.cpp +++ b/game/renderer_polygon_3d/renderer_polygon_3d.cpp @@ -6,8 +6,6 @@ #include #include -#include "polygon_model.hpp" - namespace endofthejedi { void RendererPolygon3d::setup() { @@ -20,6 +18,21 @@ namespace endofthejedi { addModel("../data/mesh/planet_12.stl", &m_planetModel); addModel("../data/mesh/ship.stl", &m_shipModel); + size_t n = 10; + + m_particles = new ParticleBatch(n, 0.01); + + for (size_t i=0; isetParticle( + i, + glm::vec2(i/(float) n, 0.0), + glm::vec2(1.0, 1.0), + 0.0, + 0.0); + } + + m_particles->upload(); + std::string vss_simple = "#version 120\n" "varying vec3 vertex;\n" @@ -81,6 +94,8 @@ namespace endofthejedi { void RendererPolygon3d::render(const game::State *state) { + glClearColor(0.0, 0.0, 0.0, 1.0); + m_state = state; m_shader.bind(); @@ -91,6 +106,8 @@ namespace endofthejedi { renderShips(); renderMissiles(); + //renderParticles(); + //glColor3f(1.0, 0.0, 0.0); //glBegin(GL_QUADS); //glVertex2f(-1.0f, -1.0f); @@ -100,6 +117,12 @@ namespace endofthejedi { //glEnd(); } + void RendererPolygon3d::renderParticles() + { + //m_particles->bind(); + m_particles->render(); + } + void RendererPolygon3d::renderPlanets() { m_planetModel->bind(); diff --git a/game/renderer_polygon_3d/renderer_polygon_3d.hpp b/game/renderer_polygon_3d/renderer_polygon_3d.hpp index 728c2c1..19cad60 100644 --- a/game/renderer_polygon_3d/renderer_polygon_3d.hpp +++ b/game/renderer_polygon_3d/renderer_polygon_3d.hpp @@ -12,9 +12,10 @@ #include "state/ship.hpp" #include "state/explosion.hpp" -#include +#include "particle_batch.hpp" +#include "polygon_model.hpp" -class PolygonModel; +#include namespace endofthejedi { @@ -27,6 +28,7 @@ namespace endofthejedi { void renderPlanets(); void renderMissiles(); void renderShips(); + void renderParticles(); void addModel(const std::string &filename, PolygonModel **dest); @@ -52,5 +54,7 @@ namespace endofthejedi { // for accessing const game::State *m_state; + + ParticleBatch *m_particles; }; }