From 8b0640704d58984efd0d54e3350d21e9f5845c85 Mon Sep 17 00:00:00 2001
From: Loren Shih <seraph@lindenlab.com>
Date: Wed, 20 Apr 2011 12:37:44 -0400
Subject: [PATCH] SH-1381 FIXED avatar physics behavior is tightly tied to
 viewer framerate

Breaking up physics into smaller integration steps.
---
 indra/newview/llphysicsmotion.cpp | 354 ++++++++++++++++--------------
 1 file changed, 184 insertions(+), 170 deletions(-)

diff --git a/indra/newview/llphysicsmotion.cpp b/indra/newview/llphysicsmotion.cpp
index 5c4d006288e..e4335d968f2 100644
--- a/indra/newview/llphysicsmotion.cpp
+++ b/indra/newview/llphysicsmotion.cpp
@@ -43,7 +43,8 @@
 typedef std::map<std::string, std::string> controller_map_t;
 typedef std::map<std::string, F32> default_controller_map_t;
 
-#define MIN_REQUIRED_PIXEL_AREA_AVATAR_PHYSICS_MOTION 0.f;
+#define MIN_REQUIRED_PIXEL_AREA_AVATAR_PHYSICS_MOTION 0.f
+#define TIME_ITERATION_STEP 0.1f
 
 inline F64 llsgn(const F64 a)
 {
@@ -453,7 +454,8 @@ BOOL LLPhysicsMotion::onUpdate(F32 time)
 		return FALSE;
 	}
 	
