Starshatter_Open
Open source Starshatter engine
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
CameraDirector.cpp
Go to the documentation of this file.
1 /* Project Starshatter 4.5
2  Destroyer Studios LLC
3  Copyright © 1997-2004. All Rights Reserved.
4 
5  SUBSYSTEM: Stars.exe
6  FILE: CameraDirector.cpp
7  AUTHOR: John DiCamillo
8 
9 
10  OVERVIEW
11  ========
12  Camera Director singleton manages the main view camera based on the current ship
13 */
14 
15 #include "MemDebug.h"
16 #include "CameraDirector.h"
17 #include "Ship.h"
18 #include "FlightDeck.h"
19 #include "Contact.h"
20 #include "Sim.h"
21 #include "StarSystem.h"
22 #include "Terrain.h"
23 #include "HUDView.h"
24 #include "DetailSet.h"
25 #include "Starshatter.h"
26 
27 #include "Game.h"
28 
29 // +----------------------------------------------------------------------+
30 
32 
33 static double range_max_limit = 300e3;
34 
35 // +----------------------------------------------------------------------+
36 
39 {
40  if (!instance)
41  instance = new(__FILE__,__LINE__) CameraDirector;
42 
43  return instance;
44 }
45 
46 // +----------------------------------------------------------------------+
47 
49 : mode(MODE_COCKPIT), requested_mode(MODE_NONE), old_mode(MODE_NONE),
50 sim(0), ship(0), region(0), external_ship(0), external_body(0),
51 virt_az(0), virt_el(0), virt_x(0), virt_y(0), virt_z(0),
52 azimuth(PI/4), elevation(PI/4), az_rate(0), el_rate(0), range_rate(0),
53 range(0), range_min(100), range_max(range_max_limit),
54 base_range(0), transition(0), hud(0)
55 {
56  instance = this;
57 }
58 
59 // +--------------------------------------------------------------------+
60 
62 {
63  if (instance == this)
64  instance = 0;
65 }
66 
67 // +--------------------------------------------------------------------+
68 
69 void
71 {
75 
76  sim = 0;
77  ship = 0;
78  region = 0;
79  external_ship = 0;
80  external_body = 0;
81  transition = 0;
82  hud = 0;
83 }
84 
85 // +--------------------------------------------------------------------+
86 
87 void
89 {
90  sim = Sim::GetSim();
92 
93  // can't take control of a dead ship:
94  if (s && (s->Life() == 0 || s->IsDying() || s->IsDead()))
95  return;
96 
97  // leaving the old ship, so make sure it is visible:
98  if (ship && ship != s) {
99  ship->ShowRep();
100  ship->HideCockpit();
101  }
102 
103  // taking control of the new ship:
104  ship = s;
105 
106  if (ship) {
107  Observe(ship);
108  region = ship->GetRegion();
109 
110  if (sim && ship->GetRegion() != sim->GetActiveRegion())
112 
113  range = ship->Radius() * 4;
114 
115  if (mode == MODE_COCKPIT)
116  mode = MODE_CHASE;
117 
119  ExecFrame(0);
120  }
121 }
122 
123 // +--------------------------------------------------------------------+
124 
125 void
126 CameraDirector::SetMode(int m, double t)
127 {
128  if (requested_mode == m)
129  return;
130 
131  external_point = Point();
132 
133  // save current mode for after transition:
134  if (m == MODE_DROP && mode != MODE_DROP)
135  old_mode = mode;
136 
137  // if manually leaving drop mode, forget about
138  // restoring the previous mode when the drop
139  // expires...
140  else if (m != MODE_DROP && mode == MODE_DROP)
142 
143  if (m == MODE_VIRTUAL && ship && !ship->Cockpit())
144  return;
145 
146  if (mode == m) {
147  if (mode == MODE_TARGET || mode == MODE_ORBIT)
148  CycleViewObject();
149 
150  return;
151  }
152 
153  if (m > MODE_NONE && m < MODE_LAST) {
154  if (m <= MODE_VIRTUAL) {
155  requested_mode = m;
156  transition = t;
157  external_ship = 0;
158  ClearGroup();
159 
160  // no easy way to do a smooth transition between
161  // certain modes, so just go immediately:
162  if ((mode == MODE_TARGET && m == MODE_CHASE) ||
163  (mode == MODE_COCKPIT && m == MODE_VIRTUAL) ||
164  (mode == MODE_VIRTUAL && m == MODE_COCKPIT))
165  {
166  mode = m;
167  requested_mode = 0;
168  transition = 0;
169  }
170  }
171 
172  else if (m == MODE_TRANSLATE || m == MODE_ZOOM) {
173  requested_mode = m;
174  transition = t;
175  base_range = range;
176  }
177 
178  else if (m >= MODE_DOCKING) {
179  mode = m;
180  transition = 0;
181  external_ship = 0;
182  ClearGroup();
183  }
184 
185  virt_az = 0;
186  virt_el = 0;
187  virt_x = 0;
188  virt_y = 0;
189  virt_z = 0;
190  }
191 }
192 
193 // +--------------------------------------------------------------------+
194 
195 static const char* get_camera_mode_name(int m)
196 {
197  switch (m) {
198  default:
199  case CameraDirector::MODE_NONE: return ""; break;
200  case CameraDirector::MODE_COCKPIT: return ""; break;
201  case CameraDirector::MODE_CHASE: return "Chase Cam"; break;
202  case CameraDirector::MODE_TARGET: return "Padlock"; break;
203  case CameraDirector::MODE_THREAT: return "Threatlock"; break;
204  case CameraDirector::MODE_VIRTUAL: return "Virtual"; break;
207  case CameraDirector::MODE_ZOOM: return "Orbit Cam"; break;
208  case CameraDirector::MODE_DOCKING: return "Dock Cam"; break;
209  case CameraDirector::MODE_DROP: return ""; break;
210  }
211 }
212 
213 const char*
215 {
216  int m = GetCameraMode();
217 
218  if (m != CameraDirector::MODE_VIRTUAL) {
219  return get_camera_mode_name(m);
220  }
221  else if (instance) {
222  if (instance->virt_az > 30*DEGREES && instance->virt_az < 100*DEGREES)
223  return "RIGHT";
224 
225  else if (instance->virt_az >= 100*DEGREES)
226  return "RIGHT-AFT";
227 
228  else if (instance->virt_az < -30*DEGREES && instance->virt_az > -100*DEGREES)
229  return "LEFT";
230 
231  else if (instance->virt_az <= -100*DEGREES)
232  return "LEFT-AFT";
233 
234  else if (instance->virt_el > 15*DEGREES)
235  return "UP";
236 
237  else if (instance->virt_el < -15*DEGREES)
238  return "DOWN";
239 
240  return get_camera_mode_name(m);
241  }
242 
243  return "";
244 }
245 
246 int
248 {
249  if (instance) {
250  int op_mode = instance->mode;
252  op_mode = instance->requested_mode;
253  return op_mode;
254  }
255 
256  return 0;
257 }
258 
259 void
261 {
262  if (instance)
263  instance->SetMode(m, t);
264 }
265 
266 // +--------------------------------------------------------------------+
267 
268 double
270 {
271  return range_max_limit;
272 }
273 
274 void
276 {
277  if (r >= 1e3)
278  range_max_limit = r;
279 }
280 
281 void
282 CameraDirector::SetRangeLimits(double min, double max)
283 {
284  if (instance) {
285  instance->range_min = min;
286  instance->range_max = max;
287  }
288 }
289 
290 // +--------------------------------------------------------------------+
291 
292 void
294 {
296 }
297 
298 // +--------------------------------------------------------------------+
299 
300 void
302 {
303  if (!ship) return;
304 
305  Ship* current = external_ship;
306  external_ship = 0;
307 
309  while (++iter && !external_ship) {
310  Contact* c = iter.value();
311  Ship* c_ship = c->GetShip();
312 
313  if (c_ship && !current) {
314  external_ship = c_ship;
315  }
316 
317  else if (current && c_ship == current) {
318  while (++iter && !external_ship) {
319  c = iter.value();
320  if (c->ActLock())
321  external_ship = c->GetShip();
322  }
323  }
324  }
325 
326  if (external_ship != current) {
327  if (external_ship) {
328  if (external_ship->Life() == 0 || external_ship->IsDying() || external_ship->IsDead()) {
330  external_ship = 0;
331  }
332  else {
334  }
335  }
336 
337  if (mode == MODE_ORBIT) {
339  ExternalRange(1);
340  }
341  }
342 }
343 
344 // +--------------------------------------------------------------------+
345 
346 void
348 {
349  external_body = orb;
350 
351  if (external_body) {
352  range_min = external_body->Radius() * 2.5;
353  ClearGroup();
354  external_ship = 0;
355 
356  if (sim) {
358  if (region)
360  }
361 
362  if (ship && !region)
363  region = ship->GetRegion();
364  }
365 }
366 
367 // +--------------------------------------------------------------------+
368 
369 void
371 {
372  if (!ship) return;
373  if (!obj) {
374  obj = ship;
375  region = ship->GetRegion();
376  }
377 
378  external_body = 0;
379  external_point = Point();
380 
382 
383  if (obj->GetIFF() != ship->GetIFF() && !stars->InCutscene()) {
384  // only view solid contacts:
385  Contact* c = ship->FindContact(obj);
386  if (!c || !c->ActLock())
387  return;
388  }
389 
390  if (mode == MODE_TARGET) {
391  ClearGroup();
392  if (external_ship) {
393  external_ship = 0;
394  }
395  }
396  else if (mode >= MODE_ORBIT) {
397  if (quick) {
398  mode = MODE_ORBIT;
399  transition = 0;
400  }
401  else {
403  }
404 
405  if (external_group.size()) {
406  ClearGroup();
407 
408  if (external_ship) {
409  external_ship = 0;
410  }
411  }
412 
413  else {
414  if ((obj == external_ship) || (obj==ship && external_ship==0)) {
415  if (!quick)
417  }
418 
419  else if (external_ship) {
420  external_ship = 0;
421  }
422  }
423  }
424 
425  if (external_ship != obj) {
426  external_ship = obj;
427 
428  if (external_ship) {
430 
431  if (external_ship->Life() == 0 || external_ship->IsDying() || external_ship->IsDead()) {
432  external_ship = 0;
433  range_min = 100;
434  }
435  else {
437 
438  if (sim)
440 
441  range_min = external_ship->Radius() * 1.5;
442  }
443  }
444 
446  ExternalRange(1);
447  }
448 }
449 
450 // +--------------------------------------------------------------------+
451 
452 void
454 {
455  if (!ship) return;
456 
458 
459  if (!stars->InCutscene()) {
460  // only view solid contacts:
461  while (++group) {
462  Ship* s = group.value();
463 
464  if (s->GetIFF() != ship->GetIFF()) {
465  Contact* c = ship->FindContact(s);
466  if (!c || !c->ActLock())
467  return;
468  }
469 
470  if (s->Life() == 0 || s->IsDying() || s->IsDead())
471  return;
472  }
473  }
474 
475  group.reset();
476 
477  if (external_group.size() > 1 &&
478  external_group.size() == group.size()) {
479 
480  bool same = true;
481 
482  for (int i = 0; same && i < external_group.size(); i++) {
483  if (external_group[i] != group.container()[i])
484  same = false;
485  }
486 
487  if (same) {
489  return;
490  }
491  }
492 
493  ClearGroup();
494 
495  if (quick) {
496  mode = MODE_ORBIT;
497  transition = 0;
498  }
499  else {
501  }
502 
504 
506  while (++iter) {
507  Ship* s = iter.value();
508  region = s->GetRegion();
509  Observe(s);
510  }
511 }
512 
513 // +--------------------------------------------------------------------+
514 
515 void
516 CameraDirector::VirtualHead(double az, double el)
517 {
518  if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
519  const double alimit = 3*PI/4;
520  const double e_lo = PI/8;
521  const double e_hi = PI/3;
522  const double escale = e_hi;
523 
524  virt_az = az * alimit;
525  virt_el = el * escale;
526 
527  if (virt_az > alimit)
528  virt_az = alimit;
529 
530  else if (virt_az < -alimit)
531  virt_az = -alimit;
532 
533  if (virt_el > e_hi)
534  virt_el = e_hi;
535 
536  else if (virt_el < -e_lo)
537  virt_el = -e_lo;
538  }
539 }
540 
541 void
542 CameraDirector::VirtualHeadOffset(double x, double y, double z)
543 {
544  if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
545  virt_x = x;
546  virt_y = y;
547  virt_z = z;
548  }
549 }
550 
551 void
553 {
554  if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
555  virt_az += delta;
556 
557  const double alimit = 3*PI/4;
558 
559  if (virt_az > alimit)
560  virt_az = alimit;
561 
562  else if (virt_az < -alimit)
563  virt_az = -alimit;
564  }
565 }
566 
567 void
569 {
570  if (mode == MODE_VIRTUAL || mode == MODE_TARGET || mode == MODE_COCKPIT) {
571  virt_el += delta;
572 
573  const double e_lo = PI/8;
574  const double e_hi = PI/3;
575 
576  if (virt_el > e_hi)
577  virt_el = e_hi;
578 
579  else if (virt_el < -e_lo)
580  virt_el = -e_lo;
581  }
582 }
583 
584 // +--------------------------------------------------------------------+
585 
586 void
588 {
589  azimuth += delta;
590 
591  if (azimuth > PI)
592  azimuth = -2*PI + azimuth;
593 
594  else if (azimuth < -PI)
595  azimuth = 2*PI + azimuth;
596 }
597 
598 void
600 {
601  elevation += delta;
602 
603  const double limit = (0.43 * PI);
604 
605  if (elevation > limit)
606  elevation = limit;
607  else if (elevation < -limit)
608  elevation = -limit;
609 }
610 
611 void
613 {
614  range *= delta;
615 
616  if (ship && ship->IsAirborne())
617  range_max = 30e3;
618  else
619  range_max = range_max_limit;
620 
621  if (range < range_min)
622  range = range_min;
623 
624  else if (range > range_max)
625  range = range_max;
626 }
627 
628 // +--------------------------------------------------------------------+
629 
630 void
631 CameraDirector::SetOrbitPoint(double a, double e, double r)
632 {
633  azimuth = a;
634  elevation = e;
635  range = r;
636 
637  if (external_body) {
638  if (range < external_body->Radius() * 2)
639  range = external_body->Radius() * 2;
640 
641  else if (range > external_body->Radius() * 6)
642  range = external_body->Radius() * 6;
643  }
644 
645  else {
646  if (range < range_min)
647  range = range_min;
648 
649  else if (range > range_max)
650  range = range_max;
651  }
652 }
653 
654 void
655 CameraDirector::SetOrbitRates(double ar, double er, double rr)
656 {
657  az_rate = ar;
658  el_rate = er;
659 
660  range_rate = rr;
661 }
662 
663 // +--------------------------------------------------------------------+
664 
665 bool
667 {
668  if (obj->Type() == SimObject::SIM_SHIP) {
669  Ship* s = (Ship*) obj;
670  if (ship == s)
671  ship = 0;
672 
673  if (external_ship == s) {
674  external_point = s->Location();
675  external_ship = 0;
676  }
677 
678  if (external_group.contains(s))
680  }
681 
682  return SimObserver::Update(obj);
683 }
684 
685 const char*
687 {
688  return "CameraDirector";
689 }
690 
691 // +--------------------------------------------------------------------+
692 
693 void
695 {
696  if (!ship)
697  return;
698 
700 
701  int flight_phase = ship->GetFlightPhase();
702 
703  if (flight_phase < Ship::LOCKED)
705 
706  if (ship->IsAirborne()) {
707  if (flight_phase >= Ship::DOCKING)
709  }
710  else {
711  if (flight_phase >= Ship::RECOVERY)
713  }
714 
715  if (flight_phase >= Ship::LOCKED && flight_phase < Ship::ACTIVE) {
716  int m = GetMode();
717  if (m != MODE_COCKPIT && m != MODE_VIRTUAL)
719  }
720 
721  if (ship->InTransition()) {
723  }
724  // automatically restore mode after transition:
725  else if (old_mode != MODE_NONE) {
726  mode = old_mode;
729  }
730 
731  int op_mode = mode;
732  if (requested_mode > mode)
733  op_mode = requested_mode;
734 
736 
737  // if we are in padlock, and have not locked a ship
738  // try to padlock the current target:
739  if (op_mode == MODE_TARGET && !external_ship) {
740  SimObject* tgt = ship->GetTarget();
741  if (tgt && tgt->Type() == SimObject::SIM_SHIP)
742  SetViewObject((Ship*) tgt);
743  }
744 
745  // if in an external mode, check the external ship:
746  else if (op_mode >= MODE_TARGET && op_mode <= MODE_ZOOM) {
747  if (external_ship && external_ship != ship && !stars->InCutscene()) {
749  if (!c || !c->ActLock()) {
751  }
752  }
753  }
754 
755  if (ship->Rep()) {
756  if (op_mode == MODE_COCKPIT) {
757  ship->HideRep();
758  ship->HideCockpit();
759  }
760  else if (op_mode == MODE_VIRTUAL || op_mode == MODE_TARGET) {
761  if (ship->Cockpit()) {
762  ship->HideRep();
763  ship->ShowCockpit();
764  }
765  else {
766  ship->ShowRep();
767  }
768  }
769  else {
770  ship->Rep()->SetForeground(op_mode == MODE_DOCKING);
771  ship->ShowRep();
772  ship->HideCockpit();
773  }
774  }
775 
776  if (hud && hud->Ambient() != Color::Black)
777  sim->GetScene()->SetAmbient( hud->Ambient() );
778  else
780 
781  switch (op_mode) {
782  default:
783  case MODE_COCKPIT: Cockpit(seconds); break;
784  case MODE_CHASE: Chase(seconds); break;
785  case MODE_TARGET: Target(seconds); break;
786  case MODE_THREAT: Threat(seconds); break;
787  case MODE_VIRTUAL: Virtual(seconds); break;
788  case MODE_ORBIT:
789  case MODE_TRANSLATE:
790  case MODE_ZOOM: Orbit(seconds); break;
791  case MODE_DOCKING: Docking(seconds); break;
792  case MODE_DROP: Drop(seconds); break;
793  }
794 
795  if (ship->Shake() > 0 &&
796  (op_mode < MODE_ORBIT ||
797  (op_mode == MODE_VIRTUAL && ship->Cockpit()))) {
798 
799  Point vib = ship->Vibration() * 0.2;
800  camera.MoveBy(vib);
801  camera.Aim(0, vib.y, vib.z);
802  }
803 
804  Transition(seconds);
806 }
807 
808 // +--------------------------------------------------------------------+
809 
810 void
812 {
813  if (transition > 0)
814  transition -= seconds * 1.5;
815 
816  if (transition <= 0) {
817  transition = 0;
818 
821 
822  requested_mode = 0;
823 
824  if (mode == MODE_TRANSLATE || mode == MODE_ZOOM) {
825  if (mode == MODE_ZOOM)
826  range = range_min;
827 
828  mode = MODE_ORBIT;
829  }
830  }
831 }
832 
833 // +--------------------------------------------------------------------+
834 
835 bool
837 {
838  if (instance) {
839  double fvaz = fabs(instance->virt_az);
840  double fvel = fabs(instance->virt_el);
841 
842  return fvaz < 15*DEGREES && fvel < 15*DEGREES;
843  }
844 
845  return true;
846 }
847 
848 // +--------------------------------------------------------------------+
849 
850 void
851 CameraDirector::Cockpit(double seconds)
852 {
853  camera.Clone(ship->Cam());
854 
855  Point bridge = ship->BridgeLocation();
856  Point cpos = camera.Pos() +
857  camera.vrt() * bridge.x +
858  camera.vpn() * bridge.y +
859  camera.vup() * bridge.z;
860 
861  camera.MoveTo(cpos);
862 }
863 
864 // +--------------------------------------------------------------------+
865 
866 void
867 CameraDirector::Virtual(double seconds)
868 {
869  camera.Clone(ship->Cam());
870 
871  Point bridge = ship->BridgeLocation();
872  Point cpos = camera.Pos() +
873  camera.vrt() * (bridge.x + virt_x) +
874  camera.vpn() * (bridge.y + virt_z) +
875  camera.vup() * (bridge.z + virt_y);
876 
877  camera.MoveTo(cpos);
878 
879  camera.Yaw(virt_az);
880  camera.Pitch(-virt_el);
881 
882  double fvaz = fabs(virt_az);
883  double fvel = fabs(virt_el);
884 
885  if (fvaz > 0.01*DEGREES && fvaz < 15*DEGREES) {
886  double bleed = fvaz * 2;
887 
888  if (virt_az > 0)
889  virt_az -= bleed * seconds;
890  else
891  virt_az += bleed * seconds;
892  }
893 
894  if (fvel > 0.01*DEGREES && fvel < 15*DEGREES) {
895  double bleed = fvel;
896 
897  if (virt_el > 0)
898  virt_el -= bleed * seconds;
899  else
900  virt_el += bleed * seconds;
901  }
902 }
903 
904 // +--------------------------------------------------------------------+
905 
906 void
907 CameraDirector::Chase(double seconds)
908 {
909  double step = 1;
910 
912  step = transition;
913 
914  else if (requested_mode == MODE_CHASE)
915  step = 1 - transition;
916 
917  camera.Clone(ship->Cam());
918  Point velocity = camera.vpn();
919 
920  if (ship->Velocity().length() > 10) {
921  velocity = ship->Velocity();
922  velocity.Normalize();
923  velocity *= 0.25;
924  velocity += camera.vpn() * 2;
925  velocity.Normalize();
926  }
927 
928  Point chase = ship->ChaseLocation();
929  Point bridge = ship->BridgeLocation();
930  Point cpos = camera.Pos() +
931  camera.vrt() * bridge.x * (1-step) +
932  camera.vpn() * bridge.y * (1-step) +
933  camera.vup() * bridge.z * (1-step) +
934  velocity * chase.y * step +
935  camera.vup() * chase.z * step;
936 
937  camera.MoveTo(cpos);
938 }
939 
940 // +--------------------------------------------------------------------+
941 
942 void
943 CameraDirector::Target(double seconds)
944 {
945  Point target_loc = external_point;
946 
947  if (external_ship)
948  target_loc = external_ship->Location();
949 
950  if (!external_ship || external_ship == ship) {
951  if (!external_point) {
952  if (ship->Cockpit())
953  Virtual(seconds);
954  else
955  Orbit(seconds);
956 
957  return;
958  }
959  }
960 
961  double step = 1;
962 
964  step = transition;
965 
966  else if (requested_mode == MODE_TARGET)
967  step = 1 - transition;
968 
969  if (ship->Cockpit()) {
970  // internal padlock:
971  Cockpit(seconds);
972  camera.Padlock(target_loc, 3*PI/4, PI/8, PI/3);
973  }
974  else {
975  // external padlock:
976  Point delta = target_loc - ship->Location();
977  delta.Normalize();
978  delta *= -5 * ship->Radius() * step;
979  delta.y += ship->Radius() * step;
980 
981  camera.MoveTo(ship->Location() + delta);
982  camera.LookAt(target_loc);
983  }
984 }
985 
986 // +--------------------------------------------------------------------+
987 
988 void
989 CameraDirector::Threat(double seconds)
990 {
991  Chase(seconds);
992 }
993 
994 // +--------------------------------------------------------------------+
995 
996 void
997 CameraDirector::Orbit(double seconds)
998 {
999  Point cpos = ship->Location();
1000  int op_mode = GetCameraMode();
1001 
1002  if (seconds < 0) seconds = 0;
1003  else if (seconds > 0.2) seconds = 0.2;
1004 
1005  // auto rotate
1006  azimuth += az_rate * seconds;
1007  elevation += el_rate * seconds;
1008  range *= 1 + range_rate * seconds;
1009 
1010  if (external_body && external_body->Rep()) {
1011  range_min = external_body->Radius() * 2.5;
1012  cpos = external_body->Rep()->Location();
1013  }
1014 
1015  else if (external_group.size()) {
1016  Point neg(1e9, 1e9, 1e9);
1017  Point pos(-1e9, -1e9, -1e9);
1018 
1020  while (++iter) {
1021  Point loc = iter->Location();
1022 
1023  if (loc.x < neg.x) neg.x = loc.x;
1024  if (loc.x > pos.x) pos.x = loc.x;
1025  if (loc.y < neg.y) neg.y = loc.y;
1026  if (loc.y > pos.y) pos.y = loc.y;
1027  if (loc.z < neg.z) neg.z = loc.z;
1028  if (loc.z > pos.z) pos.z = loc.z;
1029  }
1030 
1031  double dx = pos.x - neg.x;
1032  double dy = pos.y - neg.y;
1033  double dz = pos.z - neg.z;
1034 
1035  if (dx > dy) {
1036  if (dx > dz) range_min = dx * 1.2;
1037  else range_min = dz * 1.2;
1038  }
1039  else {
1040  if (dy > dz) range_min = dy * 1.2;
1041  else range_min = dz * 1.2;
1042  }
1043 
1044  // focus on median location:
1045  cpos = neg + Point(dx/2, dy/2, dz/2);
1046  }
1047  else if (external_ship) {
1048  range_min = external_ship->Radius() * 1.5;
1049  cpos = external_ship->Location();
1050  }
1051  else {
1052  range_min = ship->Radius() * 1.5;
1053  cpos = ship->Location();
1054  }
1055 
1056  if (range < range_min)
1057  range = range_min;
1058 
1059  double er = range;
1060  double az = azimuth;
1061  double el = elevation;
1062 
1063  if (requested_mode == MODE_TRANSLATE) {
1064  cpos = cpos*(1-transition) + base_loc*(transition);
1065  }
1066 
1067  else if (requested_mode == MODE_ZOOM) {
1068  er = base_range * transition;
1069 
1070  if (er < range_min) {
1071  er = range_min;
1072  range = range_min;
1073  transition = 0;
1074  requested_mode = 0;
1075  }
1076  }
1077 
1078  // transitions:
1079  else if (mode < MODE_ORBIT || requested_mode > 0 && requested_mode < MODE_ORBIT) {
1080  double az0 = ship->CompassHeading();
1081  if (fabs(az-az0) > PI) az0 -= 2*PI;
1082 
1083  double r0 = 0;
1084  double z0 = 0;
1085 
1086  if (mode == MODE_CHASE || requested_mode == MODE_CHASE ||
1088  r0 = ship->ChaseLocation().length();
1089  z0 = 20 * DEGREES;
1090  }
1091 
1092  // pull out:
1093  if (mode < MODE_ORBIT) {
1094  er *= (1-transition);
1095  az *= (1-transition);
1096  el *= (1-transition);
1097 
1098  er += r0 * transition;
1099  az += az0 * transition;
1100  el += z0 * transition;
1101  }
1102 
1103  // push in:
1104  else {
1105  er *= transition;
1106  az *= transition;
1107  el *= transition;
1108 
1109  er += r0 * (1-transition);
1110  az += az0 * (1-transition);
1111  el += z0 * (1-transition);
1112  }
1113  }
1114 
1115  else {
1116  // save base location for next time we re-focus
1117  base_loc = cpos;
1118  }
1119 
1120  double dx = er * sin(az) * cos(el);
1121  double dy = er * cos(az) * cos(el);
1122  double dz = er * sin(el);
1123 
1124  Point cloc = cpos + Point(dx,dz,dy);
1125 
1126  Terrain* terrain = ship->GetRegion()->GetTerrain();
1127 
1128  if (terrain) {
1129  double cam_agl = cloc.y - terrain->Height(cloc.x, cloc.z);
1130 
1131  if (cam_agl < 100)
1132  cloc.y = terrain->Height(cloc.x, cloc.z) + 100;
1133  }
1134 
1135  if (external_ship == 0 && er < 0.5 * ship->Radius())
1136  ship->Rep()->Hide();
1137 
1138  camera.MoveTo(cloc.x, cloc.y, cloc.z);
1139  camera.LookAt(cpos);
1140 }
1141 
1142 // +--------------------------------------------------------------------+
1143 
1144 void
1146 {
1147  FlightDeck* dock = ship->GetDock();
1148 
1149  if (!dock) {
1150  Cockpit(seconds);
1151  return;
1152  }
1153 
1154  if (!ship->IsAirborne())
1155  sim->GetScene()->SetAmbient(Color(120,130,140));
1156  int flight_phase = ship->GetFlightPhase();
1157 
1158  Point bridge = ship->BridgeLocation();
1159  Point cloc = ship->Location() +
1160  ship->Cam().vrt() * bridge.x +
1161  ship->Cam().vpn() * bridge.y +
1162  ship->Cam().vup() * bridge.z;
1163 
1164  Point cpos = dock->CamLoc();
1165 
1166  // preflight:
1167  if (flight_phase < Ship::LOCKED) {
1168  base_loc = cpos;
1169  }
1170 
1171  else if (flight_phase == Ship::LOCKED) {
1172  if (hud)
1174  cpos = base_loc * transition +
1175  cloc * (1-transition);
1176  }
1177 
1178  // recovery:
1179  else if (flight_phase > Ship::APPROACH) {
1180  if (hud)
1181  hud->SetTacticalMode(1);
1182  }
1183 
1184  camera.MoveTo(cpos);
1185  camera.LookAt(cloc);
1186 }
1187 
1188 // +--------------------------------------------------------------------+
1189 
1190 void
1191 CameraDirector::Drop(double seconds)
1192 {
1193  // orbital transitions use "drop cam" at transition_loc
1195  camera.LookAt(ship->Location());
1196 }