View Issue Details

IDProjectCategoryView StatusLast Update
0004493The Dark ModPhysicspublic05.12.2023 01:37
Reportergrayman Assigned Toduzenko  
PrioritynormalSeveritynormalReproducibilityhave not tried
Status resolvedResolutionfixed 
Product VersionTDM 2.05 
Target VersionTDM 2.06Fixed in VersionTDM 2.06 
Summary0004493: Some physics events have changed in 2.05 due to faster fps
DescriptionSee if there are physics events that are tied specifically to fps, and see if they can be switched to being time-based, which should be a constant across fps differences.
TagsNo tags attached.
Attached Files
falltest.zip (7,039 bytes)
proofofconcept-pushentityfix.txt (4,043 bytes)   
Index: game/physics/Physics_Player.cpp
===================================================================
--- game/physics/Physics_Player.cpp	(Revision 6945)
+++ game/physics/Physics_Player.cpp	(Arbeitskopie)
@@ -283,6 +283,63 @@
 	planes[numplanes].Normalize();
 	numplanes++;
 
+	// BluePill : Push test tweaked for low frametimes (Proof of Concept, relies on hardcoded values)
+	// needs further testing for various framerates (including < 60fps) and whether it can negatively affect mission gameplay
+	if ( push && current.velocity.LengthSqr() > 0.0F )
+	{
+		// calculate the "real time" velocity as if the framerate was constantly 60fps
+		// idPhysics_RigidBody::Evaluate (-> idODE_*::Evaluate) already takes account of the frametime
+		// just applying current.velocity would cause the actual movement to be velocity*frametime� per frame, while it should be velocity*frametime.
+		idVec3 realtimeVelocity = current.velocity;
+		// prevent potential division by zero (minimum frametime should be 1ms)
+		if (frametime > 0.0001F)
+			realtimeVelocity *= (0.0166667f / frametime);
+
+		// BluePill : Hardcoded trace range of 15, need to test for different crouch/walk speeds
+		// calculate the end of the reachable range to push entities
+		end = current.origin + current.velocity * ( 15.0F / current.velocity.LengthFast() );
+
+		// see if we can make it there
+		gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
+
+		// if we are not blocked by the world
+		if ( trace.c.entityNum != ENTITYNUM_WORLD ) {
+
+			clipModel->SetPosition( current.origin, clipModel->GetAxis() );
+
+			// clip movement, only push idMoveables, don't push entities the player is standing on
+			// apply impact to pushed objects
+			pushFlags = PUSHFL_CLIP | PUSHFL_ONLYMOVEABLE | PUSHFL_NOGROUNDENTITIES | PUSHFL_APPLYIMPULSE;
+
+			// clip & push
+
+			// greebo: Don't use the idPusher
+			//totalMass = gameLocal.push.ClipTranslationalPush( trace, self, pushFlags, end, end - current.origin, cv_pm_pushmod.GetFloat() );
+
+			// Set the trace result to zero, we're pushing into things here
+			// trace.fraction = 0.0f;
+			trace.endpos = current.origin;
+
+			// greebo: Check the entity in front of us
+			idEntity* pushedEnt = gameLocal.entities[trace.c.entityNum];
+			if (pushedEnt != NULL)
+			{
+				// Register the blocking physics object with our push force
+				m_PushForce->SetPushEntity(pushedEnt, 0);
+				m_PushForce->SetContactInfo(trace, realtimeVelocity);
+
+				totalMass = pushedEnt->GetPhysics()->GetMass();
+			}
+
+			if (totalMass > 0.0f) {
+				// decrease velocity based on the total mass of the objects being pushed ?
+				current.velocity *= 1.0f - idMath::ClampFloat(0.0f, 1000.0f, totalMass - 20.0f) * (1.0f / 950.0f);
+
+				pushed = true;
+			}
+		}
+	}
+
 	for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ )
 	{
 		// calculate position we are trying to move to
@@ -391,7 +448,8 @@
 			}
 		}
 
-		// if we can push other entities and not blocked by the world
+		// original push test code; the new push test is located above this loop
+		/*// if we can push other entities and not blocked by the world
 		if ( push && trace.c.entityNum != ENTITYNUM_WORLD ) {
 
 			clipModel->SetPosition( current.origin, clipModel->GetAxis() );
@@ -401,7 +459,7 @@
 			pushFlags = PUSHFL_CLIP | PUSHFL_ONLYMOVEABLE | PUSHFL_NOGROUNDENTITIES | PUSHFL_APPLYIMPULSE;
 
 			// clip & push
-			
+
 			// greebo: Don't use the idPusher
 			//totalMass = gameLocal.push.ClipTranslationalPush( trace, self, pushFlags, end, end - current.origin, cv_pm_pushmod.GetFloat() );
 
@@ -425,7 +483,7 @@
 				current.velocity *= 1.0f - idMath::ClampFloat( 0.0f, 1000.0f, totalMass - 20.0f ) * ( 1.0f / 950.0f );
 				pushed = true;
 			}
-	
+
 			current.origin = trace.endpos;
 			time_left -= time_left * trace.fraction;
 
@@ -433,7 +491,7 @@
 			if ( trace.fraction >= 1.0f ) {
 				break;
 			}
-		}
+		}*/
 
 		if ( !stepped ) {
 			// let the entity know about the collision
crouchsmoothfix.txt (957 bytes)   
Index: game/Player.cpp
===================================================================
--- game/Player.cpp	(Revision 6945)
+++ game/Player.cpp	(Arbeitskopie)
@@ -7027,7 +7027,14 @@
 			SetEyeHeight( newEyeOffset );
 		} else {
 			// smooth out duck height changes
-			SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
+
+			// smoothing takes a constant amount of frames (and a constant minimum time if com_fixedTic == 0)
+			// SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
+
+			// smoothing in a constant duration 
+			// multiply the crouchrate by a frametime factor (frametime 1/30 => 2, 1/60 => 1, 1/120 => 0.5, ...)
+			float crouchrate = (1.0f - pm_crouchrate.GetFloat()) * ((1.0f * gameLocal.getMsec()) / gameLocal.GetMSec());
+			SetEyeHeight(EyeHeight() * (1 - crouchrate) + newEyeOffset * crouchrate);
 		}
 	}
 