-        if (time_delta > 3.0)
+	// If less than 1FPS, we don't want to be spending time updating physics at all.
+        if (time_delta > 1.0)
         {
                 mLastTime = time;
                 return FALSE;
@@ -481,195 +483,207 @@ BOOL LLPhysicsMotion::onUpdate(F32 time)
         if (physics_test)
                 behavior_maxeffect = 1.0f;
 
-        // mPositon_local should be in normalized 0,1 range already.  Just making sure...
-        F32 position_current_local = llclamp(mPosition_local,
-                                             0.0f,
-                                             1.0f);
-
-        // Normalize the param position to be from [0,1].
-        // We have to use normalized values because there may be more than one driven param,
-        // and each of these driven params may have its own range.
-        // This means we'll do all our calculations in normalized [0,1] local coordinates.
-        F32 position_user_local = mParamDriver->getWeight();
-        position_user_local = (position_user_local - mParamDriver->getMinWeight()) / (mParamDriver->getMaxWeight() - mParamDriver->getMinWeight());
-
-        // If the effect is turned off then don't process unless we need one more update
-        // to set the position to the default (i.e. user) position.
-        if ((behavior_maxeffect == 0) && (position_current_local == position_user_local))
-        {
-            return FALSE;
-        }
-
-        //
-        // End parameters and settings
-        ////////////////////////////////////////////////////////////////////////////////
-
-
-        ////////////////////////////////////////////////////////////////////////////////
-        // Calculate velocity and acceleration in parameter space.
-        //
+	BOOL update_visuals = FALSE; 
+	// Break up the physics into a bunch of iterations so that differing framerates will show
+	// roughly the same behavior.
+	for (F32 time_iteration = 0; time_iteration <= time_delta; time_iteration += TIME_ITERATION_STEP)
+	{
+		F32 time_iteration_step = TIME_ITERATION_STEP;
+		if (time_iteration + TIME_ITERATION_STEP > time_delta)
+		{
+			time_iteration_step = time_delta;
+		}
+		
+
+		// mPositon_local should be in normalized 0,1 range already.  Just making sure...
+		F32 position_current_local = llclamp(mPosition_local,
+						     0.0f,
+						     1.0f);
+
+		// Normalize the param position to be from [0,1].
+		// We have to use normalized values because there may be more than one driven param,
+		// and each of these driven params may have its own range.
+		// This means we'll do all our calculations in normalized [0,1] local coordinates.
+		F32 position_user_local = mParamDriver->getWeight();
+		position_user_local = (position_user_local - mParamDriver->getMinWeight()) / (mParamDriver->getMaxWeight() - mParamDriver->getMinWeight());
+
+		// If the effect is turned off then don't process unless we need one more update
+		// to set the position to the default (i.e. user) position.
+		if ((behavior_maxeffect == 0) && (position_current_local == position_user_local))
+		{
+			return FALSE;
+		}
+
+		//
+		// End parameters and settings
+		////////////////////////////////////////////////////////////////////////////////
+
+
+		////////////////////////////////////////////////////////////////////////////////
+		// Calculate velocity and acceleration in parameter space.
+		//
         
-        const F32 velocity_joint_local = calculateVelocity_local(time_delta);
-        const F32 acceleration_joint_local = calculateAcceleration_local(velocity_joint_local, time_delta);
+		const F32 velocity_joint_local = calculateVelocity_local(time_iteration_step);
+		const F32 acceleration_joint_local = calculateAcceleration_local(velocity_joint_local, time_iteration_step);
 
-        //
-        // End velocity and acceleration
-        ////////////////////////////////////////////////////////////////////////////////
+		//
+		// End velocity and acceleration
+		////////////////////////////////////////////////////////////////////////////////
 
 
-        ////////////////////////////////////////////////////////////////////////////////
-        // Calculate the total force 
-        //
+		////////////////////////////////////////////////////////////////////////////////
+		// Calculate the total force 
+		//
 
-        // Spring force is a restoring force towards the original user-set breast position.
-        // F = kx
-        const F32 spring_length = position_current_local - position_user_local;
-        const F32 force_spring = -spring_length * behavior_spring;
+		// Spring force is a restoring force towards the original user-set breast position.
+		// F = kx
+		const F32 spring_length = position_current_local - position_user_local;
+		const F32 force_spring = -spring_length * behavior_spring;
 
-        // Acceleration is the force that comes from the change in velocity of the torso.
-        // F = ma
-        const F32 force_accel = behavior_gain * (acceleration_joint_local * behavior_mass);
+		// Acceleration is the force that comes from the change in velocity of the torso.
+		// F = ma
+		const F32 force_accel = behavior_gain * (acceleration_joint_local * behavior_mass);
 
-        // Gravity always points downward in world space.
-        // F = mg
-        const LLVector3 gravity_world(0,0,1);
-        const F32 force_gravity = behavior_gain * (toLocal(gravity_world) * behavior_gravity * behavior_mass);
+		// Gravity always points downward in world space.
+		// F = mg
+		const LLVector3 gravity_world(0,0,1);
+		const F32 force_gravity = behavior_gain * (toLocal(gravity_world) * behavior_gravity * behavior_mass);
                 
-        // Damping is a restoring force that opposes the current velocity.
-        // F = -kv
-        const F32 force_damping = -behavior_damping * mVelocity_local;
+		// Damping is a restoring force that opposes the current velocity.
+		// F = -kv
+		const F32 force_damping = -behavior_damping * mVelocity_local;
                 
-        // Drag is a force imparted by velocity (intuitively it is similar to wind resistance)
-        // F = .5kv^2
-        const F32 force_drag = .5*behavior_drag*velocity_joint_local*velocity_joint_local*llsgn(velocity_joint_local);
+		// Drag is a force imparted by velocity (intuitively it is similar to wind resistance)
+		// F = .5kv^2
+		const F32 force_drag = .5*behavior_drag*velocity_joint_local*velocity_joint_local*llsgn(velocity_joint_local);
 
-        const F32 force_net = (force_accel + 
-                               force_gravity +
-                               force_spring + 
-                               force_damping + 
-                               force_drag);
+		const F32 force_net = (force_accel + 
+				       force_gravity +
+				       force_spring + 
+				       force_damping + 
+				       force_drag);
 
-        //
-        // End total force
-        ////////////////////////////////////////////////////////////////////////////////
+		//
+		// End total force
+		////////////////////////////////////////////////////////////////////////////////
 
         
-        ////////////////////////////////////////////////////////////////////////////////
-        // Calculate new params
-        //
-
-        // Calculate the new acceleration based on the net force.
-        // a = F/m
-        const F32 acceleration_new_local = force_net / behavior_mass;
-        static const F32 max_acceleration = 10.0f; // magic number, used to be customizable.
-        F32 velocity_new_local = mVelocity_local + acceleration_new_local;
-        velocity_new_local = llclamp(velocity_new_local, 
-                                     -max_acceleration, max_acceleration);
+		////////////////////////////////////////////////////////////////////////////////
+		// Calculate new params
+		//
+
+		// Calculate the new acceleration based on the net force.
+		// a = F/m
+		const F32 acceleration_new_local = force_net / behavior_mass;
+		static const F32 max_acceleration = 10.0f; // magic number, used to be customizable.
+		F32 velocity_new_local = mVelocity_local + acceleration_new_local;
+		velocity_new_local = llclamp(velocity_new_local, 
+					     -max_acceleration, max_acceleration);
         
-        // Temporary debugging setting to cause all avatars to move, for profiling purposes.
-        if (physics_test)
-        {
-                velocity_new_local = sin(time*4.0);
-        }
-        // Calculate the new parameters, or remain unchanged if max speed is 0.
-        F32 position_new_local = position_current_local + velocity_new_local*time_delta;
-        if (behavior_maxeffect == 0)
-            position_new_local = position_user_local;
-
-        // Zero out the velocity if the param is being pushed beyond its limits.
-        if ((position_new_local < 0 && velocity_new_local < 0) || 
-            (position_new_local > 1 && velocity_new_local > 0))
-        {
-                velocity_new_local = 0;
-        }
+		// Temporary debugging setting to cause all avatars to move, for profiling purposes.
+		if (physics_test)
+		{
+			velocity_new_local = sin(time*4.0);
+		}
+		// Calculate the new parameters, or remain unchanged if max speed is 0.
+		F32 position_new_local = position_current_local + velocity_new_local*time_iteration_step;
+		if (behavior_maxeffect == 0)
+			position_new_local = position_user_local;
+
+		// Zero out the velocity if the param is being pushed beyond its limits.
+		if ((position_new_local < 0 && velocity_new_local < 0) || 
+		    (position_new_local > 1 && velocity_new_local > 0))
+		{
+			velocity_new_local = 0;
+		}
 	
-	// Check for NaN values.  A NaN value is detected if the variables doesn't equal itself.  
-	// If NaN, then reset everything.
-	if ((mPosition_local != mPosition_local) ||
-	    (mVelocity_local != mVelocity_local) ||
-	    (position_new_local != position_new_local))
-	{
-		position_new_local = 0;
-		position_current_local = 0;
-		position_user_local = 0;
-		mVelocity_local = 0;
-		mVelocityJoint_local = 0;
-		mAccelerationJoint_local = 0;
-		mPosition_local = 0;
-		mPosition_world = LLVector3(0,0,0);
-	}
-
-        const F32 position_new_local_clamped = llclamp(position_new_local,
-						       0.0f,
-						       1.0f);
-
-        LLDriverParam *driver_param = dynamic_cast<LLDriverParam *>(mParamDriver);
-        llassert_always(driver_param);
-        if (driver_param)
-        {
-                // If this is one of our "hidden" driver params, then make sure it's
-                // the default value.
-                if ((driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) &&
-                    (driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT))
-                {
-                        mCharacter->setVisualParamWeight(driver_param,
-                                                         0,
-                                                         FALSE);
-                }
-                for (LLDriverParam::entry_list_t::iterator iter = driver_param->mDriven.begin();
-                     iter != driver_param->mDriven.end();
-                     ++iter)
-                {
-                        LLDrivenEntry &entry = (*iter);
-                        LLViewerVisualParam *driven_param = entry.mParam;
-                        setParamValue(driven_param,position_new_local_clamped, behavior_maxeffect);
-                }
-        }
+		// Check for NaN values.  A NaN value is detected if the variables doesn't equal itself.  
+		// If NaN, then reset everything.
+		if ((mPosition_local != mPosition_local) ||
+		    (mVelocity_local != mVelocity_local) ||
+		    (position_new_local != position_new_local))
+		{
+			position_new_local = 0;
+			position_current_local = 0;
+			position_user_local = 0;
+			mVelocity_local = 0;
+			mVelocityJoint_local = 0;
+			mAccelerationJoint_local = 0;
+			mPosition_local = 0;
+			mPosition_world = LLVector3(0,0,0);
+		}
+
+		const F32 position_new_local_clamped = llclamp(position_new_local,
+							       0.0f,
+							       1.0f);
+
+		LLDriverParam *driver_param = dynamic_cast<LLDriverParam *>(mParamDriver);
+		llassert_always(driver_param);
+		if (driver_param)
+		{
+			// If this is one of our "hidden" driver params, then make sure it's
+			// the default value.
+			if ((driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) &&
+			    (driver_param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT))
+			{
+				mCharacter->setVisualParamWeight(driver_param,
+								 0,
+								 FALSE);
+			}
+			for (LLDriverParam::entry_list_t::iterator iter = driver_param->mDriven.begin();
+			     iter != driver_param->mDriven.end();
+			     ++iter)
+			{
+				LLDrivenEntry &entry = (*iter);
+				LLViewerVisualParam *driven_param = entry.mParam;
+				setParamValue(driven_param,position_new_local_clamped, behavior_maxeffect);
+			}
+		}
         
-        //
-        // End calculate new params
-        ////////////////////////////////////////////////////////////////////////////////
+		//
+		// End calculate new params
+		////////////////////////////////////////////////////////////////////////////////
 
-        ////////////////////////////////////////////////////////////////////////////////
-        // Conditionally update the visual params
-        //
+		////////////////////////////////////////////////////////////////////////////////
+		// Conditionally update the visual params
+		//
         
-        // Updating the visual params (i.e. what the user sees) is fairly expensive.
-        // So only update if the params have changed enough, and also take into account
-        // the graphics LOD settings.
+		// Updating the visual params (i.e. what the user sees) is fairly expensive.
+		// So only update if the params have changed enough, and also take into account
+		// the graphics LOD settings.
         
-        BOOL update_visuals = FALSE;
-
-        // For non-self, if the avatar is small enough visually, then don't update.
-        const F32 area_for_max_settings = 0.0;
-        const F32 area_for_min_settings = 1400.0;
-        const F32 area_for_this_setting = area_for_max_settings + (area_for_min_settings-area_for_max_settings)*(1.0-lod_factor);
-        const F32 pixel_area = fsqrtf(mCharacter->getPixelArea());
+		// For non-self, if the avatar is small enough visually, then don't update.
+		const F32 area_for_max_settings = 0.0;
+		const F32 area_for_min_settings = 1400.0;
+		const F32 area_for_this_setting = area_for_max_settings + (area_for_min_settings-area_for_max_settings)*(1.0-lod_factor);
+		const F32 pixel_area = fsqrtf(mCharacter->getPixelArea());
         
-        const BOOL is_self = (dynamic_cast<LLVOAvatarSelf *>(mCharacter) != NULL);
-        if ((pixel_area > area_for_this_setting) || is_self)
-        {
-                const F32 position_diff_local = llabs(mPositionLastUpdate_local-position_new_local_clamped);
-                const F32 min_delta = (1.0001f-lod_factor)*0.4f;
-                if (llabs(position_diff_local) > min_delta)
-                {
-                        update_visuals = TRUE;
-                        mPositionLastUpdate_local = position_new_local;
-                }
-        }
+		const BOOL is_self = (dynamic_cast<LLVOAvatarSelf *>(mCharacter) != NULL);
+		if ((pixel_area > area_for_this_setting) || is_self)
+		{
+			const F32 position_diff_local = llabs(mPositionLastUpdate_local-position_new_local_clamped);
+			const F32 min_delta = (1.0001f-lod_factor)*0.4f;
+			if (llabs(position_diff_local) > min_delta)
+			{
+				update_visuals = TRUE;
+				mPositionLastUpdate_local = position_new_local;
+			}
+		}
+
+		//
+		// End update visual params
+		////////////////////////////////////////////////////////////////////////////////
+
+		mVelocityJoint_local = velocity_joint_local;
+
+		mVelocity_local = velocity_new_local;
+		mAccelerationJoint_local = acceleration_joint_local;
+		mPosition_local = position_new_local;
+
+		mPosition_world = joint->getWorldPosition();
 
-        //
-        // End update visual params
-        ////////////////////////////////////////////////////////////////////////////////
-
-        mVelocityJoint_local = velocity_joint_local;
-
-        mVelocity_local = velocity_new_local;
-        mAccelerationJoint_local = acceleration_joint_local;
-        mPosition_local = position_new_local;
-
-        mPosition_world = joint->getWorldPosition();
-        mLastTime = time;
+	}
+	mLastTime = time;
 
         /*
           // Write out debugging info into a spreadsheet.
-- 
GitLab