Skip to content

Commit a460c80

Browse files
committed
Missile guidance computed with "quaternions"
1 parent a28557c commit a460c80

File tree

2 files changed

+56
-25
lines changed

2 files changed

+56
-25
lines changed

src/bzflag/GuidedMissleStrategy.cxx

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// System headers
1717
#define GLM_ENABLE_EXPERIMENTAL
1818
#include <glm/gtx/norm.hpp>
19+
#include <glm/gtx/rotate_vector.hpp>
1920

2021
/* common implementation headers */
2122
#include "BZDBCache.h"
@@ -61,6 +62,7 @@ GuidedMissileStrategy::GuidedMissileStrategy(ShotPath* _path) :
6162
const auto dir = glm::normalize(vel);
6263
azimuth = limitAngle(atan2f(dir[1], dir[0]));
6364
elevation = limitAngle(atan2f(dir[2], hypotf(dir[1], dir[0])));
65+
currentDirection = dir;
6466

6567
// initialize segments
6668
currentTime = getPath().getStartTime();
@@ -176,37 +178,63 @@ void GuidedMissileStrategy::update(float dt)
176178
const auto &targetPos = target->getPosition();
177179
auto desiredDir = targetPos - nextPos;
178180
desiredDir[2] += target->getMuzzleHeight(); // right between the eyes
181+
desiredDir = glm::normalize(desiredDir);
179182

180183
// compute desired angles
181-
float newAzimuth = atan2f(desiredDir[1], desiredDir[0]);
182-
float newElevation = atan2f(desiredDir[2],
183-
hypotf(desiredDir[1], desiredDir[0]));
184+
// The cross product gives a vector that is normal to the plan that has
185+
// both the current direction and the desired one
186+
auto rotationAxis = glm::cross(currentDirection, desiredDir);
187+
// The magnitude is simply the sin between the two vectors, as they are
188+
// both normalize. I need the length, but the square is the same
189+
float sin2Theta = glm::length2(rotationAxis);
184190

185191
float gmissileAng = BZDB.eval(StateDatabase::BZDB_GMTURNANGLE);
192+
float maxAngle = dt * gmissileAng;
193+
bool limitingNeeded = true;
186194

187195
// compute new azimuth
188-
float deltaAzimuth = limitAngle(newAzimuth - azimuth);
189-
if (fabsf(deltaAzimuth) <= dt * gmissileAng)
190-
azimuth = limitAngle(newAzimuth);
191-
else if (deltaAzimuth > 0.0f)
192-
azimuth = limitAngle(azimuth + dt * gmissileAng);
193-
else
194-
azimuth = limitAngle(azimuth - dt * gmissileAng);
195-
196-
// compute new elevation
197-
float deltaElevation = limitAngle(newElevation - elevation);
198-
if (fabsf(deltaElevation) <= dt * gmissileAng)
199-
elevation = limitAngle(newElevation);
200-
else if (deltaElevation > 0.0f)
201-
elevation = limitAngle(elevation + dt * gmissileAng);
196+
// I assume that maxAngle is very little (as dt should be) so sin2Theta
197+
// should be like Theta*Theta
198+
if (sin2Theta <= powf(maxAngle, 2))
199+
{
200+
// It seems that the rotation angle is less than the maxAngle, but
201+
// care... could be 180 degree. So only in this case I compute the
202+
// cos
203+
float cosTheta = glm::dot(desiredDir, currentDirection);
204+
if (cosTheta > 0.0f)
205+
// Ok no need for limiting GM.
206+
limitingNeeded = false;
207+
else
208+
{
209+
// special case when vectors in opposite directions:
210+
// there is no "ideal" rotation axis
211+
// So try one; any will do as long as it's perpendicular to start
212+
rotationAxis = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), desiredDir);
213+
if (glm::length2(rotationAxis) < 0.01f)
214+
// bad luck, they were parallel, try again!
215+
rotationAxis = glm::cross(glm::vec3(1.0f, 0.0f, 0.0f), desiredDir);
216+
}
217+
}
218+
219+
glm::vec3 newDirection;
220+
221+
if (limitingNeeded)
222+
{
223+
// Normalize the rotation axis as needed by glm::rotate
224+
rotationAxis = glm::normalize(rotationAxis);
225+
// And rotate at max angle
226+
newDirection = glm::rotate(currentDirection, maxAngle, rotationAxis);
227+
}
202228
else
203-
elevation = limitAngle(elevation - dt * gmissileAng);
229+
// The new direction will be the desired one
230+
newDirection = desiredDir;
231+
232+
azimuth = limitAngle(atan2f(newDirection[1], newDirection[0]));
233+
elevation = limitAngle(atan2f(newDirection[2],
234+
hypotf(newDirection[1], newDirection[0])));
235+
currentDirection = newDirection;
204236
}
205-
auto newDirection = glm::vec3(
206-
cosf(azimuth) * cosf(elevation),
207-
sinf(azimuth) * cosf(elevation),
208-
sinf(elevation));
209-
Ray ray = Ray(nextPos, newDirection);
237+
Ray ray = Ray(nextPos, currentDirection);
210238

211239
renderTimes++;
212240
if (puffTime < 0 )
@@ -258,9 +286,8 @@ void GuidedMissileStrategy::update(float dt)
258286
segments.pop_back();
259287

260288
// update shot
261-
newDirection *= shotSpeed;
262289
setPosition(nextPos);
263-
setVelocity(newDirection);
290+
setVelocity(currentDirection * shotSpeed);
264291
}
265292

266293
float GuidedMissileStrategy::checkBuildings(const Ray& ray)
@@ -284,6 +311,8 @@ float GuidedMissileStrategy::checkBuildings(const Ray& ray)
284311
World::getWorld()->getTeleporter(target, outFace);
285312
teleporter->getPointWRT(*outTeleporter, face, outFace,
286313
nextPos, NULL, &azimuth);
314+
currentDirection[0] = cosf(azimuth) * cosf(elevation);
315+
currentDirection[1] = sinf(azimuth) * cosf(elevation);
287316
return t / shotSpeed;
288317
}
289318
else if (building)
@@ -415,6 +444,7 @@ void GuidedMissileStrategy::readUpdate(uint16_t code, const void* msg)
415444
// fix up dependent variables
416445
const auto vel = getPath().getVelocity();
417446
auto dir = glm::normalize(vel);
447+
currentDirection = dir;
418448
azimuth = limitAngle(atan2f(dir[1], dir[0]));
419449
elevation = limitAngle(atan2f(dir[2], hypotf(dir[1], dir[0])));
420450
const auto pos = getPath().getPosition();

src/bzflag/GuidedMissleStrategy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class GuidedMissileStrategy : public ShotStrategy
6161
TimeKeeper lastPuff;
6262
mutable bool needUpdate;
6363
PlayerId lastTarget;
64+
glm::vec3 currentDirection;
6465
};
6566

6667

0 commit comments

Comments
 (0)