crouchsmoothfix.txt (957 bytes)   
movespeed_cratepush_grabentity_betafixes.txt (12,884 bytes)   
Index: game/physics/Physics_Player.cpp
===================================================================
--- game/physics/Physics_Player.cpp	(Revision 6950)
+++ game/physics/Physics_Player.cpp	(Arbeitskopie)
@@ -199,18 +199,19 @@
 void idPhysics_Player::Accelerate( const idVec3 &wishdir, const float wishspeed, const float accel ) {
 #if 1
 	// q2 style
-	float addspeed, accelspeed, currentspeed;
+	//float addspeed;
+	float accelspeed, currentspeed;
 
 	currentspeed = current.velocity * wishdir;
-	addspeed = wishspeed - currentspeed;
+	/*addspeed = wishspeed - currentspeed;
 	if (addspeed <= 0) {
 		return;
-	}
-	accelspeed = accel * frametime * wishspeed;
-	if (accelspeed > addspeed) {
+	}*/
+	// this acceleration has friction-like effects if currentspeed is bigger than wishspeed.
+	accelspeed = accel * frametime * (wishspeed - currentspeed); // accel * frametime * wishspeed;
+	/*if (accelspeed > addspeed) {
 		accelspeed = addspeed;
-	}
-	
+	}*/
 	current.velocity += accelspeed * wishdir;
 #else
 	// proper way (avoids strafe jump maxspeed bug), but feels bad
@@ -283,6 +284,63 @@
 	planes[numplanes].Normalize();
 	numplanes++;
 
+	// BluePill : Push test tweaked for low frametimes (relies on hardcoded values)
+	// needs further testing whether it can negatively affect mission gameplay
+	if ( push && current.velocity.LengthSqr() > 0.0F )
+	{
+		// calculate the "real time" velocity as if the framerate was constantly 60fps
+		// idPhysics_RigidBody::Evaluate (-> idODE_*::Evaluate) already takes account of the frametime
+		// just applying current.velocity would cause the actual movement to be velocity*frametime� per frame, while it should be velocity*frametime.
+		idVec3 realtimeVelocity = current.velocity;
+		// prevent potential division by zero (minimum frametime should be 1ms)
+		if (frametime > 0.0001F)
+			realtimeVelocity *= (MS2SEC(USERCMD_MSEC) / frametime);
+
+		// BluePill : Hardcoded trace range of 5.0, looked good for different crouch/walk speeds
+		// calculate the end of the reachable range to push entities
+		end = current.origin + current.velocity * ( 5.0F / current.velocity.LengthFast() );
+
+		// see if we can make it there
+		gameLocal.clip.Translation( trace, current.origin, end, clipModel, clipModel->GetAxis(), clipMask, self );
+
+		// if we are not blocked by the world
+		if ( trace.c.entityNum != ENTITYNUM_WORLD ) {
+
+			clipModel->SetPosition( current.origin, clipModel->GetAxis() );
+
+			// clip movement, only push idMoveables, don't push entities the player is standing on
+			// apply impact to pushed objects
+			pushFlags = PUSHFL_CLIP | PUSHFL_ONLYMOVEABLE | PUSHFL_NOGROUNDENTITIES | PUSHFL_APPLYIMPULSE;
+
+			// clip & push
+
+			// greebo: Don't use the idPusher
+			//totalMass = gameLocal.push.ClipTranslationalPush( trace, self, pushFlags, end, end - current.origin, cv_pm_pushmod.GetFloat() );
+
+			// Set the trace result to zero, we're pushing into things here
+			// trace.fraction = 0.0f;
+			trace.endpos = current.origin;
+
+			// greebo: Check the entity in front of us
+			idEntity* pushedEnt = gameLocal.entities[trace.c.entityNum];
+			if (pushedEnt != NULL)
+			{
+				// Register the blocking physics object with our push force
+				m_PushForce->SetPushEntity(pushedEnt, 0);
+				m_PushForce->SetContactInfo(trace, realtimeVelocity);
+
+				totalMass = pushedEnt->GetPhysics()->GetMass();
+			}
+
+			if (totalMass > 0.0f) {
+				// decrease velocity based on the total mass of the objects being pushed ?
+				current.velocity *= 1.0f - idMath::ClampFloat(0.0f, 1000.0f, totalMass - 20.0f) * (1.0f / 950.0f);
+
+				pushed = true;
+			}
+		}
+	}
+
 	for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ )
 	{
 		// calculate position we are trying to move to
@@ -391,7 +449,8 @@
 			}
 		}
 
-		// if we can push other entities and not blocked by the world
+		// original push test code; the new push test is located above this loop
+		/*// if we can push other entities and not blocked by the world
 		if ( push && trace.c.entityNum != ENTITYNUM_WORLD ) {
 
 			clipModel->SetPosition( current.origin, clipModel->GetAxis() );
@@ -401,7 +460,7 @@
 			pushFlags = PUSHFL_CLIP | PUSHFL_ONLYMOVEABLE | PUSHFL_NOGROUNDENTITIES | PUSHFL_APPLYIMPULSE;
 
 			// clip & push
-			
+
 			// greebo: Don't use the idPusher
 			//totalMass = gameLocal.push.ClipTranslationalPush( trace, self, pushFlags, end, end - current.origin, cv_pm_pushmod.GetFloat() );
 
@@ -425,7 +484,7 @@
 				current.velocity *= 1.0f - idMath::ClampFloat( 0.0f, 1000.0f, totalMass - 20.0f ) * ( 1.0f / 950.0f );
 				pushed = true;
 			}
-	
+
 			current.origin = trace.endpos;
 			time_left -= time_left * trace.fraction;
 
@@ -433,7 +492,7 @@
 			if ( trace.fraction >= 1.0f ) {
 				break;
 			}
-		}
+		}*/
 
 		if ( !stepped ) {
 			// let the entity know about the collision
@@ -571,7 +630,7 @@
 Handles both ground friction and water friction
 ==================
 */
-void idPhysics_Player::Friction( void )
+void idPhysics_Player::Friction( const idVec3 &wishdir, const float forceFriction )
 {
 	idVec3 vel = current.velocity;
 
@@ -595,11 +654,14 @@
 		return;
 	}
 
-	float drop = 0;
+	// float drop = 0;
+	float friction = 0.0f;
 
 	// spectator friction
 	if ( current.movementType == PM_SPECTATOR ) {
-		drop += speed * PM_FLYFRICTION * frametime;
+		// TODO if anyone is crazy enough to add multiplayer and spectator mode to TDM : Check whether this works as intented!
+		friction = PM_FLYFRICTION;
+		//drop += speed * PM_FLYFRICTION * frametime;
 	}
 	// apply ground friction
 	else if ( walking && waterLevel <= WATERLEVEL_FEET )
@@ -608,30 +670,43 @@
 		{
 			// grayman - #2409 - less friction on slick surfaces
 
-			float friction = PM_FRICTION; // default
+			friction = PM_FRICTION; // default
 			if (groundMaterial && (groundMaterial->IsSlick()))
 			{
 				friction *= PM_SLICK; // reduce friction
 			}
-			float control = (speed < PM_STOPSPEED) ? PM_STOPSPEED : speed;
-			drop += control * friction * frametime;
+
+			/*float control = (speed < PM_STOPSPEED) ? PM_STOPSPEED : speed;
+			drop += control * friction * frametime;*/
 		}
 	}
 	// apply water friction even if just wading
-	else if ( waterLevel ) {
-		drop += speed * PM_WATERFRICTION * waterLevel * frametime;
+	else if (waterLevel) {
+		friction = PM_WATERFRICTION * waterLevel;
+		//drop += speed * PM_WATERFRICTION * waterLevel * frametime;
 	}
 	// apply air friction
 	else {
-		drop += speed * PM_AIRFRICTION * frametime;
+		friction = PM_AIRFRICTION;
+		//drop += speed * PM_AIRFRICTION * frametime;
 	}
+	if (forceFriction > 0.0f)
+		friction = forceFriction;
 
+	// bluepill: don't apply friction to the current acceleration direction as the acceleration calculation does that already.
+	// don't set drop as this friction calculation doesn't treat all velocity components equally
+	idVec3 frictionComponent = vel - ((vel * wishdir) * wishdir);
+	if (frictionComponent.LengthSqr() <= 1.5625f) // Length() <= 1.25f; value seems good enough
+		current.velocity -= frictionComponent;
+	else
+		current.velocity += frictionComponent * (friction * frametime * (0.0f - 1.0f)); // -1.0 for 100% frictionComponent
+	
 	// scale the velocity
-	float newspeed = speed - drop;
+	/*float newspeed = speed - drop;
 	if (newspeed < 0) {
 		newspeed = 0;
 	}
-	current.velocity *= ( newspeed / speed );
+	current.velocity *= ( newspeed / speed );*/
 }
 
 /*
@@ -665,7 +740,7 @@
 	// Lower ranged weapons while swimming
 	static_cast<idPlayer*>(self)->SetImmobilization( "WaterMove", EIM_ATTACK_RANGED );
 
-	Friction();
+	//Friction();
 
 	float scale = CmdScale( command );
 
@@ -711,6 +786,9 @@
 
 	idPhysics_Player::Accelerate( wishdir, wishspeed, PM_WATERACCELERATE );
 
+	// don't apply friction on the wishdir direction
+	idPhysics_Player::Friction( wishdir );
+
 	// make sure we can go up slopes easily under water
 	if ( groundPlane && ( current.velocity * groundTrace.c.normal ) < 0.0f ) {
 		float vel = current.velocity.Length();
@@ -736,7 +814,7 @@
 	float	scale;
 
 	// normal slowdown
-	idPhysics_Player::Friction();
+	//idPhysics_Player::Friction();
 
 	scale = idPhysics_Player::CmdScale( command );
 
@@ -752,6 +830,9 @@
 
 	idPhysics_Player::Accelerate( wishdir, wishspeed, PM_FLYACCELERATE );
 
+	// don't apply friction on the wishdir direction
+	idPhysics_Player::Friction( wishdir );
+
 	idPhysics_Player::SlideMove( false, false, false, false );
 }
 
@@ -766,7 +847,7 @@
 	float		wishspeed;
 	float		scale;
 
-	idPhysics_Player::Friction();
+	//idPhysics_Player::Friction();
 
 	scale = idPhysics_Player::CmdScale( command );
 
@@ -784,6 +865,9 @@
 
 	// not on ground, so little effect on velocity
 	idPhysics_Player::Accelerate( wishdir, wishspeed, PM_AIRACCELERATE );
+	
+	// don't apply friction on the wishdir direction
+	idPhysics_Player::Friction( wishdir );
 
 	// we may have a ground plane that is very steep, even
 	// though we don't have a groundentity
@@ -820,7 +904,8 @@
 		return;
 	}
 
-	Friction();
+	// BluePill : Move friction calculation after acceleration.
+	//Friction();
 
 	float scale = CmdScale( command );
 
@@ -868,6 +953,7 @@
 		}
 	}
 
+
 	Accelerate( wishdir, wishspeed, accelerate );
 
 	if ( /*( groundMaterial && groundMaterial->IsSlick() ) || grayman #2409 */ current.movementFlags & PMF_TIME_KNOCKBACK )
