Advertisement
Guest User

Untitled

a guest
Jan 25th, 2020
105
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.67 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. #include <vector>
  5. #include <algorithm>
  6.  
  7. #include <GL/glew.h>
  8.  
  9. #include <GLFW/glfw3.h>
  10. GLFWwindow* window;
  11.  
  12. #include <glm/glm.hpp>
  13. #include <glm/gtc/matrix_transform.hpp>
  14. #include <glm/gtx/norm.hpp>
  15. using namespace glm;
  16.  
  17.  
  18. #include <common/shader.hpp>
  19. #include <common/texture.hpp>
  20. #include <common/controls.hpp>
  21.  
  22. // CPU representation of a particle
  23. struct Particle{
  24.     glm::vec3 pos, speed;
  25.     unsigned char r,g,b,a; // Color
  26.     float size, angle, weight;
  27.     float life; // Remaining life of the particle. if <0 : dead and unused.
  28.     float cameradistance; // *Squared* distance to the camera. if dead : -1.0f
  29.  
  30.     bool operator<(const Particle& that) const {
  31.         // Sort in reverse order : far particles drawn first.
  32.         return this->cameradistance > that.cameradistance;
  33.     }
  34. };
  35.  
  36. const int MaxParticles = 100000;
  37. Particle ParticlesContainer[MaxParticles];
  38. int LastUsedParticle = 0;
  39.  
  40. // Finds a Particle in ParticlesContainer which isn't used yet.
  41. // (i.e. life < 0);
  42. int FindUnusedParticle(){
  43.  
  44.     for(int i=LastUsedParticle; i<MaxParticles; i++){
  45.         if (ParticlesContainer[i].life < 0){
  46.             LastUsedParticle = i;
  47.             return i;
  48.         }
  49.     }
  50.  
  51.     for(int i=0; i<LastUsedParticle; i++){
  52.         if (ParticlesContainer[i].life < 0){
  53.             LastUsedParticle = i;
  54.             return i;
  55.         }
  56.     }
  57.  
  58.     return 0; // All particles are taken, override the first one
  59. }
  60.  
  61. void SortParticles(){
  62.     std::sort(&ParticlesContainer[0], &ParticlesContainer[MaxParticles]);
  63. }
  64.  
  65. int main( void )
  66. {
  67.     // Initialise GLFW
  68.     if( !glfwInit() )
  69.     {
  70.         fprintf( stderr, "Failed to initialize GLFW\n" );
  71.         getchar();
  72.         return -1;
  73.     }
  74.  
  75.     glfwWindowHint(GLFW_SAMPLES, 4);
  76.     glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);
  77.     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
  78.     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
  79.     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
  80.     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
  81.  
  82.     // Open a window and create its OpenGL context
  83.     window = glfwCreateWindow( 1024, 768, "Tutorial 18 - Particules", NULL, NULL);
  84.     if( window == NULL ){
  85.         fprintf( stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n" );
  86.         getchar();
  87.         glfwTerminate();
  88.         return -1;
  89.     }
  90.     glfwMakeContextCurrent(window);
  91.  
  92.     // Initialize GLEW
  93.     glewExperimental = true; // Needed for core profile
  94.     if (glewInit() != GLEW_OK) {
  95.         fprintf(stderr, "Failed to initialize GLEW\n");
  96.         getchar();
  97.         glfwTerminate();
  98.         return -1;
  99.     }
  100.  
  101.     // Ensure we can capture the escape key being pressed below
  102.     glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
  103.     // Hide the mouse and enable unlimited mouvement
  104.     glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
  105.    
  106.     // Set the mouse at the center of the screen
  107.     glfwPollEvents();
  108.     glfwSetCursorPos(window, 1024/2, 768/2);
  109.  
  110.     // Dark blue background
  111.     glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
  112.  
  113.     // Enable depth test
  114.     glEnable(GL_DEPTH_TEST);
  115.     // Accept fragment if it closer to the camera than the former one
  116.     glDepthFunc(GL_LESS);
  117.  
  118.     GLuint VertexArrayID;
  119.     glGenVertexArrays(1, &VertexArrayID);
  120.     glBindVertexArray(VertexArrayID);
  121.  
  122.  
  123.     // Create and compile our GLSL program from the shaders
  124.     GLuint programID = LoadShaders( "Particle.vertexshader", "Particle.fragmentshader" );
  125.  
  126.     // Vertex shader
  127.     GLuint CameraRight_worldspace_ID  = glGetUniformLocation(programID, "CameraRight_worldspace");
  128.     GLuint CameraUp_worldspace_ID  = glGetUniformLocation(programID, "CameraUp_worldspace");
  129.     GLuint ViewProjMatrixID = glGetUniformLocation(programID, "VP");
  130.  
  131.     // fragment shader
  132.     GLuint TextureID  = glGetUniformLocation(programID, "myTextureSampler");
  133.  
  134.    
  135.     static GLfloat* g_particule_position_size_data = new GLfloat[MaxParticles * 4];
  136.     static GLubyte* g_particule_color_data         = new GLubyte[MaxParticles * 4];
  137.  
  138.     for(int i=0; i<MaxParticles; i++){
  139.         ParticlesContainer[i].life = -1.0f;
  140.         ParticlesContainer[i].cameradistance = -1.0f;
  141.     }
  142.  
  143.  
  144.  
  145.     GLuint Texture = loadDDS("particle.DDS");
  146.  
  147.     // The VBO containing the 4 vertices of the particles.
  148.     // Thanks to instancing, they will be shared by all particles.
  149.     static const GLfloat g_vertex_buffer_data[] = {
  150.          -0.5f, -0.5f, 0.0f,
  151.           0.5f, -0.5f, 0.0f,
  152.          -0.5f,  0.5f, 0.0f,
  153.           0.5f,  0.5f, 0.0f,
  154.     };
  155.     GLuint billboard_vertex_buffer;
  156.     glGenBuffers(1, &billboard_vertex_buffer);
  157.     glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
  158.     glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
  159.  
  160.     // The VBO containing the positions and sizes of the particles
  161.     GLuint particles_position_buffer;
  162.     glGenBuffers(1, &particles_position_buffer);
  163.     glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
  164.     // Initialize with empty (NULL) buffer : it will be updated later, each frame.
  165.     glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
  166.  
  167.     // The VBO containing the colors of the particles
  168.     GLuint particles_color_buffer;
  169.     glGenBuffers(1, &particles_color_buffer);
  170.     glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
  171.     // Initialize with empty (NULL) buffer : it will be updated later, each frame.
  172.     glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLubyte), NULL, GL_STREAM_DRAW);
  173.  
  174.  
  175.    
  176.     double lastTime = glfwGetTime();
  177.     do
  178.     {
  179.         // Clear the screen
  180.         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  181.  
  182.         double currentTime = glfwGetTime();
  183.         double delta = currentTime - lastTime;
  184.         lastTime = currentTime;
  185.  
  186.  
  187.         computeMatricesFromInputs();
  188.         glm::mat4 ProjectionMatrix = getProjectionMatrix();
  189.         glm::mat4 ViewMatrix = getViewMatrix();
  190.  
  191.         // We will need the camera's position in order to sort the particles
  192.         // w.r.t the camera's distance.
  193.         // There should be a getCameraPosition() function in common/controls.cpp,
  194.         // but this works too.
  195.         glm::vec3 CameraPosition(glm::inverse(ViewMatrix)[3]);
  196.  
  197.         glm::mat4 ViewProjectionMatrix = ProjectionMatrix * ViewMatrix;
  198.  
  199.  
  200.         // Generate 10 new particule each millisecond,
  201.         // but limit this to 16 ms (60 fps), or if you have 1 long frame (1sec),
  202.         // newparticles will be huge and the next frame even longer.
  203.         int newparticles = (int)(delta*10000.0);
  204.         if (newparticles > (int)(0.016f*10000.0))
  205.             newparticles = (int)(0.016f*10000.0);
  206.        
  207.         for(int i=0; i<newparticles; i++){
  208.             int particleIndex = FindUnusedParticle();
  209.             ParticlesContainer[particleIndex].life = 5.0f; // This particle will live 5 seconds.
  210.             ParticlesContainer[particleIndex].pos = glm::vec3(0,0,-20.0f);
  211.  
  212.             float spread = 1.5f;
  213.             glm::vec3 maindir = glm::vec3(0.0f, 10.0f, 0.0f);
  214.             // Very bad way to generate a random direction;
  215.             // See for instance http://stackoverflow.com/questions/5408276/python-uniform-spherical-distribution instead,
  216.             // combined with some user-controlled parameters (main direction, spread, etc)
  217.             glm::vec3 randomdir = glm::vec3(
  218.                 (rand()%2000 - 1000.0f)/1000.0f,
  219.                 (rand()%2000 - 1000.0f)/1000.0f,
  220.                 (rand()%2000 - 1000.0f)/1000.0f
  221.             );
  222.            
  223.             ParticlesContainer[particleIndex].speed = maindir + randomdir*spread;
  224.  
  225.  
  226.             // Very bad way to generate a random color
  227.             ParticlesContainer[particleIndex].r = rand() % 256;
  228.             ParticlesContainer[particleIndex].g = rand() % 256;
  229.             ParticlesContainer[particleIndex].b = rand() % 256;
  230.             ParticlesContainer[particleIndex].a = (rand() % 256) / 3;
  231.  
  232.             ParticlesContainer[particleIndex].size = (rand()%1000)/2000.0f + 0.1f;
  233.            
  234.         }
  235.  
  236.  
  237.  
  238.         // Simulate all particles
  239.         int ParticlesCount = 0;
  240.         for(int i=0; i<MaxParticles; i++){
  241.  
  242.             Particle& p = ParticlesContainer[i]; // shortcut
  243.  
  244.             if(p.life > 0.0f){
  245.  
  246.                 // Decrease life
  247.                 p.life -= delta;
  248.                 if (p.life > 0.0f){
  249.  
  250.                     // Simulate simple physics : gravity only, no collisions
  251.                     p.speed += glm::vec3(0.0f,-9.81f, 0.0f) * (float)delta * 0.5f;
  252.                     p.pos += p.speed * (float)delta;
  253.                     p.cameradistance = glm::length2( p.pos - CameraPosition );
  254.                     //ParticlesContainer[i].pos += glm::vec3(0.0f,10.0f, 0.0f) * (float)delta;
  255.  
  256.                     // Fill the GPU buffer
  257.                     g_particule_position_size_data[4*ParticlesCount+0] = p.pos.x;
  258.                     g_particule_position_size_data[4*ParticlesCount+1] = p.pos.y;
  259.                     g_particule_position_size_data[4*ParticlesCount+2] = p.pos.z;
  260.                                                    
  261.                     g_particule_position_size_data[4*ParticlesCount+3] = p.size;
  262.                                                    
  263.                     g_particule_color_data[4*ParticlesCount+0] = p.r;
  264.                     g_particule_color_data[4*ParticlesCount+1] = p.g;
  265.                     g_particule_color_data[4*ParticlesCount+2] = p.b;
  266.                     g_particule_color_data[4*ParticlesCount+3] = p.a;
  267.  
  268.                 }else{
  269.                     // Particles that just died will be put at the end of the buffer in SortParticles();
  270.                     p.cameradistance = -1.0f;
  271.                 }
  272.  
  273.                 ParticlesCount++;
  274.  
  275.             }
  276.         }
  277.  
  278.         SortParticles();
  279.  
  280.  
  281.         //printf("%d ",ParticlesCount);
  282.  
  283.  
  284.         // Update the buffers that OpenGL uses for rendering.
  285.         // There are much more sophisticated means to stream data from the CPU to the GPU,
  286.         // but this is outside the scope of this tutorial.
  287.         // http://www.opengl.org/wiki/Buffer_Object_Streaming
  288.  
  289.  
  290.         glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
  291.         glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW); // Buffer orphaning, a common way to improve streaming perf. See above link for details.
  292.         glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLfloat) * 4, g_particule_position_size_data);
  293.  
  294.         glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
  295.         glBufferData(GL_ARRAY_BUFFER, MaxParticles * 4 * sizeof(GLubyte), NULL, GL_STREAM_DRAW); // Buffer orphaning, a common way to improve streaming perf. See above link for details.
  296.         glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLubyte) * 4, g_particule_color_data);
  297.  
  298.  
  299.         glEnable(GL_BLEND);
  300.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  301.  
  302.         // Use our shader
  303.         glUseProgram(programID);
  304.  
  305.         // Bind our texture in Texture Unit 0
  306.         glActiveTexture(GL_TEXTURE0);
  307.         glBindTexture(GL_TEXTURE_2D, Texture);
  308.         // Set our "myTextureSampler" sampler to use Texture Unit 0
  309.         glUniform1i(TextureID, 0);
  310.  
  311.         // Same as the billboards tutorial
  312.         glUniform3f(CameraRight_worldspace_ID, ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]);
  313.         glUniform3f(CameraUp_worldspace_ID   , ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]);
  314.  
  315.         glUniformMatrix4fv(ViewProjMatrixID, 1, GL_FALSE, &ViewProjectionMatrix[0][0]);
  316.  
  317.         // 1rst attribute buffer : vertices
  318.         glEnableVertexAttribArray(0);
  319.         glBindBuffer(GL_ARRAY_BUFFER, billboard_vertex_buffer);
  320.         glVertexAttribPointer(
  321.             0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
  322.             3,                  // size
  323.             GL_FLOAT,           // type
  324.             GL_FALSE,           // normalized?
  325.             0,                  // stride
  326.             (void*)0            // array buffer offset
  327.         );
  328.        
  329.         // 2nd attribute buffer : positions of particles' centers
  330.         glEnableVertexAttribArray(1);
  331.         glBindBuffer(GL_ARRAY_BUFFER, particles_position_buffer);
  332.         glVertexAttribPointer(
  333.             1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
  334.             4,                                // size : x + y + z + size => 4
  335.             GL_FLOAT,                         // type
  336.             GL_FALSE,                         // normalized?
  337.             0,                                // stride
  338.             (void*)0                          // array buffer offset
  339.         );
  340.  
  341.         // 3rd attribute buffer : particles' colors
  342.         glEnableVertexAttribArray(2);
  343.         glBindBuffer(GL_ARRAY_BUFFER, particles_color_buffer);
  344.         glVertexAttribPointer(
  345.             2,                                // attribute. No particular reason for 1, but must match the layout in the shader.
  346.             4,                                // size : r + g + b + a => 4
  347.             GL_UNSIGNED_BYTE,                 // type
  348.             GL_TRUE,                          // normalized?    *** YES, this means that the unsigned char[4] will be accessible with a vec4 (floats) in the shader ***
  349.             0,                                // stride
  350.             (void*)0                          // array buffer offset
  351.         );
  352.  
  353.         // These functions are specific to glDrawArrays*Instanced*.
  354.         // The first parameter is the attribute buffer we're talking about.
  355.         // The second parameter is the "rate at which generic vertex attributes advance when rendering multiple instances"
  356.         // http://www.opengl.org/sdk/docs/man/xhtml/glVertexAttribDivisor.xml
  357.         glVertexAttribDivisor(0, 0); // particles vertices : always reuse the same 4 vertices -> 0
  358.         glVertexAttribDivisor(1, 1); // positions : one per quad (its center)                 -> 1
  359.         glVertexAttribDivisor(2, 1); // color : one per quad                                  -> 1
  360.  
  361.         // Draw the particules !
  362.         // This draws many times a small triangle_strip (which looks like a quad).
  363.         // This is equivalent to :
  364.         // for(i in ParticlesCount) : glDrawArrays(GL_TRIANGLE_STRIP, 0, 4),
  365.         // but faster.
  366.         glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount);
  367.  
  368.         glDisableVertexAttribArray(0);
  369.         glDisableVertexAttribArray(1);
  370.         glDisableVertexAttribArray(2);
  371.  
  372.         // Swap buffers
  373.         glfwSwapBuffers(window);
  374.         glfwPollEvents();
  375.  
  376.     } // Check if the ESC key was pressed or the window was closed
  377.     while( glfwGetKey(window, GLFW_KEY_ESCAPE ) != GLFW_PRESS &&
  378.            glfwWindowShouldClose(window) == 0 );
  379.  
  380.  
  381.     delete[] g_particule_position_size_data;
  382.  
  383.     // Cleanup VBO and shader
  384.     glDeleteBuffers(1, &particles_color_buffer);
  385.     glDeleteBuffers(1, &particles_position_buffer);
  386.     glDeleteBuffers(1, &billboard_vertex_buffer);
  387.     glDeleteProgram(programID);
  388.     glDeleteTextures(1, &Texture);
  389.     glDeleteVertexArrays(1, &VertexArrayID);
  390.    
  391.  
  392.     // Close OpenGL window and terminate GLFW
  393.     glfwTerminate();
  394.  
  395.     return 0;
  396. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement