void getEdge(int p, int q, double* X, int N, int D, double* ts, int* e1, int* e2, double* ed, double eps) { double dpq = sqrt(getSqrDist(X, N, D, p, q)); double tp = ts[p]; double tq = ts[q]; double alpha; //SEE SHEEHY PAGE 8 if (tq < tp) { //Make sure tp indexes the earlier of the two death times double temp = tq; tq = tp; tp = temp; } //Case a (no warping has occurred yet) if (dpq < tp*(1-2*eps)) { *e1 = p; *e2 = q; *ed = dpq; return; } //Case b: alpha is between (1-2eps)tp and tp but is less than (1-2eps)tq alpha = 2*dpq - (1-2*eps)*tp; if (alpha >= (1-2*eps)*tp && alpha <= tp && alpha <= (1-2*eps)*tq) { *e1 = p; *e2 = q; *ed = alpha; return; } //Otherwise, it's never added *e1 = -1; *e2 = -1; }
bool Sphere::intersects(const Vector3D& v0,const Vector3D& v1,Vector3D* intersection,Vector3D* normal) const { float r2 = radius * radius; float dist0 = getSqrDist(getTransformedPosition(),v0); float dist1 = getSqrDist(getTransformedPosition(),v1); if ((dist0 <= r2) == (dist1 <= r2)) return false; if (intersection != NULL) { Vector3D vDir = v1 - v0; float norm = vDir.getNorm(); float d = dotProduct(vDir,getTransformedPosition() - v0) / norm; float a = std::sqrt(r2 - dist0 + d * d); float ti; if (dist0 <= r2) ti = d - a; else ti = d + a; ti /= norm; if (ti < 0.0f) ti = 0.0f; if (ti > 1.0f) ti = 1.0f; norm *= ti; ti = norm < APPROXIMATION_VALUE ? 0.0f : ti * (norm - APPROXIMATION_VALUE) / norm; vDir *= ti; *intersection = v0 + vDir; if (normal != NULL) { if (dist0 <= r2) *normal = getTransformedPosition() - *intersection; else *normal = *intersection - getTransformedPosition(); normal->normalize(); } } return true; }
void Collider::modify(Group& group,DataSet* dataSet,float deltaTime) const { float groupSqrRadius = group.getPhysicalRadius() * group.getPhysicalRadius(); SPK_ASSERT(group.getOctree() != NULL,"GLQuadRenderer::render(const Group&,const DataSet*,RenderBuffer*) - renderBuffer must not be NULL"); const Octree& octree = *group.getOctree(); for (GroupIterator particleIt0(group); !particleIt0.end(); ++particleIt0) { Particle& particle0 = *particleIt0; float radius0 = particle0.getParam(PARAM_SCALE); float m0 = particle0.getParam(PARAM_MASS); size_t index0 = particle0.getIndex(); const Octree::Array<size_t>& neighborCells = octree.getNeighborCells(index0); size_t nbCells = neighborCells.size(); for (size_t i = 0; i < nbCells; ++i) // For each neighboring cell in the octree { const Octree::Cell& cell = octree.getCell(neighborCells[i]); size_t nbParticleInCells = cell.particles.size(); for (size_t j = 0; j < nbParticleInCells; ++j) // for each particles in the cell { size_t index1 = cell.particles[j]; if (index1 >= index0) break; // as particle are ordered Particle particle1 = group.getParticle(index1); float radius1 = particle1.getParam(PARAM_SCALE); float sqrRadius = radius0 + radius1; sqrRadius *= sqrRadius * groupSqrRadius; // Gets the normal of the collision plane Vector3D normal = particle0.position() - particle1.position(); float sqrDist = normal.getSqrNorm(); if (sqrDist < sqrRadius) // particles are intersecting each other { Vector3D delta = particle0.velocity() - particle1.velocity(); if (dotProduct(normal,delta) < 0.0f) // particles are moving towards each other { float oldSqrDist = getSqrDist(particle0.oldPosition(),particle1.oldPosition()); if (oldSqrDist > sqrDist) { // Disables the move from this frame particle0.position() = particle0.oldPosition(); particle1.position() = particle1.oldPosition(); normal = particle0.position() - particle1.position(); if (dotProduct(normal,delta) >= 0.0f) continue; } normal.normalize(); // Gets the normal components of the velocities Vector3D normal0 = normal * dotProduct(normal,particle0.velocity()); Vector3D normal1 = normal * dotProduct(normal,particle1.velocity()); // Resolves collision float m1 = particle1.getParam(PARAM_MASS); if (oldSqrDist < sqrRadius && sqrDist < sqrRadius) { // Tweak to separate particles that intersects at both t - deltaTime and t // In that case the collision is no more considered as punctual if (dotProduct(normal,normal0) < 0.0f) { particle0.velocity() -= normal0; particle1.velocity() += normal0; } if (dotProduct(normal,normal1) > 0.0f) { particle1.velocity() -= normal1; particle0.velocity() += normal1; } } else { // Else classic collision equations are applied // Tangent components of the velocities are left untouched float elasticityM0 = elasticity * m0; float elasticityM1 = elasticity * m1; float invM01 = 1 / (m0 + m1); particle0.velocity() -= (1.0f + (elasticityM1 - m0) * invM01) * normal0; particle1.velocity() -= (1.0f + (elasticityM0 - m1) * invM01) * normal1; normal0 *= (elasticityM0 + m0) * invM01; normal1 *= (elasticityM1 + m0) * invM01; particle0.velocity() += normal1; particle1.velocity() += normal0; } } } } } } }
bool Group::updateParticles(float deltaTime) { // Prepares the additionnal data prepareAdditionnalData(); size_t nbAutoBorn = 0; size_t nbManualBorn = nbBufferedParticles; // Checks the number of born particles bool hasAliveEmitters = false; activeEmitters.clear(); for (std::vector<Ref<Emitter> >::const_iterator it = emitters.begin(); it != emitters.end(); ++it) if ((*it)->isActive()) { int nb = (*it)->updateTankFromTime(deltaTime); if (nb > 0) { activeEmitters.push_back(WeakEmitterPair(*it,nb)); nbAutoBorn += nb; } hasAliveEmitters |= ((*it)->getCurrentTank() != 0); // An emitter with some particles in its tank is still potentially alive } size_t emitterIndex = 0; size_t nbBorn = nbAutoBorn + nbManualBorn; // Updates the age of the particles function of the delta time for (size_t i = 0; i < particleData.nbParticles; ++i) particleData.ages[i] += deltaTime; // Computes the energy of the particles (if they are not immortal) if (!immortal) for (size_t i = 0; i < particleData.nbParticles; ++i) particleData.energies[i] = 1.0f - particleData.ages[i] / particleData.lifeTimes[i]; // Updates the position of particles function of their velocity if (!still) for (size_t i = 0; i < particleData.nbParticles; ++i) { particleData.oldPositions[i] = particleData.positions[i]; particleData.positions[i] += particleData.velocities[i] * deltaTime; } // Interpolates the parameters if (colorInterpolator.obj) colorInterpolator.obj->interpolate(particleData.colors,*this,colorInterpolator.dataSet); for (size_t i = 0; i < nbEnabledParameters; ++i) { FloatInterpolatorDef& interpolator = paramInterpolators[enabledParamIndices[i]]; interpolator.obj->interpolate(particleData.parameters[enabledParamIndices[i]],*this,interpolator.dataSet); } // Updates the octree if one if (octree != NULL) octree->update(); // Modifies the particles with specific active modifiers behavior for (std::vector<WeakModifierDef>::const_iterator it = activeModifiers.begin(); it != activeModifiers.end(); ++it) it->obj->modify(*this,it->dataSet,deltaTime); // Updates the renderer data if (renderer.obj) renderer.obj->update(*this,renderer.dataSet); // Checks dead particles and reinits or swaps for (size_t i = 0; i < particleData.nbParticles; ++i) if (particleData.energies[i] <= 0.0f) { // Death action if (deathAction && deathAction->isActive()) { Particle particle = getParticle(i); // fix for gcc deathAction->apply(particle); } bool replaceDeadParticle = false; while (!replaceDeadParticle && nbBorn > 0) { if (initParticle(i,emitterIndex,nbManualBorn)) replaceDeadParticle = true; --nbBorn; } if (!replaceDeadParticle) { swapParticles(i,particleData.nbParticles - 1); --particleData.nbParticles; --i; // As we need to test the swapped particle } } // Emits new particles if some left while (nbBorn > 0 && particleData.maxParticles - particleData.nbParticles > 0) { if (!initParticle(particleData.nbParticles++,emitterIndex,nbManualBorn)) --particleData.nbParticles; --nbBorn; } // Computes the distance of particles from the camera if (distanceComputationEnabled) { for (size_t i = 0; i < particleData.nbParticles; ++i) particleData.sqrDists[i] = getSqrDist(particleData.positions[i],system->getCameraPosition()); } emptyBufferedParticles(); return hasAliveEmitters || particleData.nbParticles > 0; }
void Collision::modify(Particle& particle,float deltaTime) const { size_t index = particle.getIndex(); float radius1 = particle.getParamCurrentValue(PARAM_SIZE) * scale * 0.5f; float m1 = particle.getParamCurrentValue(PARAM_MASS); Group& group = *particle.getGroup(); // Tests collisions with all the particles that are stored before in the pool for (size_t i = 0; i < index; ++i) { Particle& particle2 = group.getParticle(i); float radius2 = particle2.getParamCurrentValue(PARAM_SIZE) * scale * 0.5f; float sqrRadius = radius1 + radius2; sqrRadius *= sqrRadius; // Gets the normal of the collision plane vec3 normal = particle.position(); normal -= particle2.position(); // float sqrDist = normal.getSqrNorm(); float sqrDist = glm::length2(normal); if (sqrDist < sqrRadius) // particles are intersecting each other { vec3 delta = particle.velocity(); delta -= particle2.velocity(); if (dotProduct(normal,delta) < 0.0f) // particles are moving towards each other { float oldSqrDist = getSqrDist(particle.oldPosition(),particle2.oldPosition()); if (oldSqrDist > sqrDist) { // Disables the move from this frame particle.position() = particle.oldPosition(); particle2.position() = particle2.oldPosition(); normal = particle.position(); normal -= particle2.position(); if (dotProduct(normal,delta) >= 0.0f) continue; } // normal.normalize(); normal = glm::normalize(normal); // Gets the normal components of the velocities vec3 normal1(normal); vec3 normal2(normal); normal1 *= dotProduct(normal,particle.velocity()); normal2 *= dotProduct(normal,particle2.velocity()); // Resolves collision float m2 = particle2.getParamCurrentValue(PARAM_MASS); if (oldSqrDist < sqrRadius && sqrDist < sqrRadius) { // Tweak to separate particles that intersects at both t - deltaTime and t // In that case the collision is no more considered as punctual if (dotProduct(normal,normal1) < 0.0f) { particle.velocity() -= normal1; particle2.velocity() += normal1; } if (dotProduct(normal,normal2) > 0.0f) { particle2.velocity() -= normal2; particle.velocity() += normal2; } } else { // Else classic collision equations are applied // Tangent components of the velocities are left untouched particle.velocity() -= (1.0f + (elasticity * m2 - m1) / (m1 + m2)) * normal1; particle2.velocity() -= (1.0f + (elasticity * m1 - m2) / (m1 + m2)) * normal2; normal1 *= ((1.0f + elasticity) * m1) / (m1 + m2); normal2 *= ((1.0f + elasticity) * m2) / (m1 + m2); particle.velocity() += normal2; particle2.velocity() += normal1; } } } } }
float getDist(const Vector3D& v0,const Vector3D& v1) { return std::sqrt(getSqrDist(v0,v1)); }
bool Sphere::contains(const Vector3D& v) const { return getSqrDist(getTransformedPosition(),v) <= radius * radius; }
void Particle::computeSqrDist() { data->sqrDist = getSqrDist(position(),System::getCameraPosition()); }