@@ -875,6 +961,9 @@
 		current.velocity += gravityVector * frametime;
 	}
 
+	// BluePill : don't apply friction on the wishdir direction, Accelerate does that already.
+	Friction( wishdir );
+
 	idVec3 oldVelocity = current.velocity;
 
 	// slide along the ground plane
@@ -942,12 +1031,12 @@
 ===============
 */
 void idPhysics_Player::NoclipMove( void ) {
-	float		speed, drop, friction, newspeed, stopspeed;
+	//float		speed, drop, friction, newspeed, stopspeed;
 	float		scale, wishspeed;
 	idVec3		wishdir;
 
 	// friction
-	speed = current.velocity.Length();
+	/*speed = current.velocity.Length();
 	if ( speed < 20.0f ) {
 		current.velocity = vec3_origin;
 	}
@@ -966,7 +1055,7 @@
 		}
 
 		current.velocity *= newspeed / speed;
-	}
+	}*/
 
 	// accelerate
 	scale = idPhysics_Player::CmdScale( command );
@@ -978,6 +1067,9 @@
 
 	idPhysics_Player::Accelerate( wishdir, wishspeed, PM_ACCELERATE );
 
+	// don't apply friction on the wishdir direction
+	idPhysics_Player::Friction( wishdir, PM_NOCLIPFRICTION );
+
 	// move
 	current.origin += frametime * current.velocity;
 }
@@ -997,7 +1089,7 @@
 
 	// fly movement
 
-	idPhysics_Player::Friction();
+	// idPhysics_Player::Friction();
 
 	scale = idPhysics_Player::CmdScale( command );
 
@@ -1011,6 +1103,9 @@
 	wishspeed = wishdir.Normalize();
 
 	idPhysics_Player::Accelerate( wishdir, wishspeed, PM_FLYACCELERATE );
+	
+	// don't apply friction on the wishdir direction
+	idPhysics_Player::Friction( wishdir );
 
 	idPhysics_Player::SlideMove( false, false, false, false );
 }
@@ -1620,12 +1715,15 @@
 	// ========================== End Surface Extent Test ==================
 
 	// do strafe friction
-	idPhysics_Player::Friction();
+	// idPhysics_Player::Friction();
 
 	// accelerate
 	wishspeed = wishvel.Normalize();
 	idPhysics_Player::Accelerate( wishvel, wishspeed, accel );
 
+	// don't apply friction on the wishdir direction
+	idPhysics_Player::Friction( wishdir );
+
 	// cap the vertical travel velocity
 	upscale = current.velocity * -gravityNormal;
 	if ( upscale < -m_ClimbMaxVelVert * playerSpeed )
Index: game/physics/Physics_Player.h
===================================================================
--- game/physics/Physics_Player.h	(Revision 6950)
+++ game/physics/Physics_Player.h	(Arbeitskopie)
@@ -399,7 +399,7 @@
 	float					CmdScale( const usercmd_t &cmd ) const;
 	void					Accelerate( const idVec3 &wishdir, const float wishspeed, const float accel );
 	bool					SlideMove( bool gravity, bool stepUp, bool stepDown, bool push );
-	void					Friction( void );
+	void					Friction( const idVec3 &wishdir = idVec3(), const float forceFriction = -1 );
 	void					WaterMove( void );
 	void					FlyMove( void );
 	void					AirMove( void );
Index: game/Force_Grab.cpp
===================================================================
--- game/Force_Grab.cpp	(Revision 6950)
+++ game/Force_Grab.cpp	(Arbeitskopie)
@@ -215,8 +215,9 @@
 
 	dir1 = m_dragPosition - dragOrigin;
 	l1 = dir1.Normalize();
-	//dT =  MS2SEC( USERCMD_MSEC ); // time elapsed is time between user mouse commands
-	dT = MS2SEC(gameLocal.getMsec()); // duzenko 4409: fixed tick + USERCMD_MSEC -> flickering
+	// BluePill : reverted to USERCMD_MSEC because the frametime is already considered by idPhysics_*::Evaluate of the grabbed entity.
+	dT =  MS2SEC( USERCMD_MSEC ); // time elapsed is time between user mouse commands
+	//dT = MS2SEC(gameLocal.getMsec()); // duzenko 4409: fixed tick + USERCMD_MSEC -> flickering  
 
 	if( !m_bApplyDamping )
 		m_damping = 0.0f;
grabentity_dropbody_betafixes.txt (2,842 bytes)   
Index: game/Force_Grab.cpp
===================================================================
--- game/Force_Grab.cpp	(Revision 6950)
+++ game/Force_Grab.cpp	(Arbeitskopie)
@@ -215,8 +215,9 @@
 
 	dir1 = m_dragPosition - dragOrigin;
 	l1 = dir1.Normalize();
-	//dT =  MS2SEC( USERCMD_MSEC ); // time elapsed is time between user mouse commands
 	dT = MS2SEC(gameLocal.getMsec()); // duzenko 4409: fixed tick + USERCMD_MSEC -> flickering
+	if (dT < MS2SEC( USERCMD_MSEC )) // BluePill : Fix grab speed for higher framerates
+		dT =  MS2SEC( USERCMD_MSEC ); // time elapsed is time between user mouse commands
 
 	if( !m_bApplyDamping )
 		m_damping = 0.0f;
Index: game/physics/Physics_AF.cpp
===================================================================
--- game/physics/Physics_AF.cpp	(Revision 6950)
+++ game/physics/Physics_AF.cpp	(Arbeitskopie)
@@ -5491,6 +5491,7 @@
 	idVec6 force;
 	idRotation rotation;
 	float vSqr, maxLinearVelocity, maxAngularVelocity;
+	float frictionTickMul;
 	
 	maxLinearVelocity = af_maxLinearVelocity.GetFloat() / timeStep;
 	maxAngularVelocity = af_maxAngularVelocity.GetFloat() / timeStep;
