void GLFWCALL PhysicsThreadFun( void *arg ) { while( running ) { // Lock mutex glfwLockMutex( thread_sync.particles_lock ); // Wait for particle drawing to be done while( running && thread_sync.p_frame > thread_sync.d_frame ) { glfwWaitCond( thread_sync.d_done, thread_sync.particles_lock, 0.1 ); } // No longer running? if( !running ) { break; } // Update particles ParticleEngine( thread_sync.t, thread_sync.dt ); // Update frame counter thread_sync.p_frame ++; // Unlock mutex and signal drawing thread glfwUnlockMutex( thread_sync.particles_lock ); glfwSignalCond( thread_sync.p_done ); } }
ParticleEngine ParticleEngineBuilder::Build(){ return ParticleEngine(gBuilder,rMinBuilder,blackHoleMassBuilder,blackHoleRadiusBuilder,disappearingRadiusBuilder,minSpawnRadiusBuilder,maxSpawnRadiusBuilder,spawnVelocityBuilder,maxZSpawnDistanceBuilder,numberOfThreadsBuilder,numberOfParticlesBuilder,collisionsBuilder,integratorBuilder, gravityCutOffBuilder);}
void DrawParticles( double t, float dt ) { int i, particle_count; VERTEX vertex_array[ BATCH_PARTICLES * PARTICLE_VERTS ], *vptr; float alpha; GLuint rgba; VEC quad_lower_left, quad_lower_right; GLfloat mat[ 16 ]; PARTICLE *pptr; // Here comes the real trick with flat single primitive objects (s.c. // "billboards"): We must rotate the textured primitive so that it // always faces the viewer (is coplanar with the view-plane). // We: // 1) Create the primitive around origo (0,0,0) // 2) Rotate it so that it is coplanar with the view plane // 3) Translate it according to the particle position // Note that 1) and 2) is the same for all particles (done only once). // Get modelview matrix. We will only use the upper left 3x3 part of // the matrix, which represents the rotation. glGetFloatv( GL_MODELVIEW_MATRIX, mat ); // 1) & 2) We do it in one swift step: // Although not obvious, the following six lines represent two matrix/ // vector multiplications. The matrix is the inverse 3x3 rotation // matrix (i.e. the transpose of the same matrix), and the two vectors // represent the lower left corner of the quad, PARTICLE_SIZE/2 * // (-1,-1,0), and the lower right corner, PARTICLE_SIZE/2 * (1,-1,0). // The upper left/right corners of the quad is always the negative of // the opposite corners (regardless of rotation). quad_lower_left.x = (-PARTICLE_SIZE/2) * (mat[0] + mat[1]); quad_lower_left.y = (-PARTICLE_SIZE/2) * (mat[4] + mat[5]); quad_lower_left.z = (-PARTICLE_SIZE/2) * (mat[8] + mat[9]); quad_lower_right.x = (PARTICLE_SIZE/2) * (mat[0] - mat[1]); quad_lower_right.y = (PARTICLE_SIZE/2) * (mat[4] - mat[5]); quad_lower_right.z = (PARTICLE_SIZE/2) * (mat[8] - mat[9]); // Don't update z-buffer, since all particles are transparent! glDepthMask( GL_FALSE ); // Enable blending glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE ); // Select particle texture if( !wireframe ) { glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, particle_tex_id ); } // Set up vertex arrays. We use interleaved arrays, which is easier to // handle (in most situations) and it gives a linear memeory access // access pattern (which may give better performance in some // situations). GL_T2F_C4UB_V3F means: 2 floats for texture coords, // 4 ubytes for color and 3 floats for vertex coord (in that order). // Most OpenGL cards / drivers are optimized for this format. glInterleavedArrays( GL_T2F_C4UB_V3F, 0, vertex_array ); // Is particle physics carried out in a separate thread? if( multithreading ) { // Wait for particle physics thread to be done glfwLockMutex( thread_sync.particles_lock ); while( running && thread_sync.p_frame <= thread_sync.d_frame ) { glfwWaitCond( thread_sync.p_done, thread_sync.particles_lock, 0.1 ); } // Store the frame time and delta time for the physics thread thread_sync.t = t; thread_sync.dt = dt; // Update frame counter thread_sync.d_frame ++; } else { // Perform particle physics in this thread ParticleEngine( t, dt ); } // Loop through all particles and build vertex arrays. particle_count = 0; vptr = vertex_array; pptr = particles; for( i = 0; i < MAX_PARTICLES; i ++ ) { if( pptr->active ) { // Calculate particle intensity (we set it to max during 75% // of its life, then it fades out) alpha = 4.0f * pptr->life; if( alpha > 1.0f ) { alpha = 1.0f; } // Convert color from float to 8-bit (store it in a 32-bit // integer using endian independent type casting) ((GLubyte *)&rgba)[0] = (GLubyte)(pptr->r * 255.0f); ((GLubyte *)&rgba)[1] = (GLubyte)(pptr->g * 255.0f); ((GLubyte *)&rgba)[2] = (GLubyte)(pptr->b * 255.0f); ((GLubyte *)&rgba)[3] = (GLubyte)(alpha * 255.0f); // 3) Translate the quad to the correct position in modelview // space and store its parameters in vertex arrays (we also // store texture coord and color information for each vertex). // Lower left corner vptr->s = 0.0f; vptr->t = 0.0f; vptr->rgba = rgba; vptr->x = pptr->x + quad_lower_left.x; vptr->y = pptr->y + quad_lower_left.y; vptr->z = pptr->z + quad_lower_left.z; vptr ++; // Lower right corner vptr->s = 1.0f; vptr->t = 0.0f; vptr->rgba = rgba; vptr->x = pptr->x + quad_lower_right.x; vptr->y = pptr->y + quad_lower_right.y; vptr->z = pptr->z + quad_lower_right.z; vptr ++; // Upper right corner vptr->s = 1.0f; vptr->t = 1.0f; vptr->rgba = rgba; vptr->x = pptr->x - quad_lower_left.x; vptr->y = pptr->y - quad_lower_left.y; vptr->z = pptr->z - quad_lower_left.z; vptr ++; // Upper left corner vptr->s = 0.0f; vptr->t = 1.0f; vptr->rgba = rgba; vptr->x = pptr->x - quad_lower_right.x; vptr->y = pptr->y - quad_lower_right.y; vptr->z = pptr->z - quad_lower_right.z; vptr ++; // Increase count of drawable particles particle_count ++; } // If we have filled up one batch of particles, draw it as a set // of quads using glDrawArrays. if( particle_count >= BATCH_PARTICLES ) { // The first argument tells which primitive type we use (QUAD) // The second argument tells the index of the first vertex (0) // The last argument is the vertex count glDrawArrays( GL_QUADS, 0, PARTICLE_VERTS * particle_count ); particle_count = 0; vptr = vertex_array; } // Next particle pptr ++; } // We are done with the particle data: Unlock mutex and signal physics // thread if( multithreading ) { glfwUnlockMutex( thread_sync.particles_lock ); glfwSignalCond( thread_sync.d_done ); } // Draw final batch of particles (if any) glDrawArrays( GL_QUADS, 0, PARTICLE_VERTS * particle_count ); // Disable vertex arrays (Note: glInterleavedArrays implicitly called // glEnableClientState for vertex, texture coord and color arrays) glDisableClientState( GL_VERTEX_ARRAY ); glDisableClientState( GL_TEXTURE_COORD_ARRAY ); glDisableClientState( GL_COLOR_ARRAY ); // Disable texturing and blending glDisable( GL_TEXTURE_2D ); glDisable( GL_BLEND ); // Allow Z-buffer updates again glDepthMask( GL_TRUE ); }