UINT cagdAddPoint(const CCagdPoint *where) { UINT id = findUnused(); SEGMENT *segment = &list[id]; segment->type = CAGD_SEGMENT_POINT; segment->visible = TRUE; memcpy(segment->color, color, sizeof(GLubyte) * 3); segment->where = (CCagdPoint *)malloc(sizeof(CCagdPoint)); *segment->where = *where; segment->length = 1; return id; }
UINT cagdAddPolyline(const CCagdPoint *where, UINT length, UINT type) { UINT id = findUnused(); SEGMENT *segment = &list[id]; if(length < 2) return 0; segment->type = type; segment->visible = TRUE; memcpy(segment->color, color, sizeof(GLubyte) * 3); segment->where = (CCagdPoint *)malloc(sizeof(CCagdPoint) * length); memcpy(segment->where, where, sizeof(CCagdPoint) * length); segment->length = length; return id; }
UINT cagdAddText(const CCagdPoint *where, PCSTR text) { UINT id = findUnused(); SEGMENT *segment = &list[id]; if(!text) return 0; segment->type = CAGD_SEGMENT_TEXT; segment->visible = TRUE; memcpy(segment->color, color, sizeof(GLubyte) * 3); segment->where = (CCagdPoint *)malloc(sizeof(CCagdPoint)); *segment->where = *where; if (!text) text = ""; segment->text = _strdup(text); segment->length = strlen(text); return id; }
static UINT findUnused() { UINT id; for(id = 1; id < nSegments; id++) if(list[id].type == CAGD_SEGMENT_UNUSED) break; if(nSegments <= id){ list = (SEGMENT*)realloc(list, sizeof(SEGMENT) * (nSegments += 20)); for(id = nSegments - 20; id < nSegments; id++){ SEGMENT *segment = &list[id]; segment->type = CAGD_SEGMENT_UNUSED; segment->visible = FALSE; segment->length = 0; segment->text = NULL; segment->where = NULL; } return findUnused(); } return id; }
/* Update the particle animation and draw the particles */ void DragonBreath::draw(glm::mat4 ProjectionMatrix, glm::mat4 ViewMatrix, glm::vec3 modelpos, glm::vec3 modelrotation, glm::vec3 positionFix) { double currentTime = glfwGetTime(); double delta = currentTime - lastTime; lastTime = currentTime; if (loc != -1) { glUniform1f(loc, false); } if (loc2 != -1) { glUniform1f(loc2, false); } glBindVertexArray(ID.VertexArray); // We will need the camera's position in order to sort the particles // w.r.t the camera's distance glm::vec3 CameraPosition(glm::inverse(ViewMatrix)[3]); glm::mat4 ViewProjectionMatrix = ProjectionMatrix * ViewMatrix; // Generate 10 new particule each millisecond, // but limit this to 16 ms (60 fps), or if you have 1 long frame (1sec), // newparticles will be huge and the next frame even longer. int newparticles = (int)(delta*1000.0); if (newparticles > (int)(0.016f*1000.0)) newparticles = (int)(0.016f*1000.0); for (int i = 0; i<newparticles; i++){ int particleIndex = findUnused(); ParticlesContainer[particleIndex].life = 2.0f; // This particle will live 5 seconds. ParticlesContainer[particleIndex].pos = modelpos + positionFix; float spread = 2.5f; glm::vec3 maindir = modelrotation; // Very bad way to generate a random direction; // See for instance http://stackoverflow.com/questions/5408276/python-uniform-spherical-distribution instead, // combined with some user-controlled parameters (main direction, spread, etc) glm::vec3 randomdir = glm::vec3( (rand() % 2000 - 1000.0f) / 1000.0f, (rand() % 2000 - 1000.0f) / 1000.0f, (rand() % 2000 - 1000.0f) / 1000.0f ); ParticlesContainer[particleIndex].speed = maindir + randomdir*spread; // Very bad way to generate a random color ParticlesContainer[particleIndex].colour.r = 0; ParticlesContainer[particleIndex].colour.g = 0; ParticlesContainer[particleIndex].colour.b = 1; ParticlesContainer[particleIndex].colour.a = (rand() % 256) / 3; ParticlesContainer[particleIndex].size = 0.1; } // Simulate all particles int ParticlesCount = 0; for (int i = 0; i<MaxParticles; i++){ Spark& p = ParticlesContainer[i]; // shortcut if (p.life > 0.0f){ // Decrease life p.life -= delta; if (p.life > 0.0f){ // Simulate simple physics : gravity only, no collisions p.speed += glm::vec3(0.0f, -0.1f, 0.0f) * (float)delta * 0.1f; p.pos += p.speed * (float)delta; p.cameradistance = glm::length2(p.pos - CameraPosition); //ParticlesContainer[i].pos += glm::vec3(0.0f,10.0f, 0.0f) * (float)delta; // Fill the GPU buffer g_particule_position_size_data[4 * ParticlesCount + 0] = p.pos.x; g_particule_position_size_data[4 * ParticlesCount + 1] = p.pos.y; g_particule_position_size_data[4 * ParticlesCount + 2] = p.pos.z; g_particule_position_size_data[4 * ParticlesCount + 3] = p.size; g_particule_color_data[4 * ParticlesCount + 0] = p.colour.r; g_particule_color_data[4 * ParticlesCount + 1] = p.colour.g; g_particule_color_data[4 * ParticlesCount + 2] = p.colour.b; g_particule_color_data[4 * ParticlesCount + 3] = p.colour.a; } else{ // Particles that just died will be put at the end of the buffer in SortParticles(); p.cameradistance = -1.0f; } ParticlesCount++; } } Sort(); // Use our shader glUseProgram(ID.program); // Update the buffers that OpenGL uses for rendering. // There are much more sophisticated means to stream data from the CPU to the GPU, // but this is outside the scope of this tutorial. // http://www.opengl.org/wiki/Buffer_Object_Streaming glBindBuffer(GL_ARRAY_BUFFER, buffer.position); 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. glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLfloat) * 4, g_particule_position_size_data); glBindBuffer(GL_ARRAY_BUFFER, buffer.colour); 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. glBufferSubData(GL_ARRAY_BUFFER, 0, ParticlesCount * sizeof(GLubyte) * 4, g_particule_color_data); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); if (ID.Texture != -1) { glUniform1f(ID.Texture, true); } // Bind our texture in Texture Unit 0 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, Texture); // Set our "myTextureSampler" sampler to user Texture Unit 0 glUniform1i(ID.Texture, 0); // Same as the billboards tutorial glUniform3f(ID.CameraRight_worldspace, ViewMatrix[0][0], ViewMatrix[1][0], ViewMatrix[2][0]); glUniform3f(ID.CameraUp_worldspace, ViewMatrix[0][1], ViewMatrix[1][1], ViewMatrix[2][1]); glUniformMatrix4fv(ID.ViewProjMatrix, 1, GL_FALSE, &ViewProjectionMatrix[0][0]); // 1rst attribute buffer : vertices glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, buffer.vertex); glVertexAttribPointer( 0, // attribute. No particular reason for 0, but must match the layout in the shader. 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); // 2nd attribute buffer : positions of particles' centers glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, buffer.position); glVertexAttribPointer( 1, // attribute. No particular reason for 1, but must match the layout in the shader. 4, // size : x + y + z + size => 4 GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); // 3rd attribute buffer : particles' colors glEnableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, buffer.colour); glVertexAttribPointer( 2, // attribute. No particular reason for 1, but must match the layout in the shader. 4, // size : r + g + b + a => 4 GL_UNSIGNED_BYTE, // type GL_TRUE, // normalized? *** YES, this means that the unsigned char[4] will be accessible with a vec4 (floats) in the shader *** 0, // stride (void*)0 // array buffer offset ); glVertexAttribDivisor(0, 0); glVertexAttribDivisor(1, 1); glVertexAttribDivisor(2, 1); glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, ParticlesCount); glVertexAttribDivisor(0, 0); glVertexAttribDivisor(1, 0); glVertexAttribDivisor(2, 0); glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); }