@@ -5522,6 +5523,9 @@
 	// make absolutely sure all contact constraints are satisfied
 	VerifyContactConstraints();
 
+	// make friction independent of the frametime (i.e. the time between two calls of this function)
+	frictionTickMul = timeStep / MS2SEC(gameLocal.GetMSec());
+
 	// calculate the position of the bodies for the next physics state
 	for ( i = 0; i < bodies.Num(); i++ ) {
 		body = bodies[i];
@@ -5544,15 +5548,15 @@
 		// apply a higher friction value if the AF is underwater
 		waterLevel = body->GetWaterLevel();
 		if( waterLevel == 0.0f || this->water == NULL ) {
-			body->next->spatialVelocity.SubVec3(0) -= body->linearFriction * body->next->spatialVelocity.SubVec3(0);
+			body->next->spatialVelocity.SubVec3(0) -= frictionTickMul * body->linearFriction * body->next->spatialVelocity.SubVec3(0);
 		}
 		else {
-			body->next->spatialVelocity.SubVec3(0) -= (body->linearFriction * (this->water->GetViscosity()+WATER_FRICTION) * waterLevel) * body->next->spatialVelocity.SubVec3(0);
+			body->next->spatialVelocity.SubVec3(0) -= frictionTickMul * (body->linearFriction * (this->water->GetViscosity() + WATER_FRICTION) * waterLevel) * body->next->spatialVelocity.SubVec3(0);
 		}
 #else
-		body->next->spatialVelocity.SubVec3(0) -= body->linearFriction * body->next->spatialVelocity.SubVec3(0);
+		body->next->spatialVelocity.SubVec3(0) -= frictionTickMul * body->linearFriction * body->next->spatialVelocity.SubVec3(0);
 #endif	
-		body->next->spatialVelocity.SubVec3(1) -= body->angularFriction * body->next->spatialVelocity.SubVec3(1);
+		body->next->spatialVelocity.SubVec3(1) -= frictionTickMul * body->angularFriction * body->next->spatialVelocity.SubVec3(1);
 	}
 }
 

Relationships

related to 0003706 resolvedgrayman Vine arrow bounces around 
parent of 0004552 resolvednbohr1more Slide when coming to a stop 
parent of 0004924 resolvedstgatilov Rope physics mad when FPS is low and uncapped 
parent of 0004983 closed AIs randomly dying with uncapped and low FPS 
parent of 0004426 resolvedduzenko Grabber items vibrating 
parent of 0004696 resolvedstgatilov At high FPS player footsteps don't always play 
parent of 0004741 resolvedgrayman Possible stutter in player movement 
parent of 0005666 confirmed Shooting a noise arrow then picking it up, sound keeps playing - at HIGH FPS 
related to 0004865 resolvedcabalistic Improve the way how FPS cap works (uncapped FPS) 
related to 0005464 resolvednbohr1more TDM 2.08-2.09 beta 1: uncapped FPS causing weird player physics 
related to 0006333 resolvedstgatilov Jumping player sometimes pauses at top on very high FPS 
child of 0004409 resolvedduzenko Add an option to lift the 60 fps lock 
Not all the children of this issue are yet resolved or closed.

Activities

AluminumHaste

AluminumHaste

11.03.2017 16:38

developer   ~0008763

Okay the crate test is confirmed and it's significant.

With fps at 60 fps max, the smallest crate moves quickly and easily.

At 300 fps, it's slooooooooooooooooooow.

 

Find test map attached

 

Steps:

Open TDM

map falltest.map

 

move smallest crate around.

 

Set com_fixedTic 1

vid_restart (not sure if this is necessary)

 

Try moving small crate again.
AluminumHaste

AluminumHaste

13.03.2017 01:01

developer   ~0008764

Another issue is the player crouching and standing, those animations can play a lot faster.
BluePill

BluePill

19.06.2017 14:09

reporter   ~0008916

Last edited: 19.06.2017 14:09

Dragging/carrying as well as throwing entities is affected, too.
Crouching slowly is much slower with high fps (at least 400+), everything else (crouching normally, crouching fast, walking slow, etc.) seems to work fine.

I've attached a PoC fix for the push crate issue.
It still needs to be tested in actual missions as well as with different walk speeds and light items that can be kicked away.
At least it seemed to fix that issue in the falltest map.

