From 313c998e38f87050c56ec54aeb5ed8e85a60984f Mon Sep 17 00:00:00 2001 From: rt Date: Fri, 12 Oct 2018 18:01:55 +0200 Subject: [PATCH] fix #4515 / add. bf22703f --- rts/Sim/MoveTypes/StrafeAirMoveType.cpp | 552 +++++++++++------------- rts/Sim/MoveTypes/StrafeAirMoveType.h | 12 +- 2 files changed, 265 insertions(+), 299 deletions(-) diff --git a/rts/Sim/MoveTypes/StrafeAirMoveType.cpp b/rts/Sim/MoveTypes/StrafeAirMoveType.cpp index 4cdf345833c..e9ddb95dbf0 100644 --- a/rts/Sim/MoveTypes/StrafeAirMoveType.cpp +++ b/rts/Sim/MoveTypes/StrafeAirMoveType.cpp @@ -97,7 +97,8 @@ static float TurnRadius(const float rawRadius, const float rawSpeed) { return (std::min(1000.0f, rawRadius * rawSpeed)); } -static float GetAileronDeflection( + +static float GetRudderDeflection( const CUnit* owner, const CUnit* /*collidee*/, const float3& pos, @@ -108,83 +109,30 @@ static float GetAileronDeflection( const float3& goalDir, float groundHeight, float wantedHeight, - float maxAileron, - float maxBank, + float maxRudder, + float /*maxYaw*/, float goalDotRight, float goalDotFront, bool /*avoidCollision*/, bool isAttacking ) { - float aileron = 0.0f; + float rudder = 0.0f; - if (isAttacking) { - const float maxAileronSpeedf = maxAileron * spd.w; - const float maxAileronSpeedf2 = maxAileronSpeedf * 4.0f; - const float minPredictedHeight = pos.y + spd.y * 60.0f * math::fabs(frontdir.y) + std::min(0.0f, updir.y * 1.0f) * (GAME_SPEED * 5); - const float maxPredictedHeight = groundHeight + 60.0f + math::fabs(rightdir.y) * (GAME_SPEED * 5); + const float minGroundHeight = groundHeight + 15.0f * (1.0f + isAttacking); + const float maxRudderSpeedf = std::max(0.01f, maxRudder * spd.w * (1.0f + isAttacking)); - if (spd.w > 0.45f && minPredictedHeight > maxPredictedHeight) { - const float goalBankDif = goalDotRight + rightdir.y * 0.2f; + const float minHeightMult = (pos.y > minGroundHeight); - if (goalBankDif > maxAileronSpeedf2) { - aileron = 1.0f; - } else if (goalBankDif < -maxAileronSpeedf2) { - aileron = -1.0f; - } else if (maxAileronSpeedf2 > 0.0f) { - aileron = goalBankDif / maxAileronSpeedf2; - } - } else { - if (rightdir.y > 0.0f) { - if (rightdir.y > maxAileronSpeedf || frontdir.y < -0.7f) { - aileron = 1.0f; - } else { - if (maxAileronSpeedf > 0.0f) { - aileron = rightdir.y / maxAileronSpeedf; - } - } - } else { - if (rightdir.y < -maxAileronSpeedf || frontdir.y < -0.7f) { - aileron = -1.0f; - } else { - if (maxAileronSpeedf > 0.0f) { - aileron = rightdir.y / maxAileronSpeedf; - } - } - } - } - } else { - if (spd.w > 1.5f && ((pos.y + spd.y * 10.0f) > (groundHeight + wantedHeight * 0.6f))) { - const float goalBankDif = goalDotRight + rightdir.y * 0.5f; - const float maxAileronSpeedf = maxAileron * spd.w * 4.0f; - - if (goalBankDif > maxAileronSpeedf && rightdir.y > -maxBank) { - aileron = 1.0f; - } else if (goalBankDif < -maxAileronSpeedf && rightdir.y < maxBank) { - aileron = -1.0f; - } else { - if (math::fabs(rightdir.y) < maxBank && maxAileronSpeedf > 0.0f) { - aileron = goalBankDif / maxAileronSpeedf; - } else { - if (rightdir.y < 0.0f && goalBankDif < 0.0f) { - aileron = -1.0f; - } else if (rightdir.y > 0.0f && goalBankDif > 0.0f) { - aileron = 1.0f; - } - } - } - } else { - if (rightdir.y > 0.01f) { - aileron = 1.0f; - } else if (rightdir.y < -0.01f) { - aileron = -1.0f; - } - } - } + rudder -= (1.0f * minHeightMult * (goalDotRight < -maxRudderSpeedf) ); + rudder += (1.0f * minHeightMult * (goalDotRight > maxRudderSpeedf) ); + rudder += (1.0f * minHeightMult * (goalDotRight / maxRudderSpeedf) * (rudder == 0.0f)); + // we have to choose a direction in case our target is almost straight behind us + rudder += (1.0f * minHeightMult * ((goalDotRight < 0.0f)? -0.01f: 0.01f) * (math::fabs(rudder) < 0.01f && goalDotFront < 0.0f)); - return aileron; + return rudder; } -static float GetRudderDeflection( +static float GetAileronDeflection( const CUnit* owner, const CUnit* /*collidee*/, const float3& pos, @@ -195,58 +143,64 @@ static float GetRudderDeflection( const float3& goalDir, float groundHeight, float wantedHeight, - float maxRudder, - float, + float maxAileron, + float maxBank, float goalDotRight, float goalDotFront, bool /*avoidCollision*/, bool isAttacking ) { - float rudder = 0.0f; + float aileron = 0.0f; + + // ailerons function less effectively at low (forward) speed + const float maxAileronSpeedf = std::max(0.01f, maxAileron * spd.w); + const float maxAileronSpeedf2 = std::max(0.01f, maxAileronSpeedf * 4.0f); if (isAttacking) { - if (pos.y > (groundHeight + 30.0f)) { - const float maxRudderSpeedf = maxRudder * spd.w; + const float minPredictedHeight = pos.y + spd.y * 60.0f * math::fabs(frontdir.y) + std::min(0.0f, updir.y * 1.0f) * (GAME_SPEED * 5); + const float maxPredictedHeight = groundHeight + 60.0f + math::fabs(rightdir.y) * (GAME_SPEED * 5); - if (goalDotRight < -maxRudderSpeedf) { - rudder = -1.0f; - } else if (goalDotRight > maxRudderSpeedf) { - rudder = 1.0f; - } else { - if (maxRudderSpeedf > 0.0f) { - rudder = goalDotRight / maxRudderSpeedf; + const float minSpeedMult = (spd.w > 0.45f && minPredictedHeight > maxPredictedHeight); + const float goalBankDif = goalDotRight + rightdir.y * 0.2f; - if (math::fabs(rudder) < 0.01f && goalDotFront < 0.0f) { - // target almost straight behind us, we have to choose a direction - rudder = (goalDotRight < 0.0f) ? -0.01f : 0.01f; - } - } - } - } + aileron += (1.0f * ( minSpeedMult) * (goalBankDif > maxAileronSpeedf2) ); + aileron -= (1.0f * ( minSpeedMult) * (goalBankDif < -maxAileronSpeedf2) ); + aileron += (1.0f * ( minSpeedMult) * (goalBankDif / maxAileronSpeedf2) * (aileron == 0.0f)); + + aileron += (1.0f * (1.0f - minSpeedMult) * (rightdir.y > 0.0f) * (rightdir.y > maxAileronSpeedf || frontdir.y < -0.7f)); + aileron += (1.0f * (1.0f - minSpeedMult) * (aileron == 0.0f) * (rightdir.y > 0.0f) * (rightdir.y / maxAileronSpeedf )); + + aileron -= (1.0f * (1.0f - minSpeedMult) * (rightdir.y < 0.0f) * (rightdir.y < -maxAileronSpeedf || frontdir.y < -0.7f)); + aileron += (1.0f * (1.0f - minSpeedMult) * (aileron == 0.0f) * (rightdir.y < 0.0f) * (rightdir.y / maxAileronSpeedf )); } else { - if (pos.y > (groundHeight + 15.0f)) { - const float maxRudderSpeedf = maxRudder * spd.w * 2.0f; + const float minPredictedHeight = pos.y + spd.y * 10.0f; + const float maxPredictedHeight = groundHeight + wantedHeight * 0.6f; - if (goalDotRight < -maxRudderSpeedf) { - rudder = -1.0f; - } else if (goalDotRight > maxRudderSpeedf) { - rudder = 1.0f; - } else { - if (maxRudderSpeedf > 0.0f) { - rudder = goalDotRight / maxRudderSpeedf; + const float absRightDirY = math::fabs(rightdir.y); + const float goalBankDif = goalDotRight + rightdir.y * 0.5f; - if (math::fabs(rudder) < 0.01f && goalDotFront < 0.0f) { - // target almost straight behind us, we have to choose a direction - rudder = (goalDotRight < 0.0f) ? -0.01f : 0.01f; - } - } else { - rudder = 0.0f; - } - } - } + const float relBankMult = (absRightDirY < maxBank && maxAileronSpeedf2 > 0.0f); + const float minSpeedMult = (spd.w > 1.5f && minPredictedHeight > maxPredictedHeight); + + const float clampedBank = 1.0f; + // using this directly does not function well at higher pitch-angles, interplay with GetElevatorDeflection + // const float clampedBankAbs = math::fabs(Clamp(absRightDirY - maxBank, -1.0f, 1.0f)); + // const float clampedBank = std::max(clampedBankAbs, 0.3f); + + aileron -= (clampedBank * (minSpeedMult) * (goalBankDif < -maxAileronSpeedf2 && rightdir.y < maxBank)); + aileron += (clampedBank * (minSpeedMult) * (goalBankDif > maxAileronSpeedf2 && rightdir.y > -maxBank)); + + /// NB: these ignore maxBank + aileron += (1.0f * (minSpeedMult) * (aileron == 0.0f) * ( relBankMult) * (goalBankDif / maxAileronSpeedf2)); + aileron -= (1.0f * (minSpeedMult) * (aileron == 0.0f) * (1.0f - relBankMult) * (rightdir.y < 0.0f && goalBankDif < 0.0f)); + aileron += (1.0f * (minSpeedMult) * (aileron == 0.0f) * (1.0f - relBankMult) * (rightdir.y > 0.0f && goalBankDif > 0.0f)); + + // if right wing too high, roll right (cw) + // if right wing too low, roll left (ccw) + aileron += std::copysign(1.0f, float(rightdir.y)) * (1.0f - minSpeedMult) * (absRightDirY > maxAileronSpeedf); } - return rudder; + return aileron; } static float GetElevatorDeflection( @@ -268,88 +222,82 @@ static float GetElevatorDeflection( bool isAttacking ) { float elevator = 0.0f; - float upside = (updir.y >= -0.3f) * 2.0f - 1.0f; + + const float upside = (updir.y >= -0.3f) * 2.0f - 1.0f; + const float speedMult = (spd.w >= 1.5f); + + if (collidee == nullptr || !avoidCollision) + collidee = owner; if (isAttacking) { - if (spd.w < 1.5f) { - if (frontdir.y < 0.0f) { - elevator = upside; - } else if (frontdir.y > 0.0f) { - elevator = -upside; - } - } else { - const float gHeightR = CGround::GetHeightAboveWater(pos.x + spd.x * 40.0f, pos.z + spd.z * 40.0f); - const float hdif = std::max(gHeightR, groundHeight) + 60 - pos.y - frontdir.y * spd.w * 20.0f; + elevator += (upside * (1.0f - speedMult) * (frontdir.y < (-maxElevator * spd.w))); + elevator -= (upside * (1.0f - speedMult) * (frontdir.y > ( maxElevator * spd.w))); - const float maxElevatorSpeedf = maxElevator * spd.w; - const float maxElevatorSpeedf2 = maxElevatorSpeedf * spd.w * 20.0f; + const float posHeight = CGround::GetHeightAboveWater(pos.x + spd.x * 40.0f, pos.z + spd.z * 40.0f); + const float difHeight = std::max(posHeight, groundHeight) + 60 - pos.y - frontdir.y * spd.w * 20.0f; + const float goalDotY = goalDir.dot(updir); - float minPitch = 1.0f; // min(1.0f, hdif / (maxElevator * spd.w * spd.w * 20)); + const float maxElevatorSpeedf = std::max(0.01f, maxElevator * spd.w ); + const float maxElevatorSpeedf2 = std::max(0.01f, maxElevatorSpeedf * spd.w * 20.0f); - if (hdif < -maxElevatorSpeedf2) { - minPitch = -1.0f; - } else if (hdif > maxElevatorSpeedf2) { - minPitch = 1.0f; - } else if (maxElevatorSpeedf2 > 0.0f) { - minPitch = hdif / maxElevatorSpeedf2; - } + const float ydirMult = (updir.dot(collidee->midPos - owner->midPos) > 0.0f); + const float zdirMult = (frontdir.dot(collidee->pos + collidee->speed * 20.0f - pos - spd * 20.0f) < 0.0f); - if (collidee != nullptr && avoidCollision && frontdir.dot(collidee->pos + collidee->speed * 20.0f - pos - spd * 20.0f) < 0.0f) { - elevator = (updir.dot(collidee->midPos - owner->midPos) > 0.0f)? -1 : 1; - } else { - const float hdif = goalDir.dot(updir); - - if (hdif < -maxElevatorSpeedf) { - elevator = -1.0f; - } else if (hdif > maxElevatorSpeedf) { - elevator = 1.0f; - } else if (maxElevatorSpeedf > 0.0f) { - elevator = hdif / maxElevatorSpeedf; - } - } + float minPitch = 0.0f; - if ((elevator * upside) < minPitch) { - elevator = minPitch * upside; - } - } + minPitch -= (1.0f * speedMult * (difHeight < -maxElevatorSpeedf2)); + minPitch += (1.0f * speedMult * (difHeight > maxElevatorSpeedf2)); + minPitch += (1.0f * speedMult * (minPitch == 0.0f) * (difHeight / maxElevatorSpeedf2)); + + // busted collision avoidance + // elevator += ((ydirMult * -2.0f + 1.0f) * zdirMult * speedMult * (collidee != owner)); + + elevator -= (1.0f * speedMult * (goalDotY < -maxElevatorSpeedf) * (collidee == owner) * (1.0f - zdirMult)); + elevator += (1.0f * speedMult * (goalDotY > maxElevatorSpeedf) * (collidee == owner) * (1.0f - zdirMult)); + elevator += (1.0f * speedMult * (elevator == 0.0f) * (goalDotY / maxElevatorSpeedf) * (collidee == owner) * (1.0f - zdirMult)); + + elevator = mix(elevator, minPitch * upside, (elevator * upside) < minPitch && spd.w >= 1.5f); } else { - if (spd.w > 0.8f) { - bool colliding = false; - - if (avoidCollision) { - const float3 cvec = collidee->midPos - owner->midPos; - const float3 svec = collidee->speed - spd; - - // pitch down or up based on relative direction to - // a (collisionState == COLLISION_DIRECT) collidee - // this usually just results in jittering, callers - // disable it - // FIXME? dot < 0 means collidee is behind us - if ((colliding = (frontdir.dot(cvec + svec * 20.0f) < 0.0f))) - elevator = Sign(updir.dot(cvec) * -1.0f); - } + #if 0 + bool colliding = false; + + if (avoidCollision) { + const float3 cvec = collidee->midPos - owner->midPos; + const float3 svec = collidee->speed - spd; + + // pitch down or up based on relative direction to + // a (collisionState == COLLISION_DIRECT) collidee + // this usually just results in jittering, callers + // disable it + // FIXME? zdirDot < 0 means collidee is behind us + const float ydirDot = updir.dot(cvec) * -1.0f; + const float zdirDot = frontdir.dot(cvec + svec * 20.0f); + + elevator = Sign(ydirDot) * (spd.w > 0.8f) * (colliding = (zdirDot < 0.0f)); + } - if (!colliding) { - const float maxElevatorSpeedf = maxElevator * 20.0f * spd.w * spd.w; - const float gHeightAW = CGround::GetHeightAboveWater(pos.x + spd.x * 40.0f, pos.z + spd.z * 40.0f); - const float hdif = std::max(groundHeight, gHeightAW) + wantedHeight - pos.y - frontdir.y * spd.w * 20.0f; - - if (hdif < -maxElevatorSpeedf && frontdir.y > -maxPitch) { - elevator = -1.0f; - } else if (hdif > maxElevatorSpeedf && frontdir.y < maxPitch) { - elevator = 1.0f; - } else if (maxElevatorSpeedf > 0.0f) { - if (math::fabs(frontdir.y) < maxPitch) - elevator = hdif / maxElevatorSpeedf; - } - } - } else { - if (frontdir.y < -0.1f) { - elevator = 1.0f; - } else if (frontdir.y > 0.15f) { - elevator = -1.0f; - } + if (!colliding) { + #endif + { + const float maxElevatorSpeedf = std::max(0.001f, maxElevator * 20.0f * spd.w * spd.w); + + const float posHeight = CGround::GetHeightAboveWater(pos.x + spd.x * 40.0f, pos.z + spd.z * 40.0f); + const float difHeight = std::max(groundHeight, posHeight) + wantedHeight - pos.y - frontdir.y * spd.w * 20.0f; + + const float absFrontDirY = math::fabs(frontdir.y); + const float clampedPitch = 1.0f; + // using this directly does not function well at higher bank-angles + // const float clampedPitchAbs = math::fabs(Clamp(absFrontDirY - maxPitch, -1.0f, 1.0f)); + // const float clampedPitch = std::max(clampedPitchAbs, 0.3f); + + elevator -= (clampedPitch * (spd.w > 0.8f) * (difHeight < -maxElevatorSpeedf && frontdir.y > -maxPitch)); + elevator += (clampedPitch * (spd.w > 0.8f) * (difHeight > maxElevatorSpeedf && frontdir.y < maxPitch)); + + elevator += (1.0f * (elevator == 0.0f) * (spd.w > 0.8f) * (difHeight / maxElevatorSpeedf) * (absFrontDirY < maxPitch)); } + + elevator += (1.0f * (spd.w < 0.8f) * (frontdir.y < -0.10f)); + elevator -= (1.0f * (spd.w < 0.8f) * (frontdir.y > 0.15f)); } return elevator; @@ -364,8 +312,8 @@ static float3 GetControlSurfaceAngles( const SyncedFloat3& updir, const SyncedFloat3& frontdir, const float3& goalDir, - const float3& maxBodyAngles, // .x := maxBank, .y := maxYaw, .z := maxPitch - const float3& maxCtrlAngles, // .x := maxAileron, .y := maxRudder, .z := maxElevator + const float3& maxBodyAngles, // .x := maxYaw, .y := maxPitch, .z := maxBank + const float3& maxCtrlAngles, // .x := maxRudder, .y := maxElevator, .z := maxAileron const float3* prvCtrlAngles, float groundHeight, float wantedHeight, @@ -376,12 +324,13 @@ static float3 GetControlSurfaceAngles( ) { float3 ctrlAngles; - ctrlAngles.x = GetRudderDeflection (owner, collidee, pos, spd, rightdir, updir, frontdir, goalDir, groundHeight, wantedHeight, maxCtrlAngles.y, maxBodyAngles.y, goalDotRight, goalDotFront, avoidCollision, isAttacking); // yaw - ctrlAngles.y = GetElevatorDeflection(owner, collidee, pos, spd, rightdir, updir, frontdir, goalDir, groundHeight, wantedHeight, maxCtrlAngles.z, maxBodyAngles.z, goalDotRight, goalDotFront, avoidCollision, isAttacking); // pitch - ctrlAngles.z = GetAileronDeflection (owner, collidee, pos, spd, rightdir, updir, frontdir, goalDir, groundHeight, wantedHeight, maxCtrlAngles.x, maxBodyAngles.x, goalDotRight, goalDotFront, avoidCollision, isAttacking); // roll + // yaw (rudder), pitch (elevator), roll (aileron) + ctrlAngles.x = GetRudderDeflection (owner, collidee, pos, spd, rightdir, updir, frontdir, goalDir, groundHeight, wantedHeight, maxCtrlAngles.x, maxBodyAngles.x, goalDotRight, goalDotFront, avoidCollision, isAttacking); + ctrlAngles.y = GetElevatorDeflection(owner, collidee, pos, spd, rightdir, updir, frontdir, goalDir, groundHeight, wantedHeight, maxCtrlAngles.y, maxBodyAngles.y, goalDotRight, goalDotFront, avoidCollision, isAttacking); + ctrlAngles.z = GetAileronDeflection (owner, collidee, pos, spd, rightdir, updir, frontdir, goalDir, groundHeight, wantedHeight, maxCtrlAngles.z, maxBodyAngles.z, goalDotRight, goalDotFront, avoidCollision, isAttacking); // let the previous control angles have some authority - return (ctrlAngles * 0.85f + prvCtrlAngles[0] * 0.1f + prvCtrlAngles[1] * 0.05f); + return (ctrlAngles * 0.7f + prvCtrlAngles[0] * 0.2f + prvCtrlAngles[1] * 0.1f); } @@ -476,7 +425,7 @@ bool CStrafeAirMoveType::Update() // otherwise we might fall through the map when stunned // (the kill-on-impact code is not reached in that case) if ((owner->IsStunned() && !owner->IsCrashing()) || owner->beingBuilt) { - UpdateAirPhysics(0.0f * lastRudderPos[0], lastAileronPos[0], lastElevatorPos[0], 0.0f, ZeroVector); + UpdateAirPhysics({0.0f * lastRudderPos[0], lastElevatorPos[0], lastAileronPos[0], 0.0f}, ZeroVector); return (HandleCollisions(collide && !owner->beingBuilt && (aircraftState != AIRCRAFT_TAKEOFF))); } @@ -495,7 +444,7 @@ bool CStrafeAirMoveType::Update() aileron += (1.0f * fpsCon.right ); aileron -= (1.0f * fpsCon.left ); - UpdateAirPhysics(0.0f, aileron, elevator, 1.0f, owner->frontdir); + UpdateAirPhysics({0.0f, elevator, aileron, 1.0f}, owner->frontdir); maneuverState = MANEUVER_FLY_STRAIGHT; return (HandleCollisions(collide && !owner->beingBuilt && (aircraftState != AIRCRAFT_TAKEOFF))); @@ -561,7 +510,7 @@ bool CStrafeAirMoveType::Update() break; case AIRCRAFT_CRASHING: { // NOTE: the crashing-state can only be set (and unset) by scripts - UpdateAirPhysics(crashRudder, crashAileron, crashElevator, 0.0f, owner->frontdir); + UpdateAirPhysics({crashRudder, crashElevator, crashAileron, 0.0f}, owner->frontdir); if ((CGround::GetHeightAboveWater(owner->pos.x, owner->pos.z) + 5.0f + owner->radius) > owner->pos.y) owner->ForcedKillUnit(nullptr, true, false); @@ -729,20 +678,12 @@ void CStrafeAirMoveType::UpdateManeuver() switch (maneuverState) { case MANEUVER_IMMELMAN: { float aileron = 0.0f; - float elevator = 0.0f; - - if (owner->updir.y > 0.0f) { - if (owner->rightdir.y > maxAileron * speedf) { - aileron = 1.0f; - } else if (owner->rightdir.y < -maxAileron * speedf) { - aileron = -1.0f; - } - } + float elevator = 1.0f * (math::fabs(owner->rightdir.y) < maxAileron * 3.0f * speedf || owner->updir.y < 0.0f); - if (math::fabs(owner->rightdir.y) < maxAileron * 3.0f * speedf || owner->updir.y < 0.0f) - elevator = 1.0f; + aileron += (1.0f * (owner->updir.y > 0.0f) * (owner->rightdir.y > maxAileron * speedf)); + aileron -= (1.0f * (owner->updir.y > 0.0f) * (owner->rightdir.y < -maxAileron * speedf)); - UpdateAirPhysics(0.0f, aileron, elevator, 1.0f, owner->frontdir); + UpdateAirPhysics({0.0f, elevator, aileron, 1.0f}, owner->frontdir); if ((owner->updir.y < 0.0f && owner->frontdir.y < 0.0f) || speedf < 0.8f) maneuverState = MANEUVER_FLY_STRAIGHT; @@ -758,20 +699,15 @@ void CStrafeAirMoveType::UpdateManeuver() float aileron = 0.0f; float elevator = 0.0f; - if (maneuverSubState == 0) { - if (owner->rightdir.y >= 0.0f) { - aileron = -1.0f; - } else { - aileron = 1.0f; - } - } + aileron -= (1.0f * (maneuverSubState == 0) * (owner->rightdir.y >= 0.0f)); + aileron += (1.0f * (maneuverSubState == 0) * (owner->rightdir.y < 0.0f)); if (owner->frontdir.y < -0.7f) maneuverSubState = 1; if (maneuverSubState == 1 || owner->updir.y < 0.0f) elevator = 1.0f; - UpdateAirPhysics(0.0f, aileron, elevator, 1.0f, owner->frontdir); + UpdateAirPhysics({0.0f, elevator, aileron, 1.0f}, owner->frontdir); if ((owner->updir.y > 0.0f && owner->frontdir.y > 0.0f && maneuverSubState == 1) || speedf < 0.2f) maneuverState = MANEUVER_FLY_STRAIGHT; @@ -779,7 +715,7 @@ void CStrafeAirMoveType::UpdateManeuver() } break; default: { - UpdateAirPhysics(0.0f, 0.0f, 0.0f, 1.0f, owner->frontdir); + UpdateAirPhysics({0.0f, 0.0f, 0.0f, 1.0f}, owner->frontdir); maneuverState = MANEUVER_FLY_STRAIGHT; } break; } @@ -802,7 +738,7 @@ void CStrafeAirMoveType::UpdateAttack() const SyncedFloat3& updir = owner->updir; if (spd.w < 0.01f) { - UpdateAirPhysics(0.0f, 0.0f, 0.0f, 1.0f, owner->frontdir); + UpdateAirPhysics({0.0f, 0.0f, 0.0f, 1.0f}, owner->frontdir); return; } @@ -837,25 +773,25 @@ void CStrafeAirMoveType::UpdateAttack() goalDotRight /= goalDotFront01; { - const float3 maxBodyAngles = {maxBank, 0.0f, maxPitch}; - const float3 maxCtrlAngles = {maxAileron, maxRudder, maxElevator}; - const float3 prvCtrlAngles[2] = {{lastAileronPos[0], lastRudderPos[0], lastElevatorPos[0]}, {lastAileronPos[1], lastRudderPos[1], lastElevatorPos[1]}}; + const float3 maxBodyAngles = {0.0f, maxPitch, maxBank}; + const float3 maxCtrlAngles = {maxRudder, maxElevator, maxAileron}; + const float3 prvCtrlAngles[2] = {{lastRudderPos[0], lastElevatorPos[0], lastAileronPos[0]}, {lastRudderPos[1], lastElevatorPos[1], lastAileronPos[1]}}; const float3& curCtrlAngles = GetControlSurfaceAngles(owner, lastCollidee, pos, spd, rightdir, updir, frontdir, goalDir, maxBodyAngles, maxCtrlAngles, prvCtrlAngles, gHeightAW, wantedHeight, goalDotRight, goalDotFront, false && collisionState == COLLISION_DIRECT, true); const CUnit* attackee = owner->curTarget.unit; // limit thrust when in range of (air) target and directly behind it - const float rangeLim = goalDist / owner->maxRange; - const float angleLim = 1.0f - goalDotFront * 0.7f; - const float engine = ((attackee == nullptr) || attackee->unitDef->IsGroundUnit())? 1.0f: std::min(1.0f, rangeLim + angleLim); + const float rangeLim = goalDist / owner->maxRange; + const float angleLim = 1.0f - goalDotFront * 0.7f; + const float thrustLim = ((attackee == nullptr) || attackee->unitDef->IsGroundUnit())? 1.0f: std::min(1.0f, rangeLim + angleLim); - UpdateAirPhysics(curCtrlAngles.x, curCtrlAngles.z, curCtrlAngles.y, engine, frontdir); + UpdateAirPhysics({curCtrlAngles.x, curCtrlAngles.y, curCtrlAngles.z, thrustLim}, frontdir); } } -bool CStrafeAirMoveType::UpdateFlying(float wantedHeight, float engine) +bool CStrafeAirMoveType::UpdateFlying(float wantedHeight, float thrust) { const float3& pos = owner->pos; const float4& spd = owner->speed; @@ -876,9 +812,7 @@ bool CStrafeAirMoveType::UpdateFlying(float wantedHeight, float engine) const float3 goalDir2D = goalVec / goalDist2D; // const float3 goalDir3D = goalVec / goalDist3D; - // do not check if the plane can be submerged here, - // since it'll cause ground collisions later on (?) - const float groundHeight = amtGetGroundHeightFuncs[5 * UseSmoothMesh()](pos.x, pos.z); + const float3 rightDir2D = (rightdir * XZVector).Normalize2D(); if (((gs->frameNum + owner->id) & 3) == 0) CheckForCollision(); @@ -891,22 +825,27 @@ bool CStrafeAirMoveType::UpdateFlying(float wantedHeight, float engine) const bool allowUnlockYawRoll = (goalDist2D >= TurnRadius(turnRadius, spd.w) || goalVec.dot(owner->frontdir) > 0.0f); const bool forceUnlockYawRoll = ((gs->frameNum - owner->lastFireWeapon) >= maneuverBlockTime); - const float3 rightDir2D = (rightdir * XZVector).Normalize2D(); - const float3 yprMults = (XZVector * float(allowUnlockYawRoll || forceUnlockYawRoll)) + UpVector; - - const float goalDotFront = goalDir2D.dot(frontdir); - float goalDotRight = goalDir2D.dot(rightDir2D); + // do not check if the plane can be submerged here, + // since it'll cause ground collisions later on (?) + const float groundHeight = amtGetGroundHeightFuncs[5 * UseSmoothMesh()](pos.x, pos.z); - // If goal-position is behind us and goal-distance is less - // than our turning radius, turn the other way. // If goal-distance is half turn radius then turn if - // goal-position is not in front within a 45 degree arc. - // This is to prevent becoming stuck in a small circle - // around goal-position. - if ((goalDist2D < turnRadius * 0.5f && goalDir2D.dot(frontdir) < 0.7f) || (goalDist2D < turnRadius && goalDir2D.dot(frontdir) < -0.1f)) - goalDotRight *= ((!owner->UnderFirstPersonControl() || owner->fpsControlPlayer->fpsController.mouse2) * -2.0f + 1.0f); + // goal-position is not in front within a ~45 degree + // arc. + // If goal-position is behind us and goal-distance is + // less than our turning radius, turn the other way. + // These conditions prevent becoming stuck in a small + // circle around goal-position. + const float nearGoal = ((goalDist2D < turnRadius * 0.5f && goalDir2D.dot(frontdir) < 0.7f) || (goalDist2D < turnRadius && goalDir2D.dot(frontdir) < -0.1f)); + const float turnFlip = ((!owner->UnderFirstPersonControl() || owner->fpsControlPlayer->fpsController.mouse2) * -2.0f + 1.0f); + + // if nearGoal=0, multiply by 1 + // if nearGoal=1, multiply by turnFlip + const float goalDotFront = goalDir2D.dot(frontdir); + const float goalDotRight = goalDir2D.dot(rightDir2D) * ((1.0f - nearGoal) + (nearGoal * turnFlip)); + #if 0 // try to steer (yaw) away from nearby aircraft in front of us if (lastCollidee != nullptr) { const float3 collideeVec = lastCollidee->pos - pos; @@ -922,13 +861,15 @@ bool CStrafeAirMoveType::UpdateFlying(float wantedHeight, float engine) goalDotRight -= (collideeDir.dot(rightdir) * relativeDist * std::max(0.0f, collideeDir.dot(frontdir))); } + #endif - const float3 maxBodyAngles = {maxBank, 0.0f, maxPitch}; - const float3 maxCtrlAngles = {maxAileron, maxRudder, maxElevator}; - const float3 prvCtrlAngles[2] = {{lastAileronPos[0], lastRudderPos[0], lastElevatorPos[0]}, {lastAileronPos[1], lastRudderPos[1], lastElevatorPos[1]}}; + const float3 yprInputLocks = (XZVector * float(allowUnlockYawRoll || forceUnlockYawRoll)) + UpVector; + const float3 maxBodyAngles = {0.0f, maxPitch, maxBank}; + const float3 maxCtrlAngles = {maxRudder, maxElevator, maxAileron}; + const float3 prvCtrlAngles[2] = {{lastRudderPos[0], lastElevatorPos[0], lastAileronPos[0]}, {lastRudderPos[1], lastElevatorPos[1], lastAileronPos[1]}}; const float3& curCtrlAngles = GetControlSurfaceAngles(owner, lastCollidee, pos, spd, rightdir, updir, frontdir, goalDir2D, maxBodyAngles, maxCtrlAngles, prvCtrlAngles, groundHeight, wantedHeight, goalDotRight, goalDotFront, false && collisionState == COLLISION_DIRECT, false); - UpdateAirPhysics(curCtrlAngles.x * yprMults.x, curCtrlAngles.z * yprMults.z, curCtrlAngles.y * yprMults.y, engine, owner->frontdir); + UpdateAirPhysics({curCtrlAngles * yprInputLocks, thrust}, owner->frontdir); return (allowUnlockYawRoll || forceUnlockYawRoll); } @@ -982,7 +923,8 @@ void CStrafeAirMoveType::UpdateTakeOff() void CStrafeAirMoveType::UpdateLanding() { - const float3& pos = owner->pos; + const float3 pos = owner->pos; + SyncedFloat3& rightdir = owner->rightdir; SyncedFloat3& frontdir = owner->frontdir; SyncedFloat3& updir = owner->updir; @@ -993,12 +935,11 @@ void CStrafeAirMoveType::UpdateLanding() // if spot is valid, mark it on the blocking-map // so other aircraft can not claim the same spot if (HaveLandingPos()) { - const float3 originalPos = pos; wantedHeight = 0.0f; owner->Move(reservedLandingPos, false); owner->Block(); - owner->Move(originalPos, false); + owner->Move(pos, false); // Block updates the blocking-map position (mapPos) // via GroundBlockingObjectMap (which calculates it // from object->pos) and UnBlock always uses mapPos @@ -1020,22 +961,22 @@ void CStrafeAirMoveType::UpdateLanding() if (brakeSpot.SqDistance2D(reservedLandingPos) > landRadiusSq) { // If we're not going to land inside the landRadius, keep flying. - float tempWantedHeight = wantedHeight; - wantedHeight = orgWantedHeight; + const float tempWantedHeight = wantedHeight; + + UpdateFlying(wantedHeight = orgWantedHeight, 1.0f); - UpdateFlying(wantedHeight, 1.0f); wantedHeight = tempWantedHeight; return; } - if (rightdir.y < -0.01f) { updir -= (rightdir * 0.02f); } - else if (rightdir.y > 0.01f) { updir += (rightdir * 0.02f); } + updir -= ((rightdir * 0.02f) * (rightdir.y < -0.01f)); + updir += ((rightdir * 0.02f) * (rightdir.y > 0.01f)); - if (frontdir.y < -0.01f) { frontdir += (updir * 0.02f); } - else if (frontdir.y > 0.01f) { frontdir -= (updir * 0.02f); } + frontdir += ((updir * 0.02f) * (frontdir.y < -0.01f)); + frontdir -= ((updir * 0.02f) * (frontdir.y > 0.01f)); - if (rightdir.dot(reservedLandingPosDir) > 0.01f) { frontdir += (rightdir * 0.02f); } - else if (rightdir.dot(reservedLandingPosDir) < -0.01f) { frontdir -= (rightdir * 0.02f); } + frontdir += ((rightdir * 0.02f) * (rightdir.dot(reservedLandingPosDir) > 0.01f)); + frontdir -= ((rightdir * 0.02f) * (rightdir.dot(reservedLandingPosDir) < -0.01f)); { //A Mangled UpdateAirPhysics @@ -1082,7 +1023,7 @@ void CStrafeAirMoveType::UpdateLanding() -void CStrafeAirMoveType::UpdateAirPhysics(float rudder, float aileron, float elevator, float engine, const float3& engineThrustVector) +void CStrafeAirMoveType::UpdateAirPhysics(const float4& controlInputs, const float3& thrustVector) { const float3& pos = owner->pos; const float4& spd = owner->speed; @@ -1091,58 +1032,83 @@ void CStrafeAirMoveType::UpdateAirPhysics(float rudder, float aileron, float ele SyncedFloat3& frontdir = owner->frontdir; SyncedFloat3& updir = owner->updir; - bool nextPosInBounds = true; + const float groundHeight = CGround::GetHeightAboveWater(pos.x, pos.z); + const float linearSpeed = spd.w; + + const float rudder = controlInputs.x; + const float elevator = controlInputs.y; + const float aileron = controlInputs.z; + float throttle = controlInputs.w; - lastRudderPos[1] = lastRudderPos[0]; - lastRudderPos[0] = rudder; - lastAileronPos[1] = lastAileronPos[0]; - lastAileronPos[0] = aileron; - lastElevatorPos[1] = lastElevatorPos[0]; - lastElevatorPos[0] = elevator; + bool nextPosInBounds = true; - const float gHeight = CGround::GetHeightAboveWater(pos.x, pos.z); - const float speedf = spd.w; - const float3 speeddir = spd / (speedf + 0.1f); + { + // keep previous two inputs to reduce temporal jitter + lastRudderPos[1] = lastRudderPos[0]; + lastRudderPos[0] = rudder; + lastElevatorPos[1] = lastElevatorPos[0]; + lastElevatorPos[0] = elevator; + lastAileronPos[1] = lastAileronPos[0]; + lastAileronPos[0] = aileron; + } if (owner->UnderFirstPersonControl()) { - if ((pos.y - gHeight) > wantedHeight * 1.2f) { - engine = std::max(0.0f, std::min(engine, 1 - (pos.y - gHeight - wantedHeight * 1.2f) / wantedHeight)); - } + if ((pos.y - groundHeight) > wantedHeight * 1.2f) + throttle = Clamp(throttle, 0.0f, 1.0f - (pos.y - groundHeight - wantedHeight * 1.2f) / wantedHeight); + // check next position given current (unadjusted) pos and speed nextPosInBounds = (pos + spd).IsInBounds(); } - // apply gravity only when in the air - owner->SetVelocity(spd + UpVector * (mapInfo->map.gravity * myGravity) * int((owner->midPos.y - owner->radius) > gHeight)); - owner->SetVelocity(spd + (engineThrustVector * accRate * engine)); - - if (aircraftState == AIRCRAFT_CRASHING) { - owner->SetVelocity(spd * crashDrag); - } else { - owner->SetVelocity(spd * invDrag); - } - - const float3 wingDir = updir * (1 - wingAngle) - frontdir * wingAngle; - const float wingForce = wingDir.dot(spd) * wingDrag; + // unlike IRL, thrust points forward + owner->SetVelocity(spd + UpVector * (mapInfo->map.gravity * myGravity) * int((owner->midPos.y - owner->radius) > groundHeight)); + owner->SetVelocity(spd + (thrustVector * accRate * throttle)); + owner->SetVelocity(spd * ((aircraftState == AIRCRAFT_CRASHING)? crashDrag: invDrag)); if (!owner->IsStunned()) { - frontdir += (rightdir * rudder * maxRudder * speedf); // yaw - updir += (rightdir * aileron * maxAileron * speedf); // roll - frontdir += (updir * elevator * maxElevator * speedf); // pitch - frontdir += ((speeddir - frontdir) * frontToSpeed); - - owner->SetVelocity(spd - (wingDir * wingForce)); - owner->SetVelocity(spd + ((frontdir * speedf - spd) * speedToFront)); + const float3 speedDir = spd / (linearSpeed + 0.1f); + const float3 wingDir = updir * (1.0f - wingAngle) - frontdir * wingAngle; + + const float3 yprDeltas = { + rudder * maxRudder * linearSpeed, + elevator * maxElevator * linearSpeed, + aileron * maxAileron * linearSpeed, + }; + const float3 yprScales = { + #if 0 + // do not want locks when crashing or during "special" maneuvers (Immelman, etc) + // when both pitch and roll are locked (e.g during a climbing turn), yawing also + // becomes impossible since yaw motion can cause |rightdir.y| to exceed maxBank + (math::fabs((((frontdir + rightdir * yprDeltas.x).Normalize()).cross(updir)).y) < maxBank), // yawing changes rightdir if pitched or banked + (math::fabs(frontdir.y + yprDeltas.y) < maxPitch), // pitching up (ypr.y=+1) increases frontdir.y + (math::fabs(rightdir.y - yprDeltas.z) < maxBank ), // rolling left (ypr.z=-1) increases rightdir.y + #endif + #if 0 + 1.0f, + (math::fabs(rightdir.y) < 0.1f || math::fabs(frontdir.y + yprDeltas.y) < math::fabs(frontdir.y)), // allow pitch when not banked + (math::fabs(frontdir.y) < 0.1f || math::fabs(rightdir.y - yprDeltas.z) < math::fabs(rightdir.y)), // allow roll when not pitched + #endif + 1.0f, + 1.0f, + 1.0f, + }; + + frontdir += (rightdir * yprDeltas.x * yprScales.x); // yaw + frontdir += (updir * yprDeltas.y * yprScales.y); // pitch + updir += (rightdir * yprDeltas.z * yprScales.z); // roll (via updir, new rightdir derives from it) + frontdir += ((speedDir - frontdir) * frontToSpeed); + + owner->SetVelocity(spd - (wingDir * wingDir.dot(spd) * wingDrag)); + owner->SetVelocity(spd + ((frontdir * linearSpeed - spd) * speedToFront)); owner->SetVelocity(spd * (1 - int(owner->beingBuilt && aircraftState == AIRCRAFT_LANDED))); } - if (nextPosInBounds) { + if (nextPosInBounds) owner->Move(spd, true); - } - // [?] prevent aircraft from gaining unlimited altitude - owner->Move(UpVector * (Clamp(pos.y, gHeight, readMap->GetCurrMaxHeight() + owner->unitDef->wantedHeight * 5.0f) - pos.y), true); + // prevent aircraft from gaining unlimited altitude + owner->Move(UpVector * (Clamp(pos.y, groundHeight, readMap->GetCurrMaxHeight() + owner->unitDef->wantedHeight * 5.0f) - pos.y), true); // bounce away on ground collisions (including water surface) // NOTE: @@ -1154,14 +1120,16 @@ void CStrafeAirMoveType::UpdateAirPhysics(float rudder, float aileron, float ele // to start bouncing with ever-increasing amplitude while // stunned, so the same applies there if (modInfo.allowAircraftToHitGround) { - const bool groundContact = (gHeight > (owner->midPos.y - owner->radius)); + const bool groundContact = (groundHeight > (owner->midPos.y - owner->radius)); const bool handleContact = (aircraftState != AIRCRAFT_LANDED && aircraftState != AIRCRAFT_TAKEOFF); if (groundContact && handleContact) { - owner->Move(UpVector * (gHeight - (owner->midPos.y - owner->radius) + 0.01f), true); + const float3 groundOffset = UpVector * (groundHeight - (owner->midPos.y - owner->radius) + 0.01f); + const float3& groundNormal = CGround::GetNormal(pos.x, pos.z); + + const float impactSpeed = -spd.dot(groundNormal) * int(1 - owner->IsStunned()); - const float3& gNormal = CGround::GetNormal(pos.x, pos.z); - const float impactSpeed = -spd.dot(gNormal) * int(1 - owner->IsStunned()); + owner->Move(groundOffset, true); if (impactSpeed > 0.0f) { // fix for mantis #1355 @@ -1175,10 +1143,11 @@ void CStrafeAirMoveType::UpdateAirPhysics(float rudder, float aileron, float ele owner->SetVelocity(spd + (UpVector * impactSpeed)); } - owner->SetVelocity(spd + (gNormal * impactSpeed * 1.5f)); + owner->SetVelocity(spd + (groundNormal * impactSpeed * 1.5f)); - updir = (gNormal - frontdir * 0.1f); - frontdir = updir.cross(frontdir.cross(updir)); + updir = groundNormal - frontdir * 0.1f; + rightdir = frontdir.cross(updir.Normalize()); + frontdir = updir.cross(rightdir.Normalize()); } } } @@ -1187,6 +1156,7 @@ void CStrafeAirMoveType::UpdateAirPhysics(float rudder, float aileron, float ele rightdir = frontdir.cross(updir); rightdir.Normalize(); updir = rightdir.cross(frontdir); + updir.Normalize(); owner->SetSpeed(spd); owner->UpdateMidAndAimPos(); diff --git a/rts/Sim/MoveTypes/StrafeAirMoveType.h b/rts/Sim/MoveTypes/StrafeAirMoveType.h index 4959ed5107e..4e7c9621718 100644 --- a/rts/Sim/MoveTypes/StrafeAirMoveType.h +++ b/rts/Sim/MoveTypes/StrafeAirMoveType.h @@ -5,6 +5,8 @@ #include "AAirMoveType.h" +struct float4; + /** * Air movement type definition */ @@ -29,15 +31,9 @@ class CStrafeAirMoveType: public AAirMoveType void UpdateManeuver(); void UpdateAttack(); - bool UpdateFlying(float wantedHeight, float engine); + bool UpdateFlying(float wantedHeight, float thrust); void UpdateLanding(); - void UpdateAirPhysics( - float rudder, - float aileron, - float elevator, - float engine, - const float3& engineVector - ); + void UpdateAirPhysics(const float4& controlInputs, const float3& thrustVector); void SetState(AircraftState state) override; void UpdateTakeOff();