/// Integrates all particles. void PrecipitationSystem::ProcessParticles(float & timeInSeconds) { Timer timer; timer.Start(); #ifdef USE_SSE __m128 sseTime = _mm_load1_ps(&timeInSeconds); #endif /// Move/Process all alive particles const Vector3f wind = weather->globalWind; for (int i = 0; i < aliveParticles; ++i) { #ifdef SSE_PARTICLES positionsSSE[i].data = _mm_add_ps(positionsSSE[i].data, _mm_mul_ps(sseTime, _mm_add_ps(velocitiesSSE[i].data, wind.data))); #else // Not SSE_PARTICLES // Using SSE commands straight away reduced computation time to like 1 ms from 150ms when many particles were around (towards 500k somewhere) #ifdef USE_SSE positions[i].data = _mm_add_ps(positions[i].data, _mm_mul_ps(sseTime, _mm_add_ps(velocities[i].data, weather->globalWind.data))); #else positions[i] += (velocities[i] + weather->globalWind)* timeInSeconds; #endif // USE_SSE #endif // SSE_PARTICLES } timer.Stop(); FrameStats.particleProcessingIntegrate += timer.GetMs(); timer.Start(); for (int i = 0; i < aliveParticles; ++i) { #ifdef SSE_PARTICLES ldsSSE[i].y += timeInSeconds; #else // Not SSE_PARTICLES // No velocity decay. lifeDurations[i] += timeInSeconds; #endif // SSE_PARTICLES } timer.Stop(); FrameStats.particleProcessingOldify = timer.GetMs(); timer.Start(); for (int i = 0; i < aliveParticles; ++i) { #ifdef SSE_PARTICLES if (ldsSSE[i].y > ldsSSE[i].x) { int lastIndex = aliveParticles - 1; positionsSSE[i] = positionsSSE[lastIndex]; velocitiesSSE[i] = velocitiesSSE[lastIndex]; colorsSSE[i] = colorsSSE[lastIndex]; ldsSSE[i] = ldsSSE[lastIndex]; // Decrement i so we don't skip processing of the one we moved back. --i; // Decrement alive particles. --aliveParticles; } #else // Not SSE_PARTICLES // If duration has elapsed life-time.. if (lifeDurations[i] > lifeTimes[i]) { int lastIndex = aliveParticles - 1; // Kill it, by moving in the last used data to replace it. positions[i] = positions[lastIndex]; velocities[i] = velocities[lastIndex]; lifeDurations[i] = lifeDurations[lastIndex]; colors[i] = colors[lastIndex]; lifeTimes[i] = lifeTimes[lastIndex]; scales[i] = scales[lastIndex]; // Decrement i so we don't skip processing of the one we moved back. --i; // Decrement alive particles. --aliveParticles; } #endif } timer.Stop(); FrameStats.particleProcessingRedead += timer.GetMs(); }
/// Processes physics for all registered objects void PhysicsManager::ProcessPhysics() { if (physicsState->simulationPaused) return; /// Returns straight away if paused. if (paused) return; if (physicalEntities.Size() == 0) return; activeTriangles.Clear(); time_t currentTime = Timer::GetCurrentTimeMs(); time_t millisecondsSinceLastUpdate = currentTime - lastUpdate; lastUpdate = currentTime; // std::cout<<"\nCurrent time: "<<currentTime<<" last update: "<<lastUpdate; /// Throw away time if we've got more than 1 second, since this assumes we're debugging if (millisecondsSinceLastUpdate > 100){ if (millisecondsSinceLastUpdate > 1000) std::cout<<"\nPhysicsManager::Throwing away "<<millisecondsSinceLastUpdate / 1000<<" debugging seconds"; millisecondsSinceLastUpdate = 100; } float totalTimeSinceLastUpdate = millisecondsSinceLastUpdate * 0.001f; /// Multiply the total time since last update with the simulation speed multiplier before actual calculations are begun. totalTimeSinceLastUpdate = totalTimeSinceLastUpdate * simulationSpeed; /// Just return if simulation speed decreases beyond 0.1%! if (simulationSpeed <= 0.0001f){ return; } /// Debugging time statistics float messageProcessingTime = 0; float recalculatingPropertiesDuration = 0; float collissionProcessingFrameTime = 0; // Reset previous frame-times recalculatingPropertiesDuration = 0; float integration = 0; collissionProcessingFrameTime = 0; physicsMeshCollisionChecks = 0; // To be sent for Collision callback. List<Message*> messages; /// Do one process for each 10 ms we've gotten stored up /// Get sub-time to calculate. float dt = 0.010f * simulationSpeed; float timeDiff = dt; float timeInSecondsSinceLastUpdate = dt; static float timeRemainingFromLastIteration = 0.f; /// Add time from last iteration that wasn't spent (since only evaluating one physics step at a time, 10 ms default). float timeToIterate = totalTimeSinceLastUpdate + timeRemainingFromLastIteration; float stepSize = 0.010f; int steps = timeToIterate / stepSize + 0.5f; /// Use a new step size based on the amount of steps. This will hopefully vary between 5.0 and 15.0 then. float newStepSize = timeToIterate / steps; int newStepSizeMs = newStepSize * 1000; newStepSize = newStepSizeMs * 0.001f; if (newStepSize < 0.005f || newStepSize > 0.015f) { LogPhysics("Step size out of good range: "+String(newStepSize), WARNING); if (newStepSize < 0.f) return; // assert(False) } // assert(newStepSize > 0.005f && newStepSize < 0.015f); // if (steps < 1) // At least 1 physics simulation per frame, yo. Otherwise you get a 'stuttering' effect when some frames have movement and some don't. // steps = 1; float timeLeft = timeToIterate - steps * newStepSizeMs * 0.001f; /// Store time we won't simulate now. timeRemainingFromLastIteration = timeLeft; // std::cout<<"\nSteps: "<<steps; for(int i = 0; i < steps; ++i) { /// Set current time in physics for this frame. This time is not the same as real time. physicsNowMs += newStepSizeMs; /// Process estimators (if any) within all registered entities? int milliseconds = newStepSizeMs; for (int i = 0 ; i < physicalEntities.Size(); ++i) { Entity * entity = physicalEntities[i]; List<Estimator*> & estimators = entity->physics->estimators; for (int j = 0; j < estimators.Size(); ++j) { Estimator * estimator = estimators[j]; estimator->Process(milliseconds); if (entity->name == "ExplosionEntity") int lp = 5; // Recalculate other stuff too. entity->physics->UpdateProperties(entity); // Re-calculate transform matrix, as it was probably affected..? entity->RecalculateMatrix(Entity::ALL_PARTS); if (estimator->finished) { estimators.RemoveIndex(j, ListOption::RETAIN_ORDER); --j; delete estimator; } } } /// Awesome. Integrate(newStepSize); /// Apply external constraints // ApplyContraints(); /// Apply pathfinding for all relevant entities - should be done in separate property-files. Or in more dedicated classes for specific games. // ApplyPathfinding(); Timer collisionTimer; collisionTimer.Start(); Timer timer; timer.Start(); /// Detect collisions. List<Collision> collisions; Timer sweepTimer; sweepTimer.Start(); // Generate pair of possible collissions via some optimized way (AABB-sorting or Octree). List<EntityPair> pairs = this->aabbSweeper->Sweep(); sweepTimer.Stop(); int sweepDur = sweepTimer.GetMs(); FrameStats.physicsCollisionDetectionAABBSweep += sweepDur; // std::cout<<"\nAABB sweep pairs: "<<pairs.Size()<<" with "<<physicalEntities.Size()<<" entities"; Timer detectorTimer; detectorTimer.Start(); if (collisionDetector) { collisionDetector->DetectCollisions(pairs, collisions); } // Old approach which combined collision-detection and resolution in a big mess... else DetectCollisions(); detectorTimer.Stop(); int detectorMs = detectorTimer.GetMs(); FrameStats.physicsCollisionDetectionChosenDetector += detectorMs; timer.Stop(); int thisFrame = timer.GetMs(); FrameStats.physicsCollisionDetection += thisFrame; timer.Start(); /// And resolve them. if (collisionResolver) collisionResolver->ResolveCollisions(collisions); timer.Stop(); FrameStats.physicsCollisionResolution += timer.GetMs(); timer.Start(); messages.Clear(); for (int i = 0; i < collisions.Size(); ++i) { Collision & c = collisions[i]; if (c.one->physics->onCollision) c.one->OnCollision(c); if (c.two->physics->onCollision) c.two->OnCollision(c); if (c.one->physics->collisionCallback || c.two->physics->collisionCallback) { /// Check max callbacks. int & maxCallbacks1 = c.one->physics->maxCallbacks; int & maxCallbacks2 = c.two->physics->maxCallbacks; if (maxCallbacks1 == 0 || maxCallbacks2 == 0) continue; CollisionCallback * cc = new CollisionCallback(c.one, c.two); cc->impactNormal = c.collisionNormal; messages.Add(cc); if (maxCallbacks1 > 0) --maxCallbacks1; if (maxCallbacks2 > 0) --maxCallbacks2; } } if (messages.Size()) MesMan.QueueMessages(messages); timer.Stop(); FrameStats.physicsCollisionCallback += timer.GetMs(); collisionTimer.Stop(); FrameStats.physicsCollisions += collisionTimer.GetMs(); int64 colMs = collisionTimer.GetMs(); if (colMs > 50) { std::cout<<"\nCollision detection and resolution taking "<<colMs<<" milliseconds per frame."; break; // Break the loop. Simulate more next time if it's already being slow. } } // Recalc matrices for the semi-dynamic ones. if (physicsIntegrator) physicsIntegrator->RecalculateMatrices(semiDynamicEntities); else LogPhysics("No integrator assigned", ERROR); // Reset previous frame-times // FrameStats.physicsIntegration = integration; collissionProcessingFrameTime = 0; physicsMeshCollisionChecks = 0; }