The player velocity value is not per second but per frame (at least that's my observation) and is used as the push velocity.
idPhysics_RigidBody::Evaluate also applies frametime as a velocity factor, causing the distance per frame to be in the order of velocity*frametime².
The fix removes the first frametime factor in the push velocity using a constant frametime of 1/60.
Another issue was that the entity was only pushed every ~6th frame because the trace distance used to check for entities to push was too small (lower frametime -> lower velocity -> lower trace distance).
The fix uses a constant distance of 15 units.

BluePill

BluePill

19.06.2017 20:12

reporter   ~0008917

Added a fix for crouch smoothing.

The grab speed basically has the same issue as pushing entities. It can be fixed by uncommenting "dT = MS2SEC( USERCMD_MSEC );" (Force_Grab.cpp line 218 in rev. 6946) and commenting the following line but according to duzenko's comment, it'd cause flickering. I haven't seen such flickering but I'm not sure about low framerates.

The throwing range seems to be a non-issue since it only halves from 60 to 500 fps.
AluminumHaste

AluminumHaste

19.06.2017 20:25

developer   ~0008918

Ideally all this should be moved to time based instead of gametic, so even at 1 fps throwing an object would go as far as at 3000fps.
BluePill

BluePill

19.06.2017 23:58

reporter   ~0008919

The actual problem for the player velocity while crouching slowly seems to be friction.
While idPhysics_Player::Accelerate accelerates less per frame with a lower frametime to keep the same acceleration over real time, idPhysics_Player::Friction always resets the velocity in the next frame because of the small creep acceleration.
At normal framerates, the same thing also occurs but the "temporary" velocity in that frame caused by acceleration is higher (though at 60fps that's still below 1/2 of the maximum slow creep speed).
The friction calculation likely needs a more physically correct approach, at least to determine whether static friction is overcome, in which case it should not "eat away" all the velocity.
It needs to take acceleration (or accelerating force) into account for that, currently it only uses the current velocity.
It might also be necessary (and better?) to move the friction calculation directly after the acceleration.
nbohr1more

nbohr1more

20.06.2017 05:20

developer   ~0008920

Very nice!

A few testing notes:

KO'd or Killed (Ragdoll) AI still float down in slow motion when FPS is very high.

"noclip" mode still has slow movement or freezing at high FPS.
BluePill

BluePill

20.06.2017 23:52

reporter   ~0008921

Last edited: 21.06.2017 00:02

Attached movespeed_cratepush_grabentity_betafixes.txt, which overrides proofofconcept-pushentityfix.txt.

Replaced acceleration as well as friction in idPhysics_Player with a smoothing calculation that takes the frametime into account. I've first tried to fix the old algorithms but wasn't able to find a way to make slow movement equal for different framerates.
[Edit : The new Friction function doesn't touch the velocity in acceleration direction since the Acceleration calculation automatically "accelerates backwards" when the player is too fast. It only applies friction to all directions when the player doesn't accelerate (no move buttons pressed).]
I've refined the crate push distance a bit and used USERCMD_MSEC instead of a hardcoded 1/60 reference frametime.
The grab entity fix that reverts someone else's change is also included, I included further "investigation" of that in the ToDo-list below.

It looks good so far (tested LadderMove, NoclipMove, WalkMove and WaterMove; not sure how to test AirMove and FlyMove), using the original friction constants with the new function.
One map that definitely needs further testing with this is Swing. At least the start where one has to jump on small swings seemed different to me than with TDM 2.05.
Other than that, I couldn't find any issues with the changes. I couldn't find differences in 20/30/40/50/60fps and above, except that pushing crates feels less smooth on lower framerates due to the crates moving away from the player by a higher distance per frame (didn't look like a real issue though).

Current ToDo-list :
- Compare different movement speeds before and after the fixes since the previous movement modifiers are still used in a different calculation.
- Take a look at AI ragdolls (fall, grab and throw).
- Take a look at the entity throwing range.
- Test whether steep slopes still work as intended.
- Test the effects of all changes in actual missions.
- Find out more about duzenko's comment in Force_Grab.cpp ("fixed tick + USERCMD_MSEC -> flickering"); grabbing seems fine even with that change reverted.

nbohr1more

nbohr1more

21.06.2017 01:32

developer   ~0008922

Last edited: 21.06.2017 01:34

The Force_Grab change is almost certainly:

http://bugs.thedarkmod.com/view.php?id=4426

nbohr1more

nbohr1more

21.06.2017 02:39

developer   ~0008923

Hmm, movespeed_cratepush_grabentity_betafixes.txt doesn't require proofofconcept-pushentityfix.txt does it?

I applied the crouch fix and your latest betafix and all seemed to be working well but then I shot an arrow at an AI in noclip mode and the game crashed to Desktop.
BluePill

BluePill

27.06.2017 19:34

reporter   ~0008933

Last edited: 27.06.2017 19:35

Attached grabentity_dropbody_betafixes.txt.
For all fixes, apply movespeed_cratepush_grabentity_betafixes.txt without the Force_Grab.cpp changes and then this patch.

The frame time used for the grab speed now is limited to the default 16ms tick but the actual frame time is used for anything above that.
That should prevent flickering at low frame rates and the slowdown at high frame rates.

I've also added a frame time dependant factor to the body friction calculation in Physics_AF.cpp that should fix the slow motion ragdoll issue.

duzenko

duzenko

27.06.2017 20:07

developer   ~0008934

Applied to trunk, didn't test.
However I think limiting grab speed on low fps could lead to slow movement?
(Mortal Kombat Kompete behaves like that)
nbohr1more

nbohr1more

27.06.2017 20:39

developer   ~0008935

Thanks!

If this works out, I guess the last remaining issue with uncapped FPS is audio timing issues. I think they are limited to triggered events but I haven't even begun untangling that side of things.
grayman

grayman

28.06.2017 12:28

viewer   ~0008937

Last edited: 28.06.2017 12:29

One other area I'm concerned about: interleaved thinking.

The amount of frames between each time an AI thinks is governed by the AI spawnarg "max_interleave_think_frames". Each AI uses "idAI::GetThinkInterleave()" to determine the next frame where it should think. Increasing the frames between thinking events improves performance.

See http://wiki.thedarkmod.com/index.php?title=Interleaved_Thinking_optimization

With increased frame rates, this might now need to be a time-based measure, rather than a frame-based measure.

---------------------------------------------

In general, how does increased frame rate affect cycles like general entity thinking, frame-based events, etc.?

Perhaps researching anywhere gameLocal.framenum is used might show other areas that need to become time-based rather than frame-based.

AluminumHaste

AluminumHaste

02.07.2017 02:40

developer   ~0008940

When I run TheDarkMod.exe, I noticed that when running then trying to stop, there's now a small slide at the end.

If I use TheDarkModTools.exe, there's no sliding, which is how it works in TDM 2.05.

I tried with fixedTic off and on, and at 60 and 144 fps. Behaviour is the same, but the sliding might be more pronounced at 144 fps.

Video:
https://www.youtube.com/watch?v=bvL6f7F_Xsc

First part is TheDarkMod.exe, second part is TheDarkModTools.exe
grayman

grayman

02.07.2017 03:17

viewer   ~0008941

2.05 works correctly: when walking and stopping, there's a short slide at the end, and when running and stopping, there's a longer slide at the end.

Testing with the latest SVN (@60fps), the behavior is the same. My sense is that the sliding might take slightly longer to stop, but it's still reasonable behavior.

There have been no changes to the sliding code to make it behave differently since I corrected it in 1.03, 6.5 years ago.

It's possible that any changes for different fps settings might have affected the way friction works, which might account for differences you're seeing. If a different FPS setting makes sliding behave WAY differently than the 2.05 behavior, then adjustments are needed to bring it back in line with 2.05.
duzenko

duzenko

02.07.2017 07:33

developer   ~0008945

I don't see major changes with stopping either. And I think it makes sense to stop in a fraction of second rather than immediately when running?
grayman

grayman

02.07.2017 12:17

viewer   ~0008947

In 2.05 ...

A walking slide stops in about a second.

A running slide stops in about 2.5 seconds.

2.06 should do the same.
AluminumHaste

AluminumHaste

02.07.2017 17:27

developer   ~0008950

Just wanted to clarify; the sliding on stop is on other surfaces such as wood or stone.
In 2.04 and 2.05 you stop almost immediately.
In SVN TheDarkMod.exe, there's a slide before coming to a stop.
No other EXE is affected (TheDarkModTools.exe, TheDarkModx64.exe, TheDarkModx64Tools.exe)
BluePill

BluePill

03.07.2017 02:33

reporter   ~0008957

Last edited: 03.07.2017 02:33

I've added a patch in the child issue 4552 that should solve the sliding issue.
While the new sliding duration is higher (except for running) the difference should be minimal since the sliding speed goes toward zero during that time frame.

Another difference from 2.05 is that slow crouch / creep speed is higher because the new friction doesn't longer prevent reaching the wishspeed of around 16.6 instead of 8, meaning that it might be necessary to add another movement multiplier for that case.
Other movement types aren't affected.

Springheel

Springheel

09.08.2017 15:02

administrator   ~0009069

"Underwater air time". This is accelerated with uncapped FPS.
nbohr1more

nbohr1more

27.08.2017 05:00

developer   ~0009117

In SVN, even though the FPS is uncapped at either setting com_fixedTic 0 fixes the "underwater air time" issue.
Springheel

Springheel

10.09.2017 15:05

administrator   ~0009206

Don't know if it's related to this or not, but my mouse sensitivity slider has changed completely compared to 2.05. There are 8 levels, and anything above two is so fast now that it's hard to select anything.
duzenko

duzenko

11.09.2017 07:29

developer   ~0009207

Last edited: 11.09.2017 07:43

What is the value of the sensitivity cvar in console when you move the slider to the middle position?
It was recently changed from int to float to allow for modern high-dpi mice.

Springheel

Springheel

11.09.2017 21:35

administrator   ~0009208

"What is the value of the sensitivity cvar in console"

How would I find that?
nbohr1more

nbohr1more

11.09.2017 22:01

developer   ~0009209

UsercmdGen.cpp

idCVar idUsercmdGenLocal::sensitivity( "sensitivity", "5", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_FLOAT, "mouse view sensitivity" );

sensitivity = 5 (default)
nbohr1more

nbohr1more

12.09.2017 00:32

developer   ~0009210

Another bug specific to com_fixedTic 1

Mission: Swing

1) Climb onto the first ledge from the ladder
2) When fully upright, jump
3) Note that you will hover as if your head is stuck in the ceiling

