From 4ce6abdadf0f9fefa5e3b9ea502b3b231eaebeef Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Thu, 10 Aug 2023 21:29:19 -0500 Subject: [PATCH 1/8] comments --- rts/Sim/Weapons/MissileLauncher.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/rts/Sim/Weapons/MissileLauncher.cpp b/rts/Sim/Weapons/MissileLauncher.cpp index c3f8313eaa..261ff68907 100644 --- a/rts/Sim/Weapons/MissileLauncher.cpp +++ b/rts/Sim/Weapons/MissileLauncher.cpp @@ -70,6 +70,32 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP if (xzTargetDist == 0.0f) return true; + // trajectoryHeight missiles follow a pursuit curve + // https://en.wikipedia.org/wiki/Pursuit_curve + // however, while the basic case of a pursuit curve has an explicit solution + // the case here with a potentially accelerating pursuer has no explicit solution + // and the last linear portion of the trajectory needs to be accounted for + // The curve can still be stated as a differential equation and approximately solved + // I found using a midpoint method to work best + // https://en.wikipedia.org/wiki/Midpoint_method + // + // Following (2D) solution assumes nonzero turnrate + // because a zero turnrate will fail to hit the target + // + // extraHeight = eH = (dist * trajectoryHeight) + // extraHeightTime = eHT = dist / maxSpeed + // dr/dt = r'(t,r,y) = (V0 + a * t) * (rt - r)/distance + // dy/dt = y'(t,r,y) = (V0 + a * t) * (yt + (eH * (1-t/eHT)) - y)/distance + // distance = sqrt( (rt - r)^2 + (yt - y)^2) + // velocity capped at maxSpeed + // r_n+1 = r_n + h*r'(t+0.5,r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) + // y_n+1 = y_n + h*y'(t+0.5,r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) + // + // for midpoint method, we choose h so that we only need to calculate 8 points + // of the curved trajectoryheight controlled portion + // + // For close targets, impact within 8 frames, just use a TestTrajectoryCone check + const float linCoeff = launchDir.y + weaponDef->trajectoryHeight; const float qdrCoeff = -weaponDef->trajectoryHeight / xzTargetDist; const float groundDist = ((avoidFlags & Collision::NOGROUND) == 0)? From 6e6839f903bc857c65ec53e5638953f53bcbbccc Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Thu, 10 Aug 2023 23:34:00 -0500 Subject: [PATCH 2/8] math --- rts/Sim/Weapons/MissileLauncher.cpp | 147 +++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 3 deletions(-) diff --git a/rts/Sim/Weapons/MissileLauncher.cpp b/rts/Sim/Weapons/MissileLauncher.cpp index 261ff68907..a2123f0330 100644 --- a/rts/Sim/Weapons/MissileLauncher.cpp +++ b/rts/Sim/Weapons/MissileLauncher.cpp @@ -86,16 +86,157 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP // extraHeightTime = eHT = dist / maxSpeed // dr/dt = r'(t,r,y) = (V0 + a * t) * (rt - r)/distance // dy/dt = y'(t,r,y) = (V0 + a * t) * (yt + (eH * (1-t/eHT)) - y)/distance - // distance = sqrt( (rt - r)^2 + (yt - y)^2) + // distance = sqrt( (rt - r)^2 + (yt + (eH * (1-t/eHT)) - y)^2) // velocity capped at maxSpeed - // r_n+1 = r_n + h*r'(t+0.5,r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) - // y_n+1 = y_n + h*y'(t+0.5,r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) + // r_n+1 = r_n + h*r'(t+(h/2),r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) + // y_n+1 = y_n + h*y'(t+(h/2),r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) // // for midpoint method, we choose h so that we only need to calculate 8 points // of the curved trajectoryheight controlled portion // // For close targets, impact within 8 frames, just use a TestTrajectoryCone check + std::array mdist = {}; //distance radially the missile has travelled + std::array mheight = {}; //distance vertically the missile has travelled + mdist[0] = 0; + mheight[0] = 0; + + const float maxSpeed = weaponDef->projectilespeed; + const float pSpeed = weaponDef->startvelocity; + const float pAcc = weaponDef->startvelocity; + float dist = srcPos.distance(tgtPos); + float rt = (tgtPos - srcPos).Length2D(); + float yt = (tgtPos.y - srcPos.y); + float eH = (dist * weaponDef->trajectoryHeight); + int eHT = int(dist / maxSpeed); + float hstep = eHT / 8.0f; + + float drdt = 0.0f; + float dydt = 0.0f; + float rt_est = 0.0f; + float yt_est = 0.0f; + float drdt_est = 0.0f; + float dydt_est = 0.0f; + + float t = 0.0f; + //dist = math::sqrt(math::pow((rt - mdist[0]), 2) + math::pow((yt + eH * (1 - (hstep * 0.5f) / eHT) - mheight[0]), 2)); + for (int i = 1; i < 8; i++) { + dist = math::sqrt(math::pow((rt - mdist[i-1]), 2) + math::pow((yt + eH * (1 - t / eHT) - mheight[i-1]), 2)); + drdt = (pSpeed + pAcc * t) * (rt - mdist[i-1]) / dist; + dydt = (pSpeed + pAcc * t) * (yt + eH * (1 - t / eHT) - mheight[i-1]) / dist; + rt_est = mdist[i-1] + hstep * drdt; + yt_est = mheight[i-1] + hstep * dydt; + t = t + hstep; + dist = math::sqrt(math::pow((rt - rt_est), 2) + math::pow((yt + eH * (1 - t / eHT) - yt_est), 2)); + drdt_est = (pSpeed + pAcc * t) * (rt - rt_est) / dist; + dydt_est = (pSpeed + pAcc * t) * (yt + eH * (1 - t / eHT) - yt_est) / dist; + mdist[i] = mdist[i-1] + (hstep * 0.5f) * (drdt + drdt_est); + mheight[i] = mheight[i-1] + (hstep * 0.5f) * (dydt + dydt_est); + + //float drdt_half = (pSpeed + pAcc * hstep * 0.5f) * (rt - mdist[0]) / dist; + //float dydt_half = (pSpeed + pAcc * hstep * 0.5f) * (yt + eH * (1 - (hstep * 0.5f) / eHT) - mheight[0]) / dist; + //mdist[i] = mdist[i-1] + hstep * drdt_half; + //mheight[i] = mheight[i-1] + hstep * dydt_half; + } + + /* + std::array posx = {}; + std::array posy = {}; + std::array posz = {}; + posx[0] = srcPos.x; + posy[0] = srcPos.y; + posz[0] = srcPos.z; + + float pSpeed = weaponDef->startvelocity; + float pAcc = weaponDef->startvelocity; + const float maxSpeed = weaponDef->projectilespeed; + launchDir.y += weaponDef->trajectoryHeight; + launchDir = launchDir.SafeNormalize(); + + float3 varTargPos = tgtPos; + float3 varSrcPos = srcPos; + float3 varDir = launchDir; + float3 targetDir = (varTargPos - varSrcPos).SafeNormalize(); + float targetDist = varSrcPos.distance(varTargPos) + 0.1f; + float origTargetDist = targetDist; + + float extraHeight = (targetDist * weaponDef->trajectoryHeight); + int extraHeightTime = int(std::max(targetDist, maxSpeed) / maxSpeed); + float extraHeightDecay = extraHeight / extraHeightTime; + + float horDiff = (varTargPos - varSrcPos).Length2D() + 0.01f; + float verDiff = (varTargPos.y - varSrcPos.y) + 0.01f; + float dirDiff = math::fabs(targetDir.y - varDir.y); + float ratio = math::fabs(verDiff / horDiff); + + float3 targetLeadDir = (varTargPos - varSrcPos).Normalize(); + float3 targetDirDif = targetDir - varDir; + + for (int i = 0; i < 90; i++) { + + if (srcPos.distance(varSrcPos) > origTargetDist) + { + posx[i] = tgtPos.x; + posy[i] = tgtPos.y; + posz[i] = tgtPos.z; + continue; + } + varTargPos = tgtPos; + targetDir = (tgtPos - varSrcPos).SafeNormalize(); + targetDist = varSrcPos.distance(tgtPos) + 0.1f; + pSpeed += (weaponDef->weaponacceleration * (pSpeed < maxSpeed)); + posx[i] = varSrcPos.x; + posy[i] = varSrcPos.y; + posz[i] = varSrcPos.z; + + if (extraHeightTime > 0) { + extraHeight -= extraHeightDecay; + --extraHeightTime; + + varTargPos.y += extraHeight; + + std::cout << "LOF Pos = " << varSrcPos.x << " " << varSrcPos.y << " " << varSrcPos.z << std::endl; + std::cout << "LOF dir = " << varDir.x << " " << varDir.y << " " << varDir.z << std::endl; + std::cout << "LOF targetPos = " << varTargPos.x << " " << varTargPos.y << " " << varTargPos.z << std::endl; + std::cout << "LOF targetDir = " << targetDir.x << " " << targetDir.y << " " << targetDir.z << std::endl; + + if (varDir.y <= 0.0f) { + horDiff = (varTargPos - varSrcPos).Length2D() + 0.01f; + verDiff = (varTargPos.y - varSrcPos.y) + 0.01f; + dirDiff = math::fabs(targetDir.y - varDir.y); + ratio = math::fabs(verDiff / horDiff); + + varDir.y -= (dirDiff * ratio); + } + else { + varDir.y -= (extraHeightDecay / targetDist); + } + } + + targetLeadDir = (varTargPos - varSrcPos).Normalize(); + targetDirDif = targetLeadDir - varDir; + + if (targetDirDif.SqLength() < Square(weaponDef->turnrate)) { + varDir = targetLeadDir; + } + else { + targetDirDif = (targetDirDif - (varDir * (targetDirDif.dot(varDir)))).SafeNormalize(); + varDir = (varDir + (targetDirDif * weaponDef->turnrate)).SafeNormalize(); + } + + varSrcPos.x = varSrcPos.x + varDir.x * pSpeed; + varSrcPos.y = varSrcPos.y + varDir.y * pSpeed; + varSrcPos.z = varSrcPos.z + varDir.z * pSpeed; + + // ground collision check here + if (((avoidFlags & Collision::NOGROUND) == 0) && (CGround::GetApproximateHeight(varSrcPos) > varSrcPos.y)) + { + return false; + } + std::cout << i << " " << posx[i] << " " << posy[i] << " " << posz[i] << std::endl; + } + */ + const float linCoeff = launchDir.y + weaponDef->trajectoryHeight; const float qdrCoeff = -weaponDef->trajectoryHeight / xzTargetDist; const float groundDist = ((avoidFlags & Collision::NOGROUND) == 0)? From c4569860a01a3520684204c0c9e973837b2421ab Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Fri, 11 Aug 2023 01:30:51 -0500 Subject: [PATCH 3/8] basic checks working --- rts/Sim/Weapons/MissileLauncher.cpp | 154 +++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 5 deletions(-) diff --git a/rts/Sim/Weapons/MissileLauncher.cpp b/rts/Sim/Weapons/MissileLauncher.cpp index a2123f0330..7d9ace915e 100644 --- a/rts/Sim/Weapons/MissileLauncher.cpp +++ b/rts/Sim/Weapons/MissileLauncher.cpp @@ -56,6 +56,13 @@ void CMissileLauncher::FireImpl(const bool scriptCall) WeaponProjectileFactory::LoadProjectile(params); } +#include "Rendering/GlobalRendering.h" +#include "Sim/Misc/GeometricObjects.h" + +#include "Sim/Misc/QuadField.h" +#include "Sim/Misc/CollisionHandler.h" +#include "Sim/Misc/CollisionVolume.h" +#include "System/SpringMath.h" bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtPos, const SWeaponTarget& trg) const { // high-trajectory missiles use parabolic rather than linear ground intersection @@ -103,7 +110,8 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP const float maxSpeed = weaponDef->projectilespeed; const float pSpeed = weaponDef->startvelocity; - const float pAcc = weaponDef->startvelocity; + const float pAcc = weaponDef->weaponacceleration; + float curspeed = weaponDef->startvelocity; float dist = srcPos.distance(tgtPos); float rt = (tgtPos - srcPos).Length2D(); float yt = (tgtPos.y - srcPos.y); @@ -122,14 +130,16 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP //dist = math::sqrt(math::pow((rt - mdist[0]), 2) + math::pow((yt + eH * (1 - (hstep * 0.5f) / eHT) - mheight[0]), 2)); for (int i = 1; i < 8; i++) { dist = math::sqrt(math::pow((rt - mdist[i-1]), 2) + math::pow((yt + eH * (1 - t / eHT) - mheight[i-1]), 2)); - drdt = (pSpeed + pAcc * t) * (rt - mdist[i-1]) / dist; - dydt = (pSpeed + pAcc * t) * (yt + eH * (1 - t / eHT) - mheight[i-1]) / dist; + curspeed = std::min((pSpeed + pAcc * t), maxSpeed); + drdt = curspeed * (rt - mdist[i-1]) / dist; + dydt = curspeed * (yt + eH * (1 - t / eHT) - mheight[i-1]) / dist; rt_est = mdist[i-1] + hstep * drdt; yt_est = mheight[i-1] + hstep * dydt; t = t + hstep; dist = math::sqrt(math::pow((rt - rt_est), 2) + math::pow((yt + eH * (1 - t / eHT) - yt_est), 2)); - drdt_est = (pSpeed + pAcc * t) * (rt - rt_est) / dist; - dydt_est = (pSpeed + pAcc * t) * (yt + eH * (1 - t / eHT) - yt_est) / dist; + curspeed = std::min((pSpeed + pAcc * t), maxSpeed); + drdt_est = curspeed * (rt - rt_est) / dist; + dydt_est = curspeed * (yt + eH * (1 - t / eHT) - yt_est) / dist; mdist[i] = mdist[i-1] + (hstep * 0.5f) * (drdt + drdt_est); mheight[i] = mheight[i-1] + (hstep * 0.5f) * (dydt + dydt_est); @@ -139,6 +149,138 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP //mheight[i] = mheight[i-1] + hstep * dydt_half; } + // debug draw + if (globalRendering->drawDebugTraceRay) { + //launchDir = (tgtPos - srcPos) * XZVector; + //launchDir = launchDir.SafeNormalize(); + for (int i = 1; i < 8; i++) { + geometricObjects->SetColor(geometricObjects->AddLine(srcPos + targetVec * mdist[i-1] + UpVector * mheight[i-1], srcPos + targetVec * mdist[i] + UpVector * mheight[i], 3, 0, GAME_SPEED), 1.0f, 0.0f, 0.0f, 1.0f); + } + + } + + // check for ground collision + float gndDst = 0.0f; + if ((avoidFlags & Collision::NOGROUND) == 0) { + for (int i = 1; i < 8; i++) { + gndDst = CGround::LineGroundCol(srcPos + targetVec * mdist[i - 1] + UpVector * mheight[i - 1], srcPos + targetVec * mdist[i] + UpVector * mheight[i]); + if (gndDst > 0.0f) { + return false; + } + } + // offset terminal location by damageAreaOfEffect, to avoid false positive at very terminal point + gndDst = CGround::LineGroundCol(srcPos + targetVec * mdist[7] + UpVector * mheight[7], tgtPos); + if ((gndDst > 0.0f) && ((gndDst + damages->damageAreaOfEffect) < tgtPos.distance(srcPos + targetVec * mdist[7] + UpVector * mheight[7]))) { + return false; + } + } + + // check for object collision + CollisionQuery cq; + QuadFieldQuery qfQuery; + quadField.GetQuadsOnRay(qfQuery, srcPos, targetVec, xzTargetDist); + + if (qfQuery.quads->empty()) + return true; + + const bool scanForAllies = ((avoidFlags & Collision::NOFRIENDLIES) == 0); + const bool scanForNeutrals = ((avoidFlags & Collision::NONEUTRALS) == 0); + const bool scanForFeatures = ((avoidFlags & Collision::NOFEATURES) == 0); + bool checked = false; + for (const int quadIdx : *qfQuery.quads) { + const CQuadField::Quad& quad = quadField.GetQuad(quadIdx); + + // friendly units in this quad + if (scanForAllies) { + for (const CUnit* u : quad.teamUnits[owner->allyteam]) { + if (u == owner) + continue; + if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) + continue; + + //chord check here + const CollisionVolume* cv = &u->collisionVolume; + const float3 cvRelVec = cv->GetWorldSpacePos(u) - srcPos; + const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); + // chord check to hitPos + const CMatrix44f objTransform = u->GetTransformMatrix(true); + checked = false; + for (int i = 1; i < 8; i++) { + if (cvRelDst < mdist[i]) { + checked = true; + const float delta1 = mdist[i] - mdist[i - 1]; + const float delta2 = cvRelDst - mdist[i - 1]; + const float ratio = delta2 / delta1; + const float hitheight = mheight[i - 1] + ratio*(mheight[i] - mheight[i - 1]); + const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; + if (mheight[i] > mheight[i - 1]) { + // do chord check backwards + if (CCollisionHandler::DetectHit(u, objTransform, srcPos, hitPos, &cq, true)) { + return false; + } + } else { + // do chord check forwards + if (CCollisionHandler::DetectHit(u, objTransform, hitPos, tgtPos, &cq, true)) { + return false; + } + + } + + } + } + if (checked == false) { + // need to check linear end + const float delta1 = xzTargetDist - mdist[7]; + const float delta2 = cvRelDst - mdist[7]; + const float ratio = delta2 / delta1; + const float hitheight = mheight[7] + ratio * (yt - mheight[7]); + const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; + if (yt > mheight[7]) { + // do chord check backwards + if (CCollisionHandler::DetectHit(u, objTransform, srcPos, hitPos, &cq, true)) { + return false; + } + } else { + // do chord check forwards + if (CCollisionHandler::DetectHit(u, objTransform, hitPos, tgtPos, &cq, true)) { + return false; + } + } + } + } + } + + /* + // neutral units in this quad + if (scanForNeutrals) { + for (const CUnit* u : quad.units) { + if (!u->IsNeutral()) + continue; + if (u == owner) + continue; + if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) + continue; + + if (TestTrajectoryConeHelper(from, dir, length, linear, quadratic, spread, 0.0f, u)) + return true; + } + } + + // features in this quad + if (scanForFeatures) { + for (const CFeature* f : quad.features) { + if (!f->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) + continue; + + if (TestTrajectoryConeHelper(from, dir, length, linear, quadratic, spread, 0.0f, f)) + return true; + } + } + */ + } + + return true; + /* std::array posx = {}; std::array posy = {}; @@ -237,6 +379,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP } */ + /* const float linCoeff = launchDir.y + weaponDef->trajectoryHeight; const float qdrCoeff = -weaponDef->trajectoryHeight / xzTargetDist; const float groundDist = ((avoidFlags & Collision::NOGROUND) == 0)? @@ -247,5 +390,6 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP return false; return (!TraceRay::TestTrajectoryCone(srcPos, targetVec, xzTargetDist, linCoeff, qdrCoeff, 0.0f, owner->allyteam, avoidFlags, owner)); + */ } From 8fdba63d9a384b23a88803d7e9f681973af6ee33 Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Fri, 11 Aug 2023 01:39:30 -0500 Subject: [PATCH 4/8] feature and neutral collision --- rts/Sim/Weapons/MissileLauncher.cpp | 108 ++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 6 deletions(-) diff --git a/rts/Sim/Weapons/MissileLauncher.cpp b/rts/Sim/Weapons/MissileLauncher.cpp index 7d9ace915e..0089e2cba9 100644 --- a/rts/Sim/Weapons/MissileLauncher.cpp +++ b/rts/Sim/Weapons/MissileLauncher.cpp @@ -60,6 +60,7 @@ void CMissileLauncher::FireImpl(const bool scriptCall) #include "Sim/Misc/GeometricObjects.h" #include "Sim/Misc/QuadField.h" +#include "Sim/Features/Feature.h" #include "Sim/Misc/CollisionHandler.h" #include "Sim/Misc/CollisionVolume.h" #include "System/SpringMath.h" @@ -250,7 +251,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP } } - /* + // neutral units in this quad if (scanForNeutrals) { for (const CUnit* u : quad.units) { @@ -261,8 +262,55 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; - if (TestTrajectoryConeHelper(from, dir, length, linear, quadratic, spread, 0.0f, u)) - return true; + //chord check here + const CollisionVolume* cv = &u->collisionVolume; + const float3 cvRelVec = cv->GetWorldSpacePos(u) - srcPos; + const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); + // chord check to hitPos + const CMatrix44f objTransform = u->GetTransformMatrix(true); + checked = false; + for (int i = 1; i < 8; i++) { + if (cvRelDst < mdist[i]) { + checked = true; + const float delta1 = mdist[i] - mdist[i - 1]; + const float delta2 = cvRelDst - mdist[i - 1]; + const float ratio = delta2 / delta1; + const float hitheight = mheight[i - 1] + ratio*(mheight[i] - mheight[i - 1]); + const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; + if (mheight[i] > mheight[i - 1]) { + // do chord check backwards + if (CCollisionHandler::DetectHit(u, objTransform, srcPos, hitPos, &cq, true)) { + return false; + } + } else { + // do chord check forwards + if (CCollisionHandler::DetectHit(u, objTransform, hitPos, tgtPos, &cq, true)) { + return false; + } + + } + + } + } + if (checked == false) { + // need to check linear end + const float delta1 = xzTargetDist - mdist[7]; + const float delta2 = cvRelDst - mdist[7]; + const float ratio = delta2 / delta1; + const float hitheight = mheight[7] + ratio * (yt - mheight[7]); + const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; + if (yt > mheight[7]) { + // do chord check backwards + if (CCollisionHandler::DetectHit(u, objTransform, srcPos, hitPos, &cq, true)) { + return false; + } + } else { + // do chord check forwards + if (CCollisionHandler::DetectHit(u, objTransform, hitPos, tgtPos, &cq, true)) { + return false; + } + } + } } } @@ -272,11 +320,59 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP if (!f->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; - if (TestTrajectoryConeHelper(from, dir, length, linear, quadratic, spread, 0.0f, f)) - return true; + //chord check here + const CollisionVolume* cv = &f->collisionVolume; + const float3 cvRelVec = cv->GetWorldSpacePos(f) - srcPos; + const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); + // chord check to hitPos + const CMatrix44f objTransform = f->GetTransformMatrix(true); + checked = false; + for (int i = 1; i < 8; i++) { + if (cvRelDst < mdist[i]) { + checked = true; + const float delta1 = mdist[i] - mdist[i - 1]; + const float delta2 = cvRelDst - mdist[i - 1]; + const float ratio = delta2 / delta1; + const float hitheight = mheight[i - 1] + ratio * (mheight[i] - mheight[i - 1]); + const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; + if (mheight[i] > mheight[i - 1]) { + // do chord check backwards + if (CCollisionHandler::DetectHit(f, objTransform, srcPos, hitPos, &cq, true)) { + return false; + } + } + else { + // do chord check forwards + if (CCollisionHandler::DetectHit(f, objTransform, hitPos, tgtPos, &cq, true)) { + return false; + } + + } + + } + } + if (checked == false) { + // need to check linear end + const float delta1 = xzTargetDist - mdist[7]; + const float delta2 = cvRelDst - mdist[7]; + const float ratio = delta2 / delta1; + const float hitheight = mheight[7] + ratio * (yt - mheight[7]); + const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; + if (yt > mheight[7]) { + // do chord check backwards + if (CCollisionHandler::DetectHit(f, objTransform, srcPos, hitPos, &cq, true)) { + return false; + } + } + else { + // do chord check forwards + if (CCollisionHandler::DetectHit(f, objTransform, hitPos, tgtPos, &cq, true)) { + return false; + } + } + } } } - */ } return true; From 8832956e235f54dad7c3170d04e386b890e6f307 Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Fri, 11 Aug 2023 03:43:23 -0500 Subject: [PATCH 5/8] optimize ground collision check add lots of comments --- rts/Sim/Weapons/MissileLauncher.cpp | 202 +++++++++++----------------- 1 file changed, 78 insertions(+), 124 deletions(-) diff --git a/rts/Sim/Weapons/MissileLauncher.cpp b/rts/Sim/Weapons/MissileLauncher.cpp index 0089e2cba9..2e470cf63b 100644 --- a/rts/Sim/Weapons/MissileLauncher.cpp +++ b/rts/Sim/Weapons/MissileLauncher.cpp @@ -11,6 +11,13 @@ #include "Sim/Units/UnitDef.h" #include "System/SpringMath.h" +#include "Rendering/GlobalRendering.h" +#include "Sim/Misc/GeometricObjects.h" +#include "Sim/Misc/QuadField.h" +#include "Sim/Features/Feature.h" +#include "Sim/Misc/CollisionHandler.h" +#include "Sim/Misc/CollisionVolume.h" + CR_BIND_DERIVED(CMissileLauncher, CWeapon, ) CR_REG_METADATA(CMissileLauncher, ) @@ -56,14 +63,6 @@ void CMissileLauncher::FireImpl(const bool scriptCall) WeaponProjectileFactory::LoadProjectile(params); } -#include "Rendering/GlobalRendering.h" -#include "Sim/Misc/GeometricObjects.h" - -#include "Sim/Misc/QuadField.h" -#include "Sim/Features/Feature.h" -#include "Sim/Misc/CollisionHandler.h" -#include "Sim/Misc/CollisionVolume.h" -#include "System/SpringMath.h" bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtPos, const SWeaponTarget& trg) const { // high-trajectory missiles use parabolic rather than linear ground intersection @@ -84,8 +83,8 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP // the case here with a potentially accelerating pursuer has no explicit solution // and the last linear portion of the trajectory needs to be accounted for // The curve can still be stated as a differential equation and approximately solved - // I found using a midpoint method to work best - // https://en.wikipedia.org/wiki/Midpoint_method + // I found using Heun's method (a midpoint method) to work best + // https://en.wikipedia.org/wiki/Heun%27s_method // // Following (2D) solution assumes nonzero turnrate // because a zero turnrate will fail to hit the target @@ -96,19 +95,23 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP // dy/dt = y'(t,r,y) = (V0 + a * t) * (yt + (eH * (1-t/eHT)) - y)/distance // distance = sqrt( (rt - r)^2 + (yt + (eH * (1-t/eHT)) - y)^2) // velocity capped at maxSpeed - // r_n+1 = r_n + h*r'(t+(h/2),r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) - // y_n+1 = y_n + h*y'(t+(h/2),r+(h/2)*r'(t,r,y),y+(h/2)*y'(t,r,y)) + // ~r_n+1 = r_n + h*r'(t,r,y) + // ~y_n+1 = y_n + h*y'(t,r,y) + // r_n+1 = r_n + (h/2)*( r'(t,r,y) + r'(t+h,~r_n+1,~y_n+1) + // y_n+1 = y_n + (h/2)*( y'(t,r,y) + y'(t+h,~r_n+1,~y_n+1) // - // for midpoint method, we choose h so that we only need to calculate 8 points - // of the curved trajectoryheight controlled portion - // - // For close targets, impact within 8 frames, just use a TestTrajectoryCone check + // for Heun's method, we choose h so that we only need to calculate 8 points + // on the curved trajectoryheight controlled portion - std::array mdist = {}; //distance radially the missile has travelled - std::array mheight = {}; //distance vertically the missile has travelled + std::array mdist = {}; //distance radially the missile has travelled + std::array mheight = {}; //distance vertically the missile has travelled + // put the startpoint and endpoints in the arrays mdist[0] = 0; mheight[0] = 0; + mdist[8] = xzTargetDist; + mheight[8] = (tgtPos.y - srcPos.y); + // set up constants and temp variables const float maxSpeed = weaponDef->projectilespeed; const float pSpeed = weaponDef->startvelocity; const float pAcc = weaponDef->weaponacceleration; @@ -120,6 +123,10 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP int eHT = int(dist / maxSpeed); float hstep = eHT / 8.0f; + // For close targets, impact within 8 frames, just use a TestTrajectoryCone check + if (hstep < 1.0f) + return (CWeapon::HaveFreeLineOfFire(srcPos, tgtPos, trg)); + float drdt = 0.0f; float dydt = 0.0f; float rt_est = 0.0f; @@ -128,7 +135,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP float dydt_est = 0.0f; float t = 0.0f; - //dist = math::sqrt(math::pow((rt - mdist[0]), 2) + math::pow((yt + eH * (1 - (hstep * 0.5f) / eHT) - mheight[0]), 2)); + // perform the Heun's method for (int i = 1; i < 8; i++) { dist = math::sqrt(math::pow((rt - mdist[i-1]), 2) + math::pow((yt + eH * (1 - t / eHT) - mheight[i-1]), 2)); curspeed = std::min((pSpeed + pAcc * t), maxSpeed); @@ -143,47 +150,54 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP dydt_est = curspeed * (yt + eH * (1 - t / eHT) - yt_est) / dist; mdist[i] = mdist[i-1] + (hstep * 0.5f) * (drdt + drdt_est); mheight[i] = mheight[i-1] + (hstep * 0.5f) * (dydt + dydt_est); - - //float drdt_half = (pSpeed + pAcc * hstep * 0.5f) * (rt - mdist[0]) / dist; - //float dydt_half = (pSpeed + pAcc * hstep * 0.5f) * (yt + eH * (1 - (hstep * 0.5f) / eHT) - mheight[0]) / dist; - //mdist[i] = mdist[i-1] + hstep * drdt_half; - //mheight[i] = mheight[i-1] + hstep * dydt_half; } // debug draw if (globalRendering->drawDebugTraceRay) { - //launchDir = (tgtPos - srcPos) * XZVector; - //launchDir = launchDir.SafeNormalize(); - for (int i = 1; i < 8; i++) { + for (int i = 1; i < 9; i++) { geometricObjects->SetColor(geometricObjects->AddLine(srcPos + targetVec * mdist[i-1] + UpVector * mheight[i-1], srcPos + targetVec * mdist[i] + UpVector * mheight[i], 3, 0, GAME_SPEED), 1.0f, 0.0f, 0.0f, 1.0f); } - } // check for ground collision - float gndDst = 0.0f; + int ii = 1; + float delta1 = mdist[ii] - mdist[ii - 1]; + float delta2 = 0.0f; + float ratio = 0.0f; + float hitheight = 0.0f; if ((avoidFlags & Collision::NOGROUND) == 0) { - for (int i = 1; i < 8; i++) { - gndDst = CGround::LineGroundCol(srcPos + targetVec * mdist[i - 1] + UpVector * mheight[i - 1], srcPos + targetVec * mdist[i] + UpVector * mheight[i]); - if (gndDst > 0.0f) { + // do not check last bit of trajectory, sized by damageAreaOfEffect + // to avoid false positive values at very end of trajectory + // this mimics CGround::TrajectoryGroundCol called by parabolic cannon shots + // GetApproximateHeight should already do map boundary checks + for (float dd = 0; dd < xzTargetDist - damages->damageAreaOfEffect; dd += SQUARE_SIZE) { + // make sure we are using correct part of the trajectory + while (dd > mdist[ii]) { + ii = ii + 1; + delta1 = mdist[ii] - mdist[ii - 1]; + } + delta2 = dd - mdist[ii - 1]; + ratio = delta2 / delta1; + hitheight = mheight[ii - 1] + ratio * (mheight[ii] - mheight[ii - 1]); + if (CGround::GetApproximateHeight(srcPos + targetVec*dd) > (srcPos.y + hitheight)) { return false; } } - // offset terminal location by damageAreaOfEffect, to avoid false positive at very terminal point - gndDst = CGround::LineGroundCol(srcPos + targetVec * mdist[7] + UpVector * mheight[7], tgtPos); - if ((gndDst > 0.0f) && ((gndDst + damages->damageAreaOfEffect) < tgtPos.distance(srcPos + targetVec * mdist[7] + UpVector * mheight[7]))) { - return false; - } } // check for object collision - CollisionQuery cq; + // might be better to spin this off into TraceRay.cpp + // but the pursuit curve (and the 8 piecewise linear approximation used here) + // that trajectoryheight missiles follow is singularly unique + // so no need to spin it off until something else needs this QuadFieldQuery qfQuery; quadField.GetQuadsOnRay(qfQuery, srcPos, targetVec, xzTargetDist); if (qfQuery.quads->empty()) return true; + CollisionQuery cq; + const bool scanForAllies = ((avoidFlags & Collision::NOFRIENDLIES) == 0); const bool scanForNeutrals = ((avoidFlags & Collision::NONEUTRALS) == 0); const bool scanForFeatures = ((avoidFlags & Collision::NOFEATURES) == 0); @@ -203,16 +217,15 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP const CollisionVolume* cv = &u->collisionVolume; const float3 cvRelVec = cv->GetWorldSpacePos(u) - srcPos; const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); - // chord check to hitPos const CMatrix44f objTransform = u->GetTransformMatrix(true); - checked = false; - for (int i = 1; i < 8; i++) { + for (int i = 1; i < 9; i++) { if (cvRelDst < mdist[i]) { - checked = true; - const float delta1 = mdist[i] - mdist[i - 1]; - const float delta2 = cvRelDst - mdist[i - 1]; - const float ratio = delta2 / delta1; - const float hitheight = mheight[i - 1] + ratio*(mheight[i] - mheight[i - 1]); + // find the relevant linear segment + // interpolate the location + delta1 = mdist[i] - mdist[i - 1]; + delta2 = cvRelDst - mdist[i - 1]; + ratio = delta2 / delta1; + hitheight = mheight[i - 1] + ratio*(mheight[i] - mheight[i - 1]); const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; if (mheight[i] > mheight[i - 1]) { // do chord check backwards @@ -226,26 +239,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP } } - - } - } - if (checked == false) { - // need to check linear end - const float delta1 = xzTargetDist - mdist[7]; - const float delta2 = cvRelDst - mdist[7]; - const float ratio = delta2 / delta1; - const float hitheight = mheight[7] + ratio * (yt - mheight[7]); - const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; - if (yt > mheight[7]) { - // do chord check backwards - if (CCollisionHandler::DetectHit(u, objTransform, srcPos, hitPos, &cq, true)) { - return false; - } - } else { - // do chord check forwards - if (CCollisionHandler::DetectHit(u, objTransform, hitPos, tgtPos, &cq, true)) { - return false; - } + break; } } } @@ -266,49 +260,30 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP const CollisionVolume* cv = &u->collisionVolume; const float3 cvRelVec = cv->GetWorldSpacePos(u) - srcPos; const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); - // chord check to hitPos const CMatrix44f objTransform = u->GetTransformMatrix(true); - checked = false; - for (int i = 1; i < 8; i++) { + for (int i = 1; i < 9; i++) { if (cvRelDst < mdist[i]) { - checked = true; - const float delta1 = mdist[i] - mdist[i - 1]; - const float delta2 = cvRelDst - mdist[i - 1]; - const float ratio = delta2 / delta1; - const float hitheight = mheight[i - 1] + ratio*(mheight[i] - mheight[i - 1]); + // find the relevant linear segment + // interpolate the location + delta1 = mdist[i] - mdist[i - 1]; + delta2 = cvRelDst - mdist[i - 1]; + ratio = delta2 / delta1; + hitheight = mheight[i - 1] + ratio * (mheight[i] - mheight[i - 1]); const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; if (mheight[i] > mheight[i - 1]) { // do chord check backwards if (CCollisionHandler::DetectHit(u, objTransform, srcPos, hitPos, &cq, true)) { return false; } - } else { + } + else { // do chord check forwards if (CCollisionHandler::DetectHit(u, objTransform, hitPos, tgtPos, &cq, true)) { return false; } - - } - } - } - if (checked == false) { - // need to check linear end - const float delta1 = xzTargetDist - mdist[7]; - const float delta2 = cvRelDst - mdist[7]; - const float ratio = delta2 / delta1; - const float hitheight = mheight[7] + ratio * (yt - mheight[7]); - const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; - if (yt > mheight[7]) { - // do chord check backwards - if (CCollisionHandler::DetectHit(u, objTransform, srcPos, hitPos, &cq, true)) { - return false; - } - } else { - // do chord check forwards - if (CCollisionHandler::DetectHit(u, objTransform, hitPos, tgtPos, &cq, true)) { - return false; } + break; } } } @@ -324,16 +299,15 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP const CollisionVolume* cv = &f->collisionVolume; const float3 cvRelVec = cv->GetWorldSpacePos(f) - srcPos; const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); - // chord check to hitPos const CMatrix44f objTransform = f->GetTransformMatrix(true); - checked = false; - for (int i = 1; i < 8; i++) { + for (int i = 1; i < 9; i++) { if (cvRelDst < mdist[i]) { - checked = true; - const float delta1 = mdist[i] - mdist[i - 1]; - const float delta2 = cvRelDst - mdist[i - 1]; - const float ratio = delta2 / delta1; - const float hitheight = mheight[i - 1] + ratio * (mheight[i] - mheight[i - 1]); + // find the relevant linear segment + // interpolate the location + delta1 = mdist[i] - mdist[i - 1]; + delta2 = cvRelDst - mdist[i - 1]; + ratio = delta2 / delta1; + hitheight = mheight[i - 1] + ratio * (mheight[i] - mheight[i - 1]); const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; if (mheight[i] > mheight[i - 1]) { // do chord check backwards @@ -348,27 +322,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP } } - - } - } - if (checked == false) { - // need to check linear end - const float delta1 = xzTargetDist - mdist[7]; - const float delta2 = cvRelDst - mdist[7]; - const float ratio = delta2 / delta1; - const float hitheight = mheight[7] + ratio * (yt - mheight[7]); - const float3 hitPos = srcPos + targetVec * cvRelDst + UpVector * hitheight; - if (yt > mheight[7]) { - // do chord check backwards - if (CCollisionHandler::DetectHit(f, objTransform, srcPos, hitPos, &cq, true)) { - return false; - } - } - else { - // do chord check forwards - if (CCollisionHandler::DetectHit(f, objTransform, hitPos, tgtPos, &cq, true)) { - return false; - } + break; } } } From 2a2cb1e3fb8699fe43d51244adf3c89a3a83fb8e Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Fri, 11 Aug 2023 03:52:04 -0500 Subject: [PATCH 6/8] clear block of unused code --- rts/Sim/Weapons/MissileLauncher.cpp | 111 ---------------------------- 1 file changed, 111 deletions(-) diff --git a/rts/Sim/Weapons/MissileLauncher.cpp b/rts/Sim/Weapons/MissileLauncher.cpp index 2e470cf63b..b09521d9e9 100644 --- a/rts/Sim/Weapons/MissileLauncher.cpp +++ b/rts/Sim/Weapons/MissileLauncher.cpp @@ -330,116 +330,5 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP } return true; - - /* - std::array posx = {}; - std::array posy = {}; - std::array posz = {}; - posx[0] = srcPos.x; - posy[0] = srcPos.y; - posz[0] = srcPos.z; - - float pSpeed = weaponDef->startvelocity; - float pAcc = weaponDef->startvelocity; - const float maxSpeed = weaponDef->projectilespeed; - launchDir.y += weaponDef->trajectoryHeight; - launchDir = launchDir.SafeNormalize(); - - float3 varTargPos = tgtPos; - float3 varSrcPos = srcPos; - float3 varDir = launchDir; - float3 targetDir = (varTargPos - varSrcPos).SafeNormalize(); - float targetDist = varSrcPos.distance(varTargPos) + 0.1f; - float origTargetDist = targetDist; - - float extraHeight = (targetDist * weaponDef->trajectoryHeight); - int extraHeightTime = int(std::max(targetDist, maxSpeed) / maxSpeed); - float extraHeightDecay = extraHeight / extraHeightTime; - - float horDiff = (varTargPos - varSrcPos).Length2D() + 0.01f; - float verDiff = (varTargPos.y - varSrcPos.y) + 0.01f; - float dirDiff = math::fabs(targetDir.y - varDir.y); - float ratio = math::fabs(verDiff / horDiff); - - float3 targetLeadDir = (varTargPos - varSrcPos).Normalize(); - float3 targetDirDif = targetDir - varDir; - - for (int i = 0; i < 90; i++) { - - if (srcPos.distance(varSrcPos) > origTargetDist) - { - posx[i] = tgtPos.x; - posy[i] = tgtPos.y; - posz[i] = tgtPos.z; - continue; - } - varTargPos = tgtPos; - targetDir = (tgtPos - varSrcPos).SafeNormalize(); - targetDist = varSrcPos.distance(tgtPos) + 0.1f; - pSpeed += (weaponDef->weaponacceleration * (pSpeed < maxSpeed)); - posx[i] = varSrcPos.x; - posy[i] = varSrcPos.y; - posz[i] = varSrcPos.z; - - if (extraHeightTime > 0) { - extraHeight -= extraHeightDecay; - --extraHeightTime; - - varTargPos.y += extraHeight; - - std::cout << "LOF Pos = " << varSrcPos.x << " " << varSrcPos.y << " " << varSrcPos.z << std::endl; - std::cout << "LOF dir = " << varDir.x << " " << varDir.y << " " << varDir.z << std::endl; - std::cout << "LOF targetPos = " << varTargPos.x << " " << varTargPos.y << " " << varTargPos.z << std::endl; - std::cout << "LOF targetDir = " << targetDir.x << " " << targetDir.y << " " << targetDir.z << std::endl; - - if (varDir.y <= 0.0f) { - horDiff = (varTargPos - varSrcPos).Length2D() + 0.01f; - verDiff = (varTargPos.y - varSrcPos.y) + 0.01f; - dirDiff = math::fabs(targetDir.y - varDir.y); - ratio = math::fabs(verDiff / horDiff); - - varDir.y -= (dirDiff * ratio); - } - else { - varDir.y -= (extraHeightDecay / targetDist); - } - } - - targetLeadDir = (varTargPos - varSrcPos).Normalize(); - targetDirDif = targetLeadDir - varDir; - - if (targetDirDif.SqLength() < Square(weaponDef->turnrate)) { - varDir = targetLeadDir; - } - else { - targetDirDif = (targetDirDif - (varDir * (targetDirDif.dot(varDir)))).SafeNormalize(); - varDir = (varDir + (targetDirDif * weaponDef->turnrate)).SafeNormalize(); - } - - varSrcPos.x = varSrcPos.x + varDir.x * pSpeed; - varSrcPos.y = varSrcPos.y + varDir.y * pSpeed; - varSrcPos.z = varSrcPos.z + varDir.z * pSpeed; - - // ground collision check here - if (((avoidFlags & Collision::NOGROUND) == 0) && (CGround::GetApproximateHeight(varSrcPos) > varSrcPos.y)) - { - return false; - } - std::cout << i << " " << posx[i] << " " << posy[i] << " " << posz[i] << std::endl; - } - */ - - /* - const float linCoeff = launchDir.y + weaponDef->trajectoryHeight; - const float qdrCoeff = -weaponDef->trajectoryHeight / xzTargetDist; - const float groundDist = ((avoidFlags & Collision::NOGROUND) == 0)? - CGround::TrajectoryGroundCol(srcPos, targetVec, xzTargetDist, linCoeff, qdrCoeff): - -1.0f; - - if (groundDist > 0.0f) - return false; - - return (!TraceRay::TestTrajectoryCone(srcPos, targetVec, xzTargetDist, linCoeff, qdrCoeff, 0.0f, owner->allyteam, avoidFlags, owner)); - */ } From 126eb1c0b4550e6ba4331cdef6a53a39433d5e0e Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Fri, 11 Aug 2023 13:48:19 -0500 Subject: [PATCH 7/8] Change to missileProjectile to correct unstable behavior for high wobble missiles aimed at high elevation --- .../WeaponProjectiles/MissileProjectile.cpp | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/rts/Sim/Projectiles/WeaponProjectiles/MissileProjectile.cpp b/rts/Sim/Projectiles/WeaponProjectiles/MissileProjectile.cpp index 2b2d17e856..859ca35996 100644 --- a/rts/Sim/Projectiles/WeaponProjectiles/MissileProjectile.cpp +++ b/rts/Sim/Projectiles/WeaponProjectiles/MissileProjectile.cpp @@ -161,10 +161,34 @@ void CMissileProjectile::Update() const float dirDiff = math::fabs(targetDir.y - dir.y); const float ratio = math::fabs(verDiff / horDiff); - dir.y -= (dirDiff * ratio); + // tilt missile up if + // 1. missile is pointing below target + // 2. AND missile height is below target + // This compensates for high wobble zero turnrate missiles aiming at high elevations + // Prevents these missiles from quickly turing directly downwards if wobble + // causes them to undershoot their elevated target + if (((targetDir.y - dir.y) > 0.0f) && ((targetPos.y - extraHeight - pos.y) > 0.0f)) { + dir.y += (dirDiff * ratio); + } + else { + dir.y -= (dirDiff * ratio); + } + } else { // missile is still ascending - dir.y -= (extraHeightDecay / targetDist); + + // tilt missile up if + // 1. missile is pointing below target + // 2. AND missile height is below target + // This compensates for high wobble zero turnrate missiles aiming at high elevations + // Lets these missiles continue ascending to an elevated target + // even if wobble causes them to temporarily undershoot their elevated target + if ( ((targetDir.y - dir.y) > 0.0f) && ((targetPos.y - extraHeight - pos.y) > 0.0f) ) { + dir.y += (extraHeightDecay / targetDist); + } + else { + dir.y -= (extraHeightDecay / targetDist); + } } } From 270062e2e66346b23daa7f0939e4e642c48d8a4f Mon Sep 17 00:00:00 2001 From: Kyle Shepherd Date: Tue, 15 Aug 2023 20:15:59 -0500 Subject: [PATCH 8/8] Code review, some comment edits --- rts/Sim/Weapons/MissileLauncher.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/rts/Sim/Weapons/MissileLauncher.cpp b/rts/Sim/Weapons/MissileLauncher.cpp index b09521d9e9..51af49fdcd 100644 --- a/rts/Sim/Weapons/MissileLauncher.cpp +++ b/rts/Sim/Weapons/MissileLauncher.cpp @@ -65,7 +65,7 @@ void CMissileLauncher::FireImpl(const bool scriptCall) bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtPos, const SWeaponTarget& trg) const { - // high-trajectory missiles use parabolic rather than linear ground intersection + // high-trajectory missiles use curved path rather than linear ground intersection if (weaponDef->trajectoryHeight <= 0.0f) return (CWeapon::HaveFreeLineOfFire(srcPos, tgtPos, trg)); @@ -100,8 +100,9 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP // r_n+1 = r_n + (h/2)*( r'(t,r,y) + r'(t+h,~r_n+1,~y_n+1) // y_n+1 = y_n + (h/2)*( y'(t,r,y) + y'(t+h,~r_n+1,~y_n+1) // - // for Heun's method, we choose h so that we only need to calculate 8 points + // for Heun's method, we choose h so that we only need to calculate 7 points // on the curved trajectoryheight controlled portion + // so the final curve can be approximated by 8 straight line segments std::array mdist = {}; //distance radially the missile has travelled std::array mheight = {}; //distance vertically the missile has travelled @@ -137,6 +138,8 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP float t = 0.0f; // perform the Heun's method for (int i = 1; i < 8; i++) { + // due to maxSpeed boundary, and parameters of the pursuit curve, dist cannot be zero + // but if a divide by zero error does somehow occur here, a zero check can be added dist = math::sqrt(math::pow((rt - mdist[i-1]), 2) + math::pow((yt + eH * (1 - t / eHT) - mheight[i-1]), 2)); curspeed = std::min((pSpeed + pAcc * t), maxSpeed); drdt = curspeed * (rt - mdist[i-1]) / dist; @@ -160,6 +163,10 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP } // check for ground collision + // might be better to spin this off into TraceRay.cpp + // but the pursuit curve (and the 8 piecewise linear approximation used here) + // that trajectoryheight missiles follow is singularly unique + // so no need to spin it off until something else needs this int ii = 1; float delta1 = mdist[ii] - mdist[ii - 1]; float delta2 = 0.0f; @@ -201,7 +208,6 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP const bool scanForAllies = ((avoidFlags & Collision::NOFRIENDLIES) == 0); const bool scanForNeutrals = ((avoidFlags & Collision::NONEUTRALS) == 0); const bool scanForFeatures = ((avoidFlags & Collision::NOFEATURES) == 0); - bool checked = false; for (const int quadIdx : *qfQuery.quads) { const CQuadField::Quad& quad = quadField.GetQuad(quadIdx); @@ -213,7 +219,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; - //chord check here + // chord check here const CollisionVolume* cv = &u->collisionVolume; const float3 cvRelVec = cv->GetWorldSpacePos(u) - srcPos; const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); @@ -256,7 +262,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP if (!u->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; - //chord check here + // chord check here const CollisionVolume* cv = &u->collisionVolume; const float3 cvRelVec = cv->GetWorldSpacePos(u) - srcPos; const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist); @@ -295,7 +301,7 @@ bool CMissileLauncher::HaveFreeLineOfFire(const float3 srcPos, const float3 tgtP if (!f->HasCollidableStateBit(CSolidObject::CSTATE_BIT_QUADMAPRAYS)) continue; - //chord check here + // chord check here const CollisionVolume* cv = &f->collisionVolume; const float3 cvRelVec = cv->GetWorldSpacePos(f) - srcPos; const float cvRelDst = Clamp(cvRelVec.dot(targetVec), 0.0f, xzTargetDist);