Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
plane_display.cpp
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 
3 #include <cmath>
4 #include <algorithm>
5 #include <vector>
6 #include <boost/assign/std/vector.hpp>
7 #include "lin_time.h" // GetElapsedTime
8 #include "cmd/unit_generic.h"
9 #include "cmd/unit_util.h"
10 #include "gfxlib.h"
11 #include "gfx/quaternion.h"
12 #include "viewarea.h"
13 #include "plane_display.h"
14 
15 namespace
16 {
17 
18 float Degree2Radian(float angle)
19 {
20  const float ratio = M_PI / 180.0;
21  return angle * ratio;
22 }
23 
25 {
26  using namespace Radar;
27 
28  switch (threat)
29  {
31  return 20.0; // Fast pulsation
32 
34  return 7.5; // Slow pulsation
35 
36  default:
37  return 0.0; // No pulsation
38  }
39 }
40 
41 } // anonymous namespace
42 
43 namespace Radar
44 {
45 
47  : finalCameraAngle(Degree2Radian(30), Degree2Radian(0), Degree2Radian(0)),
48  currentCameraAngle(finalCameraAngle),
49  radarTime(0.0),
50  lastAnimationTime(0.0)
51 {
52  using namespace boost::assign; // vector::operator+=
53 
55 
56  // Calculate ground plane
57  const float edges = 32;
58  const float full = 2 * M_PI;
59  const float step = full / edges;
60  for (float angle = 0.0; angle < full; angle += step)
61  {
62  groundPlane.push_back(Vector(cosf(angle), 0.0f, sinf(angle)));
63  }
64 
65  // Sequences start in 1 and ends in 0
66  nothingSequence += 0.0;
67  bounceSequence += 1.0, 0.9999, 0.9991, 0.9964, 0.9900, 0.9775, 0.9559, 0.9216, 0.8704, 0.7975, 0.6975, 0.5644, 0.3916, 0.1719, 0.0, 0.1287, 0.2164, 0.2535, 0.2297, 0.1343, 0.0, 0.0660, 0.0405, 0.0, 0.0341, 0.0;
68  cosineSequence += 1.0, 0.999391, 0.997564, 0.994522, 0.990268, 0.984808, 0.978148, 0.970296, 0.961262, 0.951057, 0.939693, 0.927184, 0.913545, 0.898794, 0.882948, 0.866025, 0.848048, 0.829038, 0.809017, 0.788011, 0.766044, 0.743145, 0.71934, 0.694658, 0.669131, 0.642788, 0.615662, 0.587785, 0.559193, 0.529919, 0.5, 0.469472, 0.438371, 0.406737, 0.374607, 0.34202, 0.309017, 0.275637, 0.241922, 0.207911, 0.173648, 0.139173, 0.104528, 0.069756, 0.034899, 0.0;
69 }
70 
72 {
73  const float cosx = cosf(currentCameraAngle.x);
74  const float cosy = cosf(currentCameraAngle.y);
75  const float cosz = cosf(currentCameraAngle.z);
76  const float sinx = sinf(currentCameraAngle.x);
77  const float siny = sinf(currentCameraAngle.y);
78  const float sinz = sinf(currentCameraAngle.z);
79 
80  xrotation = Vector(cosy * cosz,
81  sinx * siny * cosz - cosx * sinz,
82  cosx * siny * cosz + sinx * sinz);
83  yrotation = Vector(cosy * sinz,
84  cosx * cosz + sinx * siny * sinz,
85  cosx * siny * sinz - sinx * cosz);
86  zrotation = Vector(-siny,
87  sinx * cosy,
88  cosx * cosy);
89 }
90 
91 void PlaneDisplay::PrepareAnimation(const Vector& fromAngle,
92  const Vector& toAngle,
93  const AngleSequence& xsequence,
94  const AngleSequence& ysequence,
95  const AngleSequence& zsequence)
96 {
97  AnimationItem firstItem;
98  firstItem.duration = 0.0;
99  firstItem.position = fromAngle;
100  animation.push(firstItem);
101 
102  float duration = 2.0;
103 
104  // Use the longest-running sequence and zero-pad shorter sequuences
105  AngleSequence::size_type longestSequenceSize = xsequence.size();
106  longestSequenceSize = std::max(longestSequenceSize, ysequence.size());
107  longestSequenceSize = std::max(longestSequenceSize, zsequence.size());
108  for (AngleSequence::size_type i = 0; i < longestSequenceSize; ++i)
109  {
110  float xentry = (i < xsequence.size()) ? xsequence[i] : 0.0;
111  float yentry = (i < ysequence.size()) ? ysequence[i] : 0.0;
112  float zentry = (i < zsequence.size()) ? zsequence[i] : 0.0;
113  float xangle = toAngle.x + xentry * (fromAngle.x - toAngle.x);
114  float yangle = toAngle.y + yentry * (fromAngle.y - toAngle.y);
115  float zangle = toAngle.z + zentry * (fromAngle.z - toAngle.z);
116  AnimationItem item;
117  item.duration = duration;
118  item.position = Vector(xangle, yangle, zangle);
119  animation.push(item);
120  duration = 0.05;
121  }
122 
123  AnimationItem finalItem;
124  finalItem.duration = duration;
125  finalItem.position = toAngle;
126  animation.push(finalItem);
127 }
128 
130 {
131  // Bounce from upright position
132  Vector undockCameraAngle(Degree2Radian(90), finalCameraAngle.y, finalCameraAngle.z);
134 }
135 
137 {
138  // Full rotation around y-axis
139  Vector jumpCameraAngle(finalCameraAngle.x, Degree2Radian(360), finalCameraAngle.z);
141 }
142 
143 void PlaneDisplay::Draw(const Sensor& sensor,
144  VSSprite *nearSprite,
145  VSSprite *distantSprite)
146 {
147  assert(nearSprite || distantSprite); // There should be at least one radar display
148 
150 
151  leftRadar.SetSprite(nearSprite);
152  rightRadar.SetSprite(distantSprite);
153 
154  if (nearSprite)
155  nearSprite->Draw();
156  if (distantSprite)
157  distantSprite->Draw();
158 
159  Sensor::TrackCollection tracks = sensor.FindTracksInRange();
160 
161  Animate();
162 
165  GFXEnable(SMOOTH);
166 
167  DrawNear(sensor, tracks);
168  DrawDistant(sensor, tracks);
169 
170  GFXPointSize(1);
174 }
175 
177 {
178  if (!animation.empty())
179  {
180  if (radarTime > lastAnimationTime + animation.front().duration)
181  {
182  currentCameraAngle = animation.front().position;
184  animation.pop();
186  }
187  }
188 }
189 
190 Vector PlaneDisplay::Projection(const ViewArea& radarView, const Vector& position)
191 {
192  // 1. Rotate
193  float rx = position.Dot(xrotation);
194  float ry = position.Dot(yrotation);
195  float rz = position.Dot(zrotation);
196 
197  // 2. Project perspective
198  // Using the symmetric viewing volume where right = -left and top = -bottom
199  // gives us this perspective projection matrix
200  // n/r 0 0 0
201  // 0 n/t 0 0
202  // 0 0 -(f+n)/(f-n) -2fn/(f-n)
203  // 0 0 -1 0
204  // location = M_perspective * rotatedPosition
205  const float nearDistance = -0.5; // -0.25 => zoom out, -0.75 => zoom in
206  const float farDistance = 0.5;
207  const float top = 0.5;
208  const float right = 0.5;
209  float x = rx * (nearDistance / right);
210  float y = ry * (nearDistance / top);
211  float z = (rz * (- (farDistance + nearDistance) / (farDistance - nearDistance)) - 2.0 * farDistance * nearDistance / (farDistance - nearDistance));
212 
213  // 3. Scale onto radarView
214  return radarView.Scale(Vector(x, y, z));
215 }
216 
217 void PlaneDisplay::DrawGround(const Sensor& sensor, const ViewArea& radarView)
218 {
219  GFXColor groundColor = radarView.GetColor();
220  const float outer = 3.0 / 3.0;
221  const float middle = 2.0 / 3.0;
222  const float inner = 1.0 / 3.0;
223 
224  groundColor.a = 0.1;
225  GFXColorf(groundColor);
226  GFXLineWidth(0.5);
227  GFXBegin(GFXPOLY);
228  for (std::vector<Vector>::const_iterator it = groundPlane.begin(); it != groundPlane.end(); ++it)
229  {
230  GFXVertexf(Projection(radarView, outer * (*it)));
231  }
232  GFXEnd();
233 
234  groundColor.a = 0.4;
235  GFXColorf(groundColor);
237  for (std::vector<Vector>::const_iterator it = groundPlane.begin(); it != groundPlane.end(); ++it)
238  {
239  GFXVertexf(Projection(radarView, middle * (*it)));
240  }
241  GFXVertexf(Projection(radarView, middle * groundPlane.front()));
242  GFXEnd();
243 
245  for (std::vector<Vector>::const_iterator it = groundPlane.begin(); it != groundPlane.end(); ++it)
246  {
247  GFXVertexf(Projection(radarView, inner * (*it)));
248  }
249  GFXVertexf(Projection(radarView, inner * groundPlane.front()));
250  GFXEnd();
251 
252  groundColor.a = 0.4;
253  const float xcone = cosf(sensor.GetLockCone());
254  const float zcone = sinf(sensor.GetLockCone());
255  const float innerCone = inner;
256  const float outerCone = outer;
257  Vector leftCone(xcone, 0.0f, zcone);
258  Vector rightCone(-xcone, 0.0f, zcone);
259  GFXColorf(groundColor);
260  GFXBegin(GFXLINE);
261  GFXVertexf(Projection(radarView, outerCone * leftCone));
262  GFXVertexf(Projection(radarView, innerCone * leftCone));
263  GFXVertexf(Projection(radarView, outerCone * rightCone));
264  GFXVertexf(Projection(radarView, innerCone * rightCone));
265  GFXEnd();
266  GFXLineWidth(1);
267 }
268 
269 void PlaneDisplay::DrawNear(const Sensor& sensor,
270  const Sensor::TrackCollection& tracks)
271 {
272  // Draw all near tracks (distance scaled)
273 
274  if (!leftRadar.IsActive())
275  return;
276 
277  float maxRange = sensor.GetCloseRange();
278 
279  DrawGround(sensor, leftRadar);
280 
281  for (Sensor::TrackCollection::const_iterator it = tracks.begin(); it != tracks.end(); ++it)
282  {
283  if (it->GetDistance() > maxRange)
284  continue;
285 
286  DrawTrack(sensor, leftRadar, *it, maxRange);
287  }
288 }
289 
291  const Sensor::TrackCollection& tracks)
292 {
293  // Draw all near tracks (distance scaled)
294 
295  if (!rightRadar.IsActive())
296  return;
297 
298  float minRange = sensor.GetCloseRange();
299  float maxRange = sensor.GetMaxRange();
300 
301  DrawGround(sensor, rightRadar);
302 
303  for (Sensor::TrackCollection::const_iterator it = tracks.begin(); it != tracks.end(); ++it)
304  {
305  if ((it->GetDistance() < minRange) || (it->GetDistance() > maxRange))
306  continue;
307 
308  DrawTrack(sensor, rightRadar, *it, maxRange);
309  }
310 }
311 
312 void PlaneDisplay::DrawTrack(const Sensor& sensor,
313  const ViewArea& radarView,
314  const Track& track,
315  float maxRange)
316 {
317  const Track::Type::Value unitType = track.GetType();
318  GFXColor color = sensor.GetColor(track);
319 
320  Vector position = track.GetPosition();
321  Vector scaledPosition = Vector(position.x, -position.y, position.z) / maxRange;
322  if (scaledPosition.Magnitude() > 1.0)
323  return;
324 
325  // FIXME: Integrate radar into damage/repair system
326  // FIXME: Jitter does not work when entering a nebula
327  // FIXME: Jitter does not work close by
328  if (sensor.InsideNebula())
329  {
330  Jitter(0.0, 0.01, scaledPosition);
331  }
332  else
333  {
334  const bool isNebula = (track.GetType() == Track::Type::Nebula);
335  const bool isEcmActive = track.HasActiveECM();
336  if (isNebula || isEcmActive)
337  {
338  const float errorOffset = (scaledPosition.x > 0.0 ? 0.01 : -0.01);
339  const float errorRange = 0.03;
340  Jitter(errorOffset, errorRange, scaledPosition);
341  }
342  }
343 
344  Vector head = Projection(radarView, scaledPosition);
345 
346  Vector scaledGround(scaledPosition.x, 0, scaledPosition.z);
347  Vector ground = Projection(radarView, scaledGround);
348 
349  const bool isBelowGround = (scaledPosition.y > 0); // Y has been inverted
350 
351  // Tracks below ground are muted
352  if (isBelowGround)
353  color.a /= 3;
354  // and so is cargo
355  if (track.GetType() == Track::Type::Cargo)
356  color.a /= 4;
357 
358  if (sensor.UseThreatAssessment())
359  {
360  float dangerRate = GetDangerRate(sensor.IdentifyThreat(track));
361  if (dangerRate > 0.0)
362  {
363  // Blinking track
364  color.a *= cosf(dangerRate * radarTime);
365  }
366  }
367 
368  // Fade out dying ships
369  if (track.IsExploding())
370  {
371  color.a *= (1.0 - track.ExplodingProgress());
372  }
373 
374  float trackSize = std::max(1.0f, std::log10(track.GetSize()));
375  if (track.GetType() != Track::Type::Cargo)
376  trackSize += 1.0;
377 
378  DrawTarget(unitType, head, ground, trackSize, color);
379 
380  if (sensor.IsTracking(track))
381  {
382  Vector center = Projection(radarView, Vector(0, 0, 0));
383  DrawTargetMarker(head, ground, center, trackSize, color, sensor.UseObjectRecognition());
384  }
385 }
386 
388  const Vector& head,
389  const Vector& ground,
390  float trackSize,
391  const GFXColor& color)
392 {
393  // Draw leg
394  GFXColor legColor = color;
395  legColor.a /= 2;
396  GFXLineWidth(0.2);
397  GFXColorf(legColor);
398  GFXBegin(GFXLINE);
399  GFXVertexf(head);
400  GFXVertexf(ground);
401  GFXEnd();
402 
403  // Draw head
404  GFXColorf(color);
405  GFXPointSize(trackSize);
407  GFXVertexf(head);
408  GFXEnd();
409 }
410 
412  const Vector& ground,
413  const Vector& center,
414  float trackSize,
415  const GFXColor& color,
416  bool drawArea)
417 {
418  if (drawArea)
419  {
420  GFXColor areaColor = color;
421  areaColor.a /= 4;
422  GFXColorf(areaColor);
423  GFXBegin(GFXPOLY);
424  GFXVertexf(head);
425  GFXVertexf(ground);
426  GFXVertexf(center);
427  GFXEnd();
428  }
429 
430  // Diamond
431  float size = 6.0 * std::max(trackSize, 1.0f);
432  float xsize = size / g_game.x_resolution;
433  float ysize = size / g_game.y_resolution;
434 
435  GFXColorf(color);
436  GFXLineWidth(1);
438  GFXVertex3f(head.x - xsize, head.y, 0.0f);
439  GFXVertex3f(head.x, head.y - ysize, 0.0f);
440  GFXVertex3f(head.x + xsize, head.y, 0.0f);
441  GFXVertex3f(head.x, head.y + ysize, 0.0f);
442  GFXVertex3f(head.x - xsize, head.y, 0.0f);
443  GFXEnd();
444 }
445 
446 } // namespace Radar