This does not happen in other modes.
nbohr1more

nbohr1more

12.09.2017 03:17

developer   ~0009211

And another:

Mission: The Alchemist

With com_fixedTic 1, simply crouch-walking from the street to the side-walk fails. You must jump or mantle onto the sidewalk even though the curb is very shallow.

com_fixedTic 0 solves this even with uncapped FPS via com_smp 1.
duzenko

duzenko

12.09.2017 10:41

developer   ~0009212

Fixed the underwater air time (svn rev 7131) - needs testing.
I think the mouse slider is 1 to 30 with step of 0.5? Any way it makes sense to create a separate bug.
duzenko

duzenko

13.09.2017 15:52

developer   ~0009213

Sidewalk climb.
I think the problem here is that when moving in small steps there's a chance player kinda puts his foot on the curb side which causes him to slide down and back. Please not that the curb side is not exactly vertical but very steep slope.
Unless someone can make out how to adopt the existing code to such situations I'm going to go ahead and just skip frames here which is a bummer...
Might also fix the Swing issues though I was never able to repeat that.
nbohr1more

nbohr1more

14.09.2017 01:31

developer   ~0009218

Last edited: 14.09.2017 02:40

Swing no longer exhibits the stuck-head problem but now has a new issue...
Landing on the moving platform causes you to wildly jump positions and it's nearly impossible to get a proper landing. com_fixedTic 0 solves that.

Edit: Climbing stairs while crouched causes lots of hopping motion too.

duzenko

duzenko

14.09.2017 09:30

developer   ~0009230

Last edited: 14.09.2017 09:32

Try rev 7137
Do we have more slope surfaces to test? Like http://www.valdisole.net/upload/cms/Piste_a_Folgarida_Marilleva.jpg

nbohr1more

nbohr1more

15.09.2017 01:10

developer   ~0009242

"The Rift" is full of slopes as I recall...

Rev 7137 fixes the platform jumping issue in "Swing" and cures the hopping motion while climbing stairs in "Briarwood Manor".

Unfortunately, the head-getting-stuck issue has returned to "Swing" but it is pretty brief and is not infuriating like the curb issue in "The Alchemist". It doesn't break the mission, eg. low priority IMHO.
nbohr1more

nbohr1more

05.10.2017 12:52

developer   ~0009393

I tested a recent build and the none of the issues were seen in Swing.
I'll retest tonight.
stgatilov

stgatilov

05.12.2019 08:48

administrator   ~0011890

I think it worth noting here that a major change was committed for uncapped FPS mode in svn rev 8435.
See related issue 0004924, or more specifically:
  https://bugs.thedarkmod.com/view.php?id=4924#c11885
