16
16
// System headers
17
17
#define GLM_ENABLE_EXPERIMENTAL
18
18
#include < glm/gtx/norm.hpp>
19
+ #include < glm/gtx/rotate_vector.hpp>
19
20
20
21
/* common implementation headers */
21
22
#include " BZDBCache.h"
@@ -61,6 +62,7 @@ GuidedMissileStrategy::GuidedMissileStrategy(ShotPath* _path) :
61
62
const auto dir = glm::normalize (vel);
62
63
azimuth = limitAngle (atan2f (dir[1 ], dir[0 ]));
63
64
elevation = limitAngle (atan2f (dir[2 ], hypotf (dir[1 ], dir[0 ])));
65
+ currentDirection = dir;
64
66
65
67
// initialize segments
66
68
currentTime = getPath ().getStartTime ();
@@ -176,37 +178,63 @@ void GuidedMissileStrategy::update(float dt)
176
178
const auto &targetPos = target->getPosition ();
177
179
auto desiredDir = targetPos - nextPos;
178
180
desiredDir[2 ] += target->getMuzzleHeight (); // right between the eyes
181
+ desiredDir = glm::normalize (desiredDir);
179
182
180
183
// 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);
184
190
185
191
float gmissileAng = BZDB.eval (StateDatabase::BZDB_GMTURNANGLE);
192
+ float maxAngle = dt * gmissileAng;
193
+ bool limitingNeeded = true ;
186
194
187
195
// 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
+ }
202
228
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;
204
236
}
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);
210
238
211
239
renderTimes++;
212
240
if (puffTime < 0 )
@@ -258,9 +286,8 @@ void GuidedMissileStrategy::update(float dt)
258
286
segments.pop_back ();
259
287
260
288
// update shot
261
- newDirection *= shotSpeed;
262
289
setPosition (nextPos);
263
- setVelocity (newDirection );
290
+ setVelocity (currentDirection * shotSpeed );
264
291
}
265
292
266
293
float GuidedMissileStrategy::checkBuildings (const Ray& ray)
@@ -284,6 +311,8 @@ float GuidedMissileStrategy::checkBuildings(const Ray& ray)
284
311
World::getWorld ()->getTeleporter (target, outFace);
285
312
teleporter->getPointWRT (*outTeleporter, face, outFace,
286
313
nextPos, NULL , &azimuth);
314
+ currentDirection[0 ] = cosf (azimuth) * cosf (elevation);
315
+ currentDirection[1 ] = sinf (azimuth) * cosf (elevation);
287
316
return t / shotSpeed;
288
317
}
289
318
else if (building)
@@ -415,6 +444,7 @@ void GuidedMissileStrategy::readUpdate(uint16_t code, const void* msg)
415
444
// fix up dependent variables
416
445
const auto vel = getPath ().getVelocity ();
417
446
auto dir = glm::normalize (vel);
447
+ currentDirection = dir;
418
448
azimuth = limitAngle (atan2f (dir[1 ], dir[0 ]));
419
449
elevation = limitAngle (atan2f (dir[2 ], hypotf (dir[1 ], dir[0 ])));
420
450
const auto pos = getPath ().getPosition ();
0 commit comments