And the forums:
  http://forums.thedarkmod.com/index.php?/topic/20173-limit-timestep/
stgatilov

stgatilov

11.11.2023 10:58

administrator   ~0016171

I fixed some bug in zeroing vertical velocity when walking (svn rev 10498).

More importantly, I commented out a piece of code (removing difference between current velocity and desired velocity) in svn rev 10499 (0006333).
It would great if someone could explain why it was necessary, or "let's hope I didn't break anything" =)

Issue History

Date Modified Username Field Change
11.03.2017 16:19 grayman New Issue
11.03.2017 16:38 AluminumHaste File Added: falltest.zip
11.03.2017 16:38 AluminumHaste Note Added: 0008763
13.03.2017 01:01 AluminumHaste Note Added: 0008764
19.06.2017 14:08 BluePill File Added: proofofconcept-pushentityfix.txt
19.06.2017 14:09 BluePill Note Added: 0008916
19.06.2017 14:09 BluePill Note Edited: 0008916
19.06.2017 19:57 BluePill File Added: crouchsmoothfix.txt
19.06.2017 20:12 BluePill Note Added: 0008917
19.06.2017 20:25 AluminumHaste Note Added: 0008918
19.06.2017 23:58 BluePill Note Added: 0008919
20.06.2017 05:20 nbohr1more Note Added: 0008920
20.06.2017 23:49 BluePill File Added: movespeed_cratepush_grabentity_betafixes.txt
20.06.2017 23:52 BluePill Note Added: 0008921
20.06.2017 23:54 BluePill Note Edited: 0008921
21.06.2017 00:02 BluePill Note Edited: 0008921
21.06.2017 00:02 BluePill Note Edited: 0008921
21.06.2017 01:32 nbohr1more Note Added: 0008922
21.06.2017 01:33 nbohr1more Relationship added related to 0004409
21.06.2017 01:34 nbohr1more Note Edited: 0008922
21.06.2017 02:39 nbohr1more Note Added: 0008923
27.06.2017 19:26 BluePill File Added: grabentity_dropbody_betafixes.txt
27.06.2017 19:34 BluePill Note Added: 0008933
27.06.2017 19:35 BluePill Note Edited: 0008933
27.06.2017 20:07 duzenko Note Added: 0008934
27.06.2017 20:39 nbohr1more Note Added: 0008935
28.06.2017 12:28 grayman Note Added: 0008937
28.06.2017 12:29 grayman Note Edited: 0008937
02.07.2017 02:40 AluminumHaste Note Added: 0008940
02.07.2017 03:17 grayman Note Added: 0008941
02.07.2017 07:33 duzenko Note Added: 0008945
02.07.2017 12:17 grayman Note Added: 0008947
02.07.2017 17:04 nbohr1more Relationship added parent of 0004552
02.07.2017 17:27 AluminumHaste Note Added: 0008950
03.07.2017 02:33 BluePill Note Added: 0008957
03.07.2017 02:33 BluePill Note Edited: 0008957
08.08.2017 01:34 nbohr1more Relationship added child of 0004060
08.08.2017 01:34 nbohr1more Relationship deleted child of 0004060
09.08.2017 15:02 Springheel Note Added: 0009069
27.08.2017 05:00 nbohr1more Note Added: 0009117
10.09.2017 15:05 Springheel Note Added: 0009206
11.09.2017 07:29 duzenko Note Added: 0009207
11.09.2017 07:43 duzenko Note Edited: 0009207
11.09.2017 21:35 Springheel Note Added: 0009208
11.09.2017 22:01 nbohr1more Note Added: 0009209
12.09.2017 00:32 nbohr1more Note Added: 0009210
12.09.2017 03:17 nbohr1more Note Added: 0009211
12.09.2017 10:41 duzenko Note Added: 0009212
13.09.2017 15:52 duzenko Note Added: 0009213
14.09.2017 01:31 nbohr1more Note Added: 0009218
14.09.2017 02:40 nbohr1more Note Edited: 0009218
14.09.2017 04:16 nbohr1more Relationship added related to 0003706
14.09.2017 09:30 duzenko Note Added: 0009230
14.09.2017 09:32 duzenko Note Edited: 0009230
15.09.2017 01:10 nbohr1more Note Added: 0009242
05.10.2017 12:52 nbohr1more Note Added: 0009393
12.12.2017 08:26 duzenko Assigned To => duzenko
12.12.2017 08:26 duzenko Status new => assigned
12.12.2017 08:26 duzenko Status assigned => resolved
12.12.2017 08:26 duzenko Fixed in Version => TDM 2.06
12.12.2017 08:26 duzenko Resolution open => fixed
15.07.2018 05:44 duzenko Relationship added related to 0004865
23.12.2018 12:53 stgatilov Relationship added related to 0004924
01.02.2019 05:37 nbohr1more Relationship added related to 0004983
01.02.2019 05:40 nbohr1more Relationship deleted related to 0004409
01.02.2019 05:40 nbohr1more Relationship added child of 0004409
01.02.2019 05:40 nbohr1more Relationship deleted related to 0004924
01.02.2019 05:41 nbohr1more Relationship added parent of 0004924
01.02.2019 05:41 nbohr1more Relationship deleted related to 0004983
01.02.2019 05:41 nbohr1more Relationship added parent of 0004983
01.02.2019 05:43 nbohr1more Relationship added parent of 0004426
01.02.2019 05:46 nbohr1more Relationship added parent of 0004696
01.02.2019 05:50 nbohr1more Relationship added parent of 0004741
05.12.2019 08:48 stgatilov Note Added: 0011890
12.01.2021 04:23 nbohr1more Relationship added related to 0005464
11.11.2023 09:34 stgatilov Relationship added related to 0006333
11.11.2023 10:58 stgatilov Note Added: 0016171
05.12.2023 01:37 nbohr1more Relationship added parent of 0005666