1 // observer.cpp
2 //
3 // Copyright (C) 2001-2008, Chris Laurel <claurel@shatters.net>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9
10 #include <celmath/mathlib.h>
11 #include <celmath/solve.h>
12 #include "observer.h"
13 #include "simulation.h"
14 #include "frametree.h"
15
16 static const double maximumSimTime = 730486721060.00073; // 2000000000 Jan 01 12:00:00 UTC
17 static const double minimumSimTime = -730498278941.99951; // -2000000000 Jan 01 12:00:00 UTC
18
19 using namespace std;
20
21
22 #define VELOCITY_CHANGE_TIME 0.25f
23
24
slerp(double t,const Vec3d & v0,const Vec3d & v1)25 static Vec3d slerp(double t, const Vec3d& v0, const Vec3d& v1)
26 {
27 double r0 = v0.length();
28 double r1 = v1.length();
29 Vec3d u = v0 / r0;
30 Vec3d n = u ^ (v1 / r1);
31 n.normalize();
32 Vec3d v = n ^ u;
33 if (v * v1 < 0.0)
34 v = -v;
35
36 double cosTheta = u * (v1 / r1);
37 double theta = acos(cosTheta);
38
39 return (cos(theta * t) * u + sin(theta * t) * v) * Mathd::lerp(t, r0, r1);
40 }
41
42
43 /*! Notes on the Observer class
44 * The values position and orientation are in observer's reference frame. positionUniv
45 * and orientationUniv are the equivalent values in the universal coordinate system.
46 * They must be kept in sync. Generally, it's position and orientation that are modified;
47 * after they're changed, the method updateUniversal is called. However, when the observer
48 * frame is changed, positionUniv and orientationUniv are not changed, but the position
49 * and orientation within the frame /do/ change. Thus, a 'reverse' update is necessary.
50 *
51 * There are two types of 'automatic' updates to position and orientation that may
52 * occur when the observer's update method is called: updates from free travel, and
53 * updates due to an active goto operation.
54 */
55
Observer()56 Observer::Observer() :
57 simTime(0.0),
58 position(0.0, 0.0, 0.0),
59 orientation(1.0),
60 velocity(0.0, 0.0, 0.0),
61 angularVelocity(0.0, 0.0, 0.0),
62 frame(NULL),
63 realTime(0.0),
64 targetSpeed(0.0),
65 targetVelocity(0.0, 0.0, 0.0),
66 initialVelocity(0.0, 0.0, 0.0),
67 beginAccelTime(0.0),
68 observerMode(Free),
69 trackingOrientation(1.0f, 0.0f, 0.0f, 0.0f),
70 fov((float) (PI / 4.0)),
71 reverseFlag(false),
72 locationFilter(~0u)
73 {
74 frame = new ObserverFrame();
75 updateUniversal();
76 }
77
78
79 /*! Copy constructor. */
Observer(const Observer & o)80 Observer::Observer(const Observer& o) :
81 simTime(o.simTime),
82 position(o.position),
83 orientation(o.orientation),
84 velocity(o.velocity),
85 angularVelocity(o.angularVelocity),
86 frame(NULL),
87 realTime(o.realTime),
88 targetSpeed(o.targetSpeed),
89 targetVelocity(o.targetVelocity),
90 beginAccelTime(o.beginAccelTime),
91 observerMode(o.observerMode),
92 journey(o.journey),
93 trackObject(o.trackObject),
94 trackingOrientation(o.trackingOrientation),
95 fov(o.fov),
96 reverseFlag(o.reverseFlag),
97 locationFilter(o.locationFilter),
98 displayedSurface(o.displayedSurface)
99 {
100 frame = new ObserverFrame(*o.frame);
101 updateUniversal();
102 }
103
104
operator =(const Observer & o)105 Observer& Observer::operator=(const Observer& o)
106 {
107 simTime = o.simTime;
108 position = o.position;
109 orientation = o.orientation;
110 velocity = o.velocity;
111 angularVelocity = o.angularVelocity;
112 frame = NULL;
113 realTime = o.realTime;
114 targetSpeed = o.targetSpeed;
115 targetVelocity = o.targetVelocity;
116 beginAccelTime = o.beginAccelTime;
117 observerMode = o.observerMode;
118 journey = o.journey;
119 trackObject = o.trackObject;
120 trackingOrientation = o.trackingOrientation;
121 fov = o.fov;
122 reverseFlag = o.reverseFlag;
123 locationFilter = o.locationFilter;
124 displayedSurface = o.displayedSurface;
125
126 setFrame(*o.frame);
127 updateUniversal();
128
129 return *this;
130 }
131
132
133 /*! Get the current simulation time. The time returned is a Julian date,
134 * and the time standard is TDB.
135 */
getTime() const136 double Observer::getTime() const
137 {
138 return simTime;
139 };
140
141
142 /*! Get the current real time. The time returned is a Julian date,
143 * and the time standard is TDB.
144 */
getRealTime() const145 double Observer::getRealTime() const
146 {
147 return realTime;
148 };
149
150
151 /*! Set the simulation time (Julian date, TDB time standard)
152 */
setTime(double jd)153 void Observer::setTime(double jd)
154 {
155 simTime = jd;
156 updateUniversal();
157 }
158
159
160 /*! Return the position of the observer in universal coordinates. The origin
161 * The origin of this coordinate system is the Solar System Barycenter, and
162 * axes are defined by the J2000 ecliptic and equinox.
163 */
getPosition() const164 UniversalCoord Observer::getPosition() const
165 {
166 return positionUniv;
167 }
168
169
170 // TODO: Low-precision set position that should be removed
setPosition(const Point3d & p)171 void Observer::setPosition(const Point3d& p)
172 {
173 setPosition(UniversalCoord(p));
174 }
175
176
177 /*! Set the position of the observer; position is specified in the universal
178 * coordinate system.
179 */
setPosition(const UniversalCoord & p)180 void Observer::setPosition(const UniversalCoord& p)
181 {
182 positionUniv = p;
183 position = frame->convertFromUniversal(p, getTime());
184 }
185
186
187 /*! Return the orientation of the observer in the universal coordinate
188 * system.
189 */
getOrientation() const190 Quatd Observer::getOrientation() const
191 {
192 return orientationUniv;
193 }
194
195 /*! Reduced precision version of getOrientation()
196 */
getOrientationf() const197 Quatf Observer::getOrientationf() const
198 {
199 Quatd q = getOrientation();
200 return Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
201 }
202
203
204 /* Set the orientation of the observer. The orientation is specified in
205 * the universal coordinate system.
206 */
setOrientation(const Quatf & q)207 void Observer::setOrientation(const Quatf& q)
208 {
209 /*
210 RigidTransform rt = frame.toUniversal(situation, getTime());
211 rt.rotation = Quatd(q.w, q.x, q.y, q.z);
212 situation = frame.fromUniversal(rt, getTime());
213 */
214 setOrientation(Quatd(q.w, q.x, q.y, q.z));
215 }
216
217
218 /*! Set the orientation of the observer. The orientation is specified in
219 * the universal coordinate system.
220 */
setOrientation(const Quatd & q)221 void Observer::setOrientation(const Quatd& q)
222 {
223 orientationUniv = q;
224 orientation = frame->convertFromUniversal(q, getTime());
225 }
226
227
228 /*! Get the velocity of the observer within the observer's reference frame.
229 */
getVelocity() const230 Vec3d Observer::getVelocity() const
231 {
232 return velocity;
233 }
234
235
236 /*! Set the velocity of the observer within the observer's reference frame.
237 */
setVelocity(const Vec3d & v)238 void Observer::setVelocity(const Vec3d& v)
239 {
240 velocity = v;
241 }
242
243
getAngularVelocity() const244 Vec3d Observer::getAngularVelocity() const
245 {
246 return angularVelocity;
247 }
248
249
setAngularVelocity(const Vec3d & v)250 void Observer::setAngularVelocity(const Vec3d& v)
251 {
252 angularVelocity = v;
253 }
254
255
256 /*! Determine an orientation that will make the negative z-axis point from
257 * from the observer to the target, with the y-axis pointing in direction
258 * of the component of 'up' that is orthogonal to the z-axis.
259 */
260 // TODO: This is a generally useful function that should be moved to
261 // the celmath package.
262 template<class T> static Quaternion<T>
lookAt(Point3<T> from,Point3<T> to,Vector3<T> up)263 lookAt(Point3<T> from, Point3<T> to, Vector3<T> up)
264 {
265 Vector3<T> n = to - from;
266 n.normalize();
267 Vector3<T> v = n ^ up;
268 v.normalize();
269 Vector3<T> u = v ^ n;
270
271 return Quaternion<T>::matrixToQuaternion(Matrix3<T>(v, u, -n));
272 }
273
274
getArrivalTime() const275 double Observer::getArrivalTime() const
276 {
277 if (observerMode != Travelling)
278 return realTime;
279 else
280 return journey.startTime + journey.duration;
281 }
282
283
284 /*! Tick the simulation by dt seconds. Update the observer position
285 * and orientation due to an active goto command or non-zero velocity
286 * or angular velocity.
287 */
update(double dt,double timeScale)288 void Observer::update(double dt, double timeScale)
289 {
290 realTime += dt;
291 simTime += (dt / 86400.0) * timeScale;
292
293 if (simTime >= maximumSimTime)
294 simTime = maximumSimTime;
295 if (simTime <= minimumSimTime)
296 simTime = minimumSimTime;
297
298 if (observerMode == Travelling)
299 {
300 // Compute the fraction of the trip that has elapsed; handle zero
301 // durations correctly by skipping directly to the destination.
302 float t = 1.0;
303 if (journey.duration > 0)
304 t = (float) clamp((realTime - journey.startTime) / journey.duration);
305
306 Vec3d jv = journey.to - journey.from;
307 UniversalCoord p;
308
309 // Another interpolation method . . . accelerate exponentially,
310 // maintain a constant velocity for a period of time, then
311 // decelerate. The portion of the trip spent accelerating is
312 // controlled by the parameter journey.accelTime; a value of 1 means
313 // that the entire first half of the trip will be spent accelerating
314 // and there will be no coasting at constant velocity.
315 {
316 double u = t < 0.5 ? t * 2 : (1 - t) * 2;
317 double x;
318 if (u < journey.accelTime)
319 {
320 x = exp(journey.expFactor * u) - 1.0;
321 }
322 else
323 {
324 x = exp(journey.expFactor * journey.accelTime) *
325 (journey.expFactor * (u - journey.accelTime) + 1) - 1;
326 }
327
328 if (journey.traj == Linear)
329 {
330 Vec3d v = jv;
331 if (v.length() == 0.0)
332 {
333 p = journey.from;
334 }
335 else
336 {
337 v.normalize();
338 if (t < 0.5)
339 p = journey.from + v * astro::kilometersToMicroLightYears(x);
340 else
341 p = journey.to - v * astro::kilometersToMicroLightYears(x);
342 }
343 }
344 else if (journey.traj == GreatCircle)
345 {
346 Selection centerObj = frame->getRefObject();
347 if (centerObj.body() != NULL)
348 {
349 Body* body = centerObj.body();
350 if (body->getSystem())
351 {
352 if (body->getSystem()->getPrimaryBody() != NULL)
353 centerObj = Selection(body->getSystem()->getPrimaryBody());
354 else
355 centerObj = Selection(body->getSystem()->getStar());
356 }
357 }
358
359 UniversalCoord ufrom = frame->convertToUniversal(journey.from, simTime);
360 UniversalCoord uto = frame->convertToUniversal(journey.to, simTime);
361 UniversalCoord origin = centerObj.getPosition(simTime);
362 Vec3d v0 = ufrom - origin;
363 Vec3d v1 = uto - origin;
364
365 if (jv.length() == 0.0)
366 {
367 p = journey.from;
368 }
369 else
370 {
371 x = astro::kilometersToMicroLightYears(x / jv.length());
372 Vec3d v;
373
374 if (t < 0.5)
375 v = slerp(x, v0, v1);
376 else
377 v = slerp(x, v1, v0);
378
379 p = frame->convertFromUniversal(origin + v, simTime);
380 }
381 }
382 else if (journey.traj == CircularOrbit)
383 {
384 Selection centerObj = frame->getRefObject();
385
386 UniversalCoord ufrom = frame->convertToUniversal(journey.from, simTime);
387 UniversalCoord uto = frame->convertToUniversal(journey.to, simTime);
388 UniversalCoord origin = centerObj.getPosition(simTime);
389 Vec3d v0 = ufrom - origin;
390 Vec3d v1 = uto - origin;
391
392 if (jv.length() == 0.0)
393 {
394 p = journey.from;
395 }
396 else
397 {
398 //x = astro::kilometersToMicroLightYears(x / jv.length());
399 Quatd q0(1.0);
400 Quatd q1(journey.rotation1.w, journey.rotation1.x,
401 journey.rotation1.y, journey.rotation1.z);
402 p = origin + v0 * Quatd::slerp(q0, q1, t).toMatrix3();
403 p = frame->convertFromUniversal(p, simTime);
404 }
405 }
406 }
407
408 // Spherically interpolate the orientation over the first half
409 // of the journey.
410 Quatd q;
411 if (t >= journey.startInterpolation && t < journey.endInterpolation )
412 {
413 // Smooth out the interpolation to avoid jarring changes in
414 // orientation
415 double v;
416 if (journey.traj == CircularOrbit)
417 {
418 // In circular orbit mode, interpolation of orientation must
419 // match the interpolation of position.
420 v = t;
421 }
422 else
423 {
424 v = pow(sin((t - journey.startInterpolation) /
425 (journey.endInterpolation - journey.startInterpolation) * PI / 2), 2);
426 }
427
428 // Be careful to choose the shortest path when interpolating
429 if (norm(journey.initialOrientation - journey.finalOrientation) <
430 norm(journey.initialOrientation + journey.finalOrientation))
431 {
432 q = Quatd::slerp(journey.initialOrientation,
433 journey.finalOrientation, v);
434 }
435 else
436 {
437 q = Quatd::slerp(journey.initialOrientation,
438 -journey.finalOrientation, v);
439 }
440 }
441 else if (t < journey.startInterpolation)
442 {
443 q = journey.initialOrientation;
444 }
445 else // t >= endInterpolation
446 {
447 q = journey.finalOrientation;
448 }
449
450 position = p;
451 orientation = q;
452
453 // If the journey's complete, reset to manual control
454 if (t == 1.0f)
455 {
456 if (journey.traj != CircularOrbit)
457 {
458 //situation = RigidTransform(journey.to, journey.finalOrientation);
459 position = journey.to;
460 orientation = journey.finalOrientation;
461 }
462 observerMode = Free;
463 setVelocity(Vec3d(0, 0, 0));
464 // targetVelocity = Vec3d(0, 0, 0);
465 }
466 }
467
468 if (getVelocity() != targetVelocity)
469 {
470 double t = clamp((realTime - beginAccelTime) / VELOCITY_CHANGE_TIME);
471 Vec3d v = getVelocity() * (1.0 - t) + targetVelocity * t;
472
473 // At some threshold, we just set the velocity to zero; otherwise,
474 // we'll end up with ridiculous velocities like 10^-40 m/s.
475 if (v.length() < 1.0e-12)
476 v = Vec3d(0.0, 0.0, 0.0);
477 setVelocity(v);
478 }
479
480 // Update the position
481 position = position + getVelocity() * dt;
482
483 if (observerMode == Free)
484 {
485 // Update the observer's orientation
486 Vec3d AV = getAngularVelocity();
487 Quatd dr = 0.5 * (AV * orientation);
488 orientation += dt * dr;
489 orientation.normalize();
490 }
491
492 updateUniversal();
493
494 // Update orientation for tracking--must occur after updateUniversal(), as it
495 // relies on the universal position and orientation of the observer.
496 if (!trackObject.empty())
497 {
498 Vec3d up = Vec3d(0, 1, 0) * getOrientation().toMatrix3();
499 Vec3d viewDir = trackObject.getPosition(getTime()) - getPosition();
500
501 setOrientation(lookAt(Point3d(0, 0, 0),
502 Point3d(viewDir.x, viewDir.y, viewDir.z),
503 up));
504 }
505 }
506
507
getTrackedObject() const508 Selection Observer::getTrackedObject() const
509 {
510 return trackObject;
511 }
512
513
setTrackedObject(const Selection & sel)514 void Observer::setTrackedObject(const Selection& sel)
515 {
516 trackObject = sel;
517 }
518
519
getDisplayedSurface() const520 const string& Observer::getDisplayedSurface() const
521 {
522 return displayedSurface;
523 }
524
525
setDisplayedSurface(const string & surf)526 void Observer::setDisplayedSurface(const string& surf)
527 {
528 displayedSurface = surf;
529 }
530
531
getLocationFilter() const532 uint32 Observer::getLocationFilter() const
533 {
534 return locationFilter;
535 }
536
537
setLocationFilter(uint32 _locationFilter)538 void Observer::setLocationFilter(uint32 _locationFilter)
539 {
540 locationFilter = _locationFilter;
541 }
542
543
reverseOrientation()544 void Observer::reverseOrientation()
545 {
546 Quatd q = getOrientation();
547 q.yrotate(PI);
548 setOrientation(q);
549 reverseFlag = !reverseFlag;
550 }
551
552
553
554 struct TravelExpFunc : public unary_function<double, double>
555 {
556 double dist, s;
557
TravelExpFuncTravelExpFunc558 TravelExpFunc(double d, double _s) : dist(d), s(_s) {};
559
operator ()TravelExpFunc560 double operator()(double x) const
561 {
562 // return (1.0 / x) * (exp(x / 2.0) - 1.0) - 0.5 - dist / 2.0;
563 return exp(x * s) * (x * (1 - s) + 1) - 1 - dist;
564 }
565 };
566
567
computeGotoParameters(const Selection & destination,JourneyParams & jparams,double gotoTime,double startInter,double endInter,Vec3d offset,ObserverFrame::CoordinateSystem offsetCoordSys,Vec3f up,ObserverFrame::CoordinateSystem upCoordSys)568 void Observer::computeGotoParameters(const Selection& destination,
569 JourneyParams& jparams,
570 double gotoTime,
571 double startInter,
572 double endInter,
573 Vec3d offset,
574 ObserverFrame::CoordinateSystem offsetCoordSys,
575 Vec3f up,
576 ObserverFrame::CoordinateSystem upCoordSys)
577 {
578 if (frame->getCoordinateSystem() == ObserverFrame::PhaseLock)
579 {
580 //setFrame(FrameOfReference(astro::Ecliptical, destination));
581 setFrame(ObserverFrame::Ecliptical, destination);
582 }
583 else
584 {
585 setFrame(frame->getCoordinateSystem(), destination);
586 }
587
588 UniversalCoord targetPosition = destination.getPosition(getTime());
589 Vec3d v = targetPosition - getPosition();
590 v.normalize();
591
592 jparams.traj = Linear;
593 jparams.duration = gotoTime;
594 jparams.startTime = realTime;
595
596 // Right where we are now . . .
597 jparams.from = getPosition();
598
599 if (offsetCoordSys == ObserverFrame::ObserverLocal)
600 {
601 offset = offset * orientationUniv.toMatrix3();
602 }
603 else
604 {
605 ObserverFrame offsetFrame(offsetCoordSys, destination);
606 offset = offset * offsetFrame.getFrame()->getOrientation(getTime()).toMatrix3();
607 }
608 jparams.to = targetPosition + offset;
609
610 Vec3d upd(up.x, up.y, up.z);
611 if (upCoordSys == ObserverFrame::ObserverLocal)
612 {
613 upd = upd * orientationUniv.toMatrix3();
614 }
615 else
616 {
617 ObserverFrame upFrame(upCoordSys, destination);
618 upd = upd * upFrame.getFrame()->getOrientation(getTime()).toMatrix3();
619 }
620
621 jparams.initialOrientation = getOrientation();
622 Vec3d vn = targetPosition - jparams.to;
623 Point3d focus(vn.x, vn.y, vn.z);
624 jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, upd);
625 jparams.startInterpolation = min(startInter, endInter);
626 jparams.endInterpolation = max(startInter, endInter);
627
628 jparams.accelTime = 0.5;
629 double distance = astro::microLightYearsToKilometers(jparams.from.distanceTo(jparams.to)) / 2.0;
630 pair<double, double> sol = solve_bisection(TravelExpFunc(distance, jparams.accelTime),
631 0.0001, 100.0,
632 1e-10);
633 jparams.expFactor = sol.first;
634
635 // Convert to frame coordinates
636 jparams.from = frame->convertFromUniversal(jparams.from, getTime());
637 jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
638 jparams.to = frame->convertFromUniversal(jparams.to, getTime());
639 jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());
640 }
641
642
computeGotoParametersGC(const Selection & destination,JourneyParams & jparams,double gotoTime,double startInter,double endInter,Vec3d offset,ObserverFrame::CoordinateSystem offsetCoordSys,Vec3f up,ObserverFrame::CoordinateSystem upCoordSys,const Selection & centerObj)643 void Observer::computeGotoParametersGC(const Selection& destination,
644 JourneyParams& jparams,
645 double gotoTime,
646 double startInter,
647 double endInter,
648 Vec3d offset,
649 ObserverFrame::CoordinateSystem offsetCoordSys,
650 Vec3f up,
651 ObserverFrame::CoordinateSystem upCoordSys,
652 const Selection& centerObj)
653 {
654 setFrame(frame->getCoordinateSystem(), destination);
655
656 UniversalCoord targetPosition = destination.getPosition(getTime());
657 Vec3d v = targetPosition - getPosition();
658 v.normalize();
659
660 jparams.traj = GreatCircle;
661 jparams.duration = gotoTime;
662 jparams.startTime = realTime;
663
664 jparams.centerObject = centerObj;
665
666 // Right where we are now . . .
667 jparams.from = getPosition();
668
669 ObserverFrame offsetFrame(offsetCoordSys, destination);
670 offset = offset * offsetFrame.getFrame()->getOrientation(getTime()).toMatrix3();
671
672 jparams.to = targetPosition + offset;
673
674 Vec3d upd(up.x, up.y, up.z);
675 if (upCoordSys == ObserverFrame::ObserverLocal)
676 {
677 upd = upd * orientationUniv.toMatrix3();
678 }
679 else
680 {
681 ObserverFrame upFrame(upCoordSys, destination);
682 upd = upd * upFrame.getFrame()->getOrientation(getTime()).toMatrix3();
683 }
684
685 jparams.initialOrientation = getOrientation();
686 Vec3d vn = targetPosition - jparams.to;
687 Point3d focus(vn.x, vn.y, vn.z);
688 jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, upd);
689 jparams.startInterpolation = min(startInter, endInter);
690 jparams.endInterpolation = max(startInter, endInter);
691
692 jparams.accelTime = 0.5;
693 double distance = astro::microLightYearsToKilometers(jparams.from.distanceTo(jparams.to)) / 2.0;
694 pair<double, double> sol = solve_bisection(TravelExpFunc(distance, jparams.accelTime),
695 0.0001, 100.0,
696 1e-10);
697 jparams.expFactor = sol.first;
698
699 // Convert to frame coordinates
700 jparams.from = frame->convertFromUniversal(jparams.from, getTime());
701 jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
702 jparams.to = frame->convertFromUniversal(jparams.to, getTime());
703 jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());
704 }
705
706
computeCenterParameters(const Selection & destination,JourneyParams & jparams,double centerTime)707 void Observer::computeCenterParameters(const Selection& destination,
708 JourneyParams& jparams,
709 double centerTime)
710 {
711 UniversalCoord targetPosition = destination.getPosition(getTime());
712
713 jparams.duration = centerTime;
714 jparams.startTime = realTime;
715 jparams.traj = Linear;
716
717 // Don't move through space, just rotate the camera
718 jparams.from = getPosition();
719 jparams.to = jparams.from;
720
721 Vec3d up = Vec3d(0, 1, 0) * getOrientation().toMatrix3();
722
723 jparams.initialOrientation = getOrientation();
724 Vec3d vn = targetPosition - jparams.to;
725 Point3d focus(vn.x, vn.y, vn.z);
726 jparams.finalOrientation = lookAt(Point3d(0, 0, 0), focus, up);
727 jparams.startInterpolation = 0;
728 jparams.endInterpolation = 1;
729
730
731 jparams.accelTime = 0.5;
732 jparams.expFactor = 0;
733
734 // Convert to frame coordinates
735 jparams.from = frame->convertFromUniversal(jparams.from, getTime());
736 jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
737 jparams.to = frame->convertFromUniversal(jparams.to, getTime());
738 jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());
739 }
740
741
computeCenterCOParameters(const Selection & destination,JourneyParams & jparams,double centerTime)742 void Observer::computeCenterCOParameters(const Selection& destination,
743 JourneyParams& jparams,
744 double centerTime)
745 {
746 jparams.duration = centerTime;
747 jparams.startTime = realTime;
748 jparams.traj = CircularOrbit;
749
750 jparams.centerObject = frame->getRefObject();
751 jparams.expFactor = 0.5;
752
753 Vec3d v = destination.getPosition(getTime()) - getPosition();
754 Vec3d w = Vec3d(0.0, 0.0, -1.0) * getOrientation().toMatrix3();
755 v.normalize();
756
757 Selection centerObj = frame->getRefObject();
758 UniversalCoord centerPos = centerObj.getPosition(getTime());
759 UniversalCoord targetPosition = destination.getPosition(getTime());
760
761 Quatd q = Quatd::vecToVecRotation(v, w);
762
763 jparams.from = getPosition();
764 jparams.to = centerPos + ((getPosition() - centerPos) * q.toMatrix3());
765 jparams.initialOrientation = getOrientation();
766 jparams.finalOrientation = getOrientation() * q;
767
768 jparams.startInterpolation = 0.0;
769 jparams.endInterpolation = 1.0;
770
771 jparams.rotation1 = q;
772
773 // Convert to frame coordinates
774 jparams.from = frame->convertFromUniversal(jparams.from, getTime());
775 jparams.initialOrientation = frame->convertFromUniversal(jparams.initialOrientation, getTime());
776 jparams.to = frame->convertFromUniversal(jparams.to, getTime());
777 jparams.finalOrientation = frame->convertFromUniversal(jparams.finalOrientation, getTime());
778 }
779
780
781 /*! Center the selection by moving on a circular orbit arround
782 * the primary body (refObject).
783 */
centerSelectionCO(const Selection & selection,double centerTime)784 void Observer::centerSelectionCO(const Selection& selection, double centerTime)
785 {
786 if (!selection.empty() && !frame->getRefObject().empty())
787 {
788 computeCenterCOParameters(selection, journey, centerTime);
789 observerMode = Travelling;
790 }
791 }
792
793
getMode() const794 Observer::ObserverMode Observer::getMode() const
795 {
796 return observerMode;
797 }
798
799
setMode(Observer::ObserverMode mode)800 void Observer::setMode(Observer::ObserverMode mode)
801 {
802 observerMode = mode;
803 }
804
805
806 // Private method to convert coordinates when a new observer frame is set.
807 // Universal coordinates remain the same. All frame coordinates get updated, including
808 // the goto parameters.
convertFrameCoordinates(const ObserverFrame * newFrame)809 void Observer::convertFrameCoordinates(const ObserverFrame* newFrame)
810 {
811 double now = getTime();
812
813 // Universal coordinates don't change.
814 // Convert frame coordinates to the new frame.
815 position = newFrame->convertFromUniversal(positionUniv, now);
816 orientation = newFrame->convertFromUniversal(orientationUniv, now);
817
818 // Convert goto parameters to the new frame
819 journey.from = ObserverFrame::convert(frame, newFrame, journey.from, now);
820 journey.initialOrientation = ObserverFrame::convert(frame, newFrame, journey.initialOrientation, now);
821 journey.to = ObserverFrame::convert(frame, newFrame, journey.to, now);
822 journey.finalOrientation = ObserverFrame::convert(frame, newFrame, journey.finalOrientation, now);
823 }
824
825
826 /*! Set the observer's reference frame. The position of the observer in
827 * universal coordinates will not change.
828 */
setFrame(ObserverFrame::CoordinateSystem cs,const Selection & refObj,const Selection & targetObj)829 void Observer::setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj, const Selection& targetObj)
830 {
831 ObserverFrame* newFrame = new ObserverFrame(cs, refObj, targetObj);
832 if (newFrame != NULL)
833 {
834 convertFrameCoordinates(newFrame);
835 delete frame;
836 frame = newFrame;
837 }
838 }
839
840
841 /*! Set the observer's reference frame. The position of the observer in
842 * universal coordinates will not change.
843 */
setFrame(ObserverFrame::CoordinateSystem cs,const Selection & refObj)844 void Observer::setFrame(ObserverFrame::CoordinateSystem cs, const Selection& refObj)
845 {
846 setFrame(cs, refObj, Selection());
847 }
848
849
850 /*! Set the observer's reference frame. The position of the observer in
851 * universal coordinates will not change.
852 */
setFrame(const ObserverFrame & f)853 void Observer::setFrame(const ObserverFrame& f)
854 {
855 if (frame != &f)
856 {
857 ObserverFrame* newFrame = new ObserverFrame(f);
858
859 if (newFrame != NULL)
860 {
861 if (frame != NULL)
862 {
863 convertFrameCoordinates(newFrame);
864 delete frame;
865 }
866 frame = newFrame;
867 }
868 }
869 }
870
871
872 /*! Get the current reference frame for the observer.
873 */
getFrame() const874 const ObserverFrame* Observer::getFrame() const
875 {
876 return frame;
877 }
878
879
880 /*! Rotate the observer about its center.
881 */
rotate(Quatf q)882 void Observer::rotate(Quatf q)
883 {
884 Quatd qd(q.w, q.x, q.y, q.z);
885 orientation = qd * orientation;
886 updateUniversal();
887 }
888
889
890 /*! Orbit around the reference object (if there is one.) This involves changing
891 * both the observer's position and orientation. If there is no current center
892 * object, the specified selection will be used as the center of rotation, and
893 * the observer reference frame will be modified.
894 */
orbit(const Selection & selection,Quatf q)895 void Observer::orbit(const Selection& selection, Quatf q)
896 {
897 Selection center = frame->getRefObject();
898 if (center.empty() && !selection.empty())
899 {
900 // Automatically set the center of the reference frame
901 center = selection;
902 setFrame(frame->getCoordinateSystem(), center);
903 }
904
905 if (!center.empty())
906 {
907 // Get the focus position (center of rotation) in frame
908 // coordinates; in order to make this function work in all
909 // frames of reference, it's important to work in frame
910 // coordinates.
911 UniversalCoord focusPosition = center.getPosition(getTime());
912 //focusPosition = frame.fromUniversal(RigidTransform(focusPosition), getTime()).translation;
913 focusPosition = frame->convertFromUniversal(focusPosition, getTime());
914
915 // v = the vector from the observer's position to the focus
916 //Vec3d v = situation.translation - focusPosition;
917 Vec3d v = position - focusPosition;
918
919 // Get a double precision version of the rotation
920 Quatd qd(q.w, q.x, q.y, q.z);
921
922 // To give the right feel for rotation, we want to premultiply
923 // the current orientation by q. However, because of the order in
924 // which we apply transformations later on, we can't pre-multiply.
925 // To get around this, we compute a rotation q2 such
926 // that q1 * r = r * q2.
927 Quatd qd2 = ~orientation * qd * orientation;
928 qd2.normalize();
929
930 // Roundoff errors will accumulate and cause the distance between
931 // viewer and focus to drift unless we take steps to keep the
932 // length of v constant.
933 double distance = v.length();
934 v = v * qd2.toMatrix3();
935 v.normalize();
936 v *= distance;
937
938 //situation.rotation = situation.rotation * qd2;
939 //situation.translation = focusPosition + v;
940 orientation = orientation * qd2;
941 position = focusPosition + v;
942 updateUniversal();
943 }
944 }
945
946
947 /*! Exponential camera dolly--move toward or away from the selected object
948 * at a rate dependent on the observer's distance from the object.
949 */
changeOrbitDistance(const Selection & selection,float d)950 void Observer::changeOrbitDistance(const Selection& selection, float d)
951 {
952 Selection center = frame->getRefObject();
953 if (center.empty() && !selection.empty())
954 {
955 center = selection;
956 setFrame(frame->getCoordinateSystem(), center);
957 }
958
959 if (!center.empty())
960 {
961 UniversalCoord focusPosition = center.getPosition(getTime());
962
963 double size = center.radius();
964
965 // Somewhat arbitrary parameters to chosen to give the camera movement
966 // a nice feel. They should probably be function parameters.
967 double minOrbitDistance = astro::kilometersToMicroLightYears(size);
968 double naturalOrbitDistance = astro::kilometersToMicroLightYears(4.0 * size);
969
970 // Determine distance and direction to the selected object
971 Vec3d v = getPosition() - focusPosition;
972 double currentDistance = v.length();
973
974 if (currentDistance < minOrbitDistance)
975 minOrbitDistance = currentDistance * 0.5;
976
977 if (currentDistance >= minOrbitDistance && naturalOrbitDistance != 0)
978 {
979 double r = (currentDistance - minOrbitDistance) / naturalOrbitDistance;
980 double newDistance = minOrbitDistance + naturalOrbitDistance * exp(log(r) + d);
981 v = v * (newDistance / currentDistance);
982
983 position = frame->convertFromUniversal(focusPosition + v, getTime());
984 updateUniversal();
985 }
986 }
987 }
988
989
setTargetSpeed(float s)990 void Observer::setTargetSpeed(float s)
991 {
992 targetSpeed = s;
993 Vec3d v;
994 if (reverseFlag)
995 s = -s;
996 if (trackObject.empty())
997 {
998 trackingOrientation = getOrientation();
999 // Generate vector for velocity using current orientation
1000 // and specified speed.
1001 v = Vec3d(0, 0, -s) * getOrientation().toMatrix4();
1002 }
1003 else
1004 {
1005 // Use tracking orientation vector to generate target velocity
1006 v = Vec3d(0, 0, -s) * trackingOrientation.toMatrix4();
1007 }
1008
1009 targetVelocity = v;
1010 initialVelocity = getVelocity();
1011 beginAccelTime = realTime;
1012 }
1013
1014
getTargetSpeed()1015 float Observer::getTargetSpeed()
1016 {
1017 return (float) targetSpeed;
1018 }
1019
1020
gotoJourney(const JourneyParams & params)1021 void Observer::gotoJourney(const JourneyParams& params)
1022 {
1023 journey = params;
1024 double distance = astro::microLightYearsToKilometers(journey.from.distanceTo(journey.to)) / 2.0;
1025 pair<double, double> sol = solve_bisection(TravelExpFunc(distance, journey.accelTime),
1026 0.0001, 100.0,
1027 1e-10);
1028 journey.expFactor = sol.first;
1029 journey.startTime = realTime;
1030 observerMode = Travelling;
1031 }
1032
gotoSelection(const Selection & selection,double gotoTime,Vec3f up,ObserverFrame::CoordinateSystem upFrame)1033 void Observer::gotoSelection(const Selection& selection,
1034 double gotoTime,
1035 Vec3f up,
1036 ObserverFrame::CoordinateSystem upFrame)
1037 {
1038 gotoSelection(selection, gotoTime, 0.0, 0.5, up, upFrame);
1039 }
1040
1041
1042 // Return the preferred distance for viewing an object
getPreferredDistance(const Selection & selection)1043 static double getPreferredDistance(const Selection& selection)
1044 {
1045 switch (selection.getType())
1046 {
1047 case Selection::Type_Body:
1048 // Handle reference points (i.e. invisible objects) specially, since the
1049 // actual radius of the point is meaningless. Instead, use the size of
1050 // bounding sphere of all child objects. This is useful for system
1051 // barycenters--the normal goto command will place the observer at
1052 // a viewpoint in which the entire system can be seen.
1053 if (selection.body()->getClassification() == Body::Invisible)
1054 {
1055 double r = selection.body()->getRadius();
1056 if (selection.body()->getFrameTree() != NULL)
1057 r = selection.body()->getFrameTree()->boundingSphereRadius();
1058 return min(astro::lightYearsToKilometers(0.1), r * 5.0);
1059 }
1060 else
1061 {
1062 return 5.0 * selection.radius();
1063 }
1064
1065 case Selection::Type_DeepSky:
1066 return 5.0 * selection.radius();
1067
1068 case Selection::Type_Star:
1069 if (selection.star()->getVisibility())
1070 {
1071 return 100.0 * selection.radius();
1072 }
1073 else
1074 {
1075 // Handle star system barycenters specially, using the same approach as
1076 // for reference points in solar systems.
1077 double maxOrbitRadius = 0.0;
1078 const vector<Star*>* orbitingStars = selection.star()->getOrbitingStars();
1079 if (orbitingStars != NULL)
1080 {
1081 for (vector<Star*>::const_iterator iter = orbitingStars->begin();
1082 iter != orbitingStars->end(); iter++)
1083 {
1084 Orbit* orbit = (*iter)->getOrbit();
1085 if (orbit != NULL)
1086 maxOrbitRadius = max(orbit->getBoundingRadius(), maxOrbitRadius);
1087 }
1088 }
1089
1090 if (maxOrbitRadius == 0.0)
1091 return astro::AUtoKilometers(1.0);
1092 else
1093 return maxOrbitRadius * 5.0;
1094 }
1095
1096 case Selection::Type_Location:
1097 {
1098 double maxDist = getPreferredDistance(selection.location()->getParentBody());
1099 return max(min(selection.location()->getSize() * 50.0, maxDist),
1100 1.0);
1101 }
1102
1103 default:
1104 return 1.0;
1105 }
1106 }
1107
1108
1109 // Given an object and its current distance from the camera, determine how
1110 // close we should go on the next goto.
getOrbitDistance(const Selection & selection,double currentDistance)1111 static double getOrbitDistance(const Selection& selection,
1112 double currentDistance)
1113 {
1114 // If further than 10 times the preferrred distance, goto the
1115 // preferred distance. If closer, zoom in 10 times closer or to the
1116 // minimum distance.
1117 double maxDist = astro::kilometersToMicroLightYears(getPreferredDistance(selection));
1118 double minDist = astro::kilometersToMicroLightYears(1.01 * selection.radius());
1119 double dist = (currentDistance > maxDist * 10.0) ? maxDist : currentDistance * 0.1;
1120
1121 return max(dist, minDist);
1122 }
1123
1124
gotoSelection(const Selection & selection,double gotoTime,double startInter,double endInter,Vec3f up,ObserverFrame::CoordinateSystem upFrame)1125 void Observer::gotoSelection(const Selection& selection,
1126 double gotoTime,
1127 double startInter,
1128 double endInter,
1129 Vec3f up,
1130 ObserverFrame::CoordinateSystem upFrame)
1131 {
1132 if (!selection.empty())
1133 {
1134 UniversalCoord pos = selection.getPosition(getTime());
1135 Vec3d v = pos - getPosition();
1136 double distance = v.length();
1137
1138 double orbitDistance = getOrbitDistance(selection, distance);
1139
1140 computeGotoParameters(selection, journey, gotoTime,
1141 startInter, endInter,
1142 v * -(orbitDistance / distance),
1143 ObserverFrame::Universal,
1144 up, upFrame);
1145 observerMode = Travelling;
1146 }
1147 }
1148
1149
1150 /*! Like normal goto, except we'll follow a great circle trajectory. Useful
1151 * for travelling between surface locations, where we'd rather not go straight
1152 * through the middle of a planet.
1153 */
gotoSelectionGC(const Selection & selection,double gotoTime,double,double,Vec3f up,ObserverFrame::CoordinateSystem upFrame)1154 void Observer::gotoSelectionGC(const Selection& selection,
1155 double gotoTime,
1156 double /*startInter*/, //TODO: remove parameter??
1157 double /*endInter*/, //TODO: remove parameter??
1158 Vec3f up,
1159 ObserverFrame::CoordinateSystem upFrame)
1160 {
1161 if (!selection.empty())
1162 {
1163 Selection centerObj = selection.parent();
1164
1165 UniversalCoord pos = selection.getPosition(getTime());
1166 Vec3d v = pos - centerObj.getPosition(getTime());
1167 double distanceToCenter = v.length();
1168 Vec3d viewVec = pos - getPosition();
1169 double orbitDistance = getOrbitDistance(selection,
1170 viewVec.length());
1171 if (selection.location() != NULL)
1172 {
1173 Selection parent = selection.parent();
1174 double maintainDist = astro::kilometersToMicroLightYears(getPreferredDistance(parent));
1175 Vec3d parentPos = parent.getPosition(getTime()) - getPosition();
1176 double parentDist = parentPos.length() -
1177 astro::kilometersToMicroLightYears(parent.radius());
1178
1179 if (parentDist <= maintainDist && parentDist > orbitDistance)
1180 {
1181 orbitDistance = parentDist;
1182 }
1183 }
1184
1185 computeGotoParametersGC(selection, journey, gotoTime,
1186 //startInter, endInter,
1187 0.25, 0.75,
1188 v * (orbitDistance / distanceToCenter),
1189 ObserverFrame::Universal,
1190 up, upFrame,
1191 centerObj);
1192 observerMode = Travelling;
1193 }
1194 }
1195
1196
gotoSelection(const Selection & selection,double gotoTime,double distance,Vec3f up,ObserverFrame::CoordinateSystem upFrame)1197 void Observer::gotoSelection(const Selection& selection,
1198 double gotoTime,
1199 double distance,
1200 Vec3f up,
1201 ObserverFrame::CoordinateSystem upFrame)
1202 {
1203 if (!selection.empty())
1204 {
1205 UniversalCoord pos = selection.getPosition(getTime());
1206 // The destination position lies along the line between the current
1207 // position and the star
1208 Vec3d v = pos - getPosition();
1209 v.normalize();
1210
1211 computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75,
1212 v * -distance * 1e6, ObserverFrame::Universal,
1213 up, upFrame);
1214 observerMode = Travelling;
1215 }
1216 }
1217
1218
gotoSelectionGC(const Selection & selection,double gotoTime,double distance,Vec3f up,ObserverFrame::CoordinateSystem upFrame)1219 void Observer::gotoSelectionGC(const Selection& selection,
1220 double gotoTime,
1221 double distance,
1222 Vec3f up,
1223 ObserverFrame::CoordinateSystem upFrame)
1224 {
1225 if (!selection.empty())
1226 {
1227 Selection centerObj = selection.parent();
1228
1229 UniversalCoord pos = selection.getPosition(getTime());
1230 Vec3d v = pos - centerObj.getPosition(getTime());
1231 v.normalize();
1232
1233 // The destination position lies along a line extended from the center
1234 // object to the target object
1235 computeGotoParametersGC(selection, journey, gotoTime, 0.25, 0.75,
1236 v * -distance * 1e6, ObserverFrame::Universal,
1237 up, upFrame,
1238 centerObj);
1239 observerMode = Travelling;
1240 }
1241 }
1242
1243
gotoSelectionLongLat(const Selection & selection,double gotoTime,double distance,float longitude,float latitude,Vec3f up)1244 void Observer::gotoSelectionLongLat(const Selection& selection,
1245 double gotoTime,
1246 double distance,
1247 float longitude,
1248 float latitude,
1249 Vec3f up)
1250 {
1251 if (!selection.empty())
1252 {
1253 double phi = -latitude + PI / 2;
1254 double theta = longitude;
1255 double x = cos(theta) * sin(phi);
1256 double y = cos(phi);
1257 double z = -sin(theta) * sin(phi);
1258 computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75,
1259 Vec3d(x, y, z) * distance * 1e6, ObserverFrame::BodyFixed,
1260 up, ObserverFrame::BodyFixed);
1261 observerMode = Travelling;
1262 }
1263 }
1264
1265
gotoLocation(const UniversalCoord & toPosition,const Quatd & toOrientation,double duration)1266 void Observer::gotoLocation(const UniversalCoord& toPosition,
1267 const Quatd& toOrientation,
1268 double duration)
1269 {
1270 journey.startTime = realTime;
1271 journey.duration = duration;
1272
1273 journey.from = position;
1274 journey.initialOrientation = orientation;
1275 journey.to = toPosition;
1276 journey.finalOrientation = toOrientation;
1277
1278 journey.startInterpolation = 0.25f;
1279 journey.endInterpolation = 0.75f;
1280
1281 journey.accelTime = 0.5;
1282 double distance = astro::microLightYearsToKilometers(journey.from.distanceTo(journey.to)) / 2.0;
1283 pair<double, double> sol = solve_bisection(TravelExpFunc(distance, journey.accelTime),
1284 0.0001, 100.0,
1285 1e-10);
1286 journey.expFactor = sol.first;
1287
1288 observerMode = Travelling;
1289 }
1290
1291
getSelectionLongLat(const Selection & selection,double & distance,double & longitude,double & latitude)1292 void Observer::getSelectionLongLat(const Selection& selection,
1293 double& distance,
1294 double& longitude,
1295 double& latitude)
1296 {
1297 // Compute distance (km) and lat/long (degrees) of observer with
1298 // respect to currently selected object.
1299 if (!selection.empty())
1300 {
1301 ObserverFrame frame(ObserverFrame::BodyFixed, selection);
1302 Point3d bfPos = (Point3d) frame.convertFromUniversal(positionUniv, getTime());
1303
1304 distance = bfPos.distanceFromOrigin();
1305
1306 // Convert from Celestia's coordinate system
1307 double x = bfPos.x;
1308 double y = -bfPos.z;
1309 double z = bfPos.y;
1310
1311 longitude = radToDeg(atan2(y, x));
1312 latitude = radToDeg(PI/2 - acos(z / distance));
1313
1314 // Convert distance from light years to kilometers.
1315 distance = astro::microLightYearsToKilometers(distance);
1316 }
1317 }
1318
1319
gotoSurface(const Selection & sel,double duration)1320 void Observer::gotoSurface(const Selection& sel, double duration)
1321 {
1322 Vec3d v = getPosition() - sel.getPosition(getTime());
1323 v.normalize();
1324
1325 Vec3d viewDir = Vec3d(0, 0, -1) * orientationUniv.toMatrix3();
1326 Vec3d up = Vec3d(0, 1, 0) * orientationUniv.toMatrix3();
1327 Quatd q = orientationUniv;
1328 if (v * viewDir < 0.0)
1329 {
1330 q = lookAt(Point3d(0.0, 0.0, 0.0), Point3d(0.0, 0.0, 0.0) + up, v);
1331 }
1332
1333 ObserverFrame frame(ObserverFrame::BodyFixed, sel);
1334 UniversalCoord bfPos = frame.convertFromUniversal(positionUniv, getTime());
1335 q = frame.convertFromUniversal(q, getTime());
1336
1337 double height = 1.0001 * astro::kilometersToMicroLightYears(sel.radius());
1338 Vec3d dir = bfPos - Point3d(0.0, 0.0, 0.0);
1339 dir.normalize();
1340 dir *= height;
1341 UniversalCoord nearSurfacePoint(dir.x, dir.y, dir.z);
1342
1343 gotoLocation(nearSurfacePoint, q, duration);
1344 };
1345
1346
cancelMotion()1347 void Observer::cancelMotion()
1348 {
1349 observerMode = Free;
1350 }
1351
1352
centerSelection(const Selection & selection,double centerTime)1353 void Observer::centerSelection(const Selection& selection, double centerTime)
1354 {
1355 if (!selection.empty())
1356 {
1357 computeCenterParameters(selection, journey, centerTime);
1358 observerMode = Travelling;
1359 }
1360 }
1361
1362
follow(const Selection & selection)1363 void Observer::follow(const Selection& selection)
1364 {
1365 setFrame(ObserverFrame::Ecliptical, selection);
1366 }
1367
1368
geosynchronousFollow(const Selection & selection)1369 void Observer::geosynchronousFollow(const Selection& selection)
1370 {
1371 if (selection.body() != NULL ||
1372 selection.location() != NULL ||
1373 selection.star() != NULL)
1374 {
1375 setFrame(ObserverFrame::BodyFixed, selection);
1376 }
1377 }
1378
1379
phaseLock(const Selection & selection)1380 void Observer::phaseLock(const Selection& selection)
1381 {
1382 Selection refObject = frame->getRefObject();
1383
1384 if (selection != refObject)
1385 {
1386 if (refObject.body() != NULL || refObject.star() != NULL)
1387 {
1388 setFrame(ObserverFrame::PhaseLock, refObject, selection);
1389 }
1390 }
1391 else
1392 {
1393 // Selection and reference object are identical, so the frame is undefined.
1394 // We'll instead use the object's star as the target object.
1395 if (selection.body() != NULL)
1396 {
1397 setFrame(ObserverFrame::PhaseLock, selection, Selection(selection.body()->getSystem()->getStar()));
1398 }
1399 }
1400 }
1401
1402
chase(const Selection & selection)1403 void Observer::chase(const Selection& selection)
1404 {
1405 if (selection.body() != NULL || selection.star() != NULL)
1406 {
1407 setFrame(ObserverFrame::Chase, selection);
1408 }
1409 }
1410
1411
getFOV() const1412 float Observer::getFOV() const
1413 {
1414 return fov;
1415 }
1416
1417
setFOV(float _fov)1418 void Observer::setFOV(float _fov)
1419 {
1420 fov = _fov;
1421 }
1422
1423
getPickRay(float x,float y) const1424 Vec3f Observer::getPickRay(float x, float y) const
1425 {
1426 float s = 2 * (float) tan(fov / 2.0);
1427
1428 Vec3f pickDirection = Vec3f(x * s, y * s, -1.0f);
1429 pickDirection.normalize();
1430
1431 return pickDirection;
1432 }
1433
1434
1435 // Internal method to update the position and orientation of the observer in
1436 // universal coordinates.
updateUniversal()1437 void Observer::updateUniversal()
1438 {
1439 positionUniv = frame->convertToUniversal(position, simTime);
1440 orientationUniv = frame->convertToUniversal(orientation, simTime);
1441 }
1442
1443
1444 /*! Create the default 'universal' observer frame, with a center at the
1445 * Solar System barycenter and coordinate axes of the J200Ecliptic
1446 * reference frame.
1447 */
ObserverFrame()1448 ObserverFrame::ObserverFrame() :
1449 coordSys(Universal),
1450 frame(NULL)
1451 {
1452 frame = createFrame(Universal, Selection(), Selection());
1453 frame->addRef();
1454 }
1455
1456
1457 /*! Create a new frame with the specified coordinate system and
1458 * reference object. The targetObject is only needed for phase
1459 * lock frames; the argument is ignored for other frames.
1460 */
ObserverFrame(CoordinateSystem _coordSys,const Selection & _refObject,const Selection & _targetObject)1461 ObserverFrame::ObserverFrame(CoordinateSystem _coordSys,
1462 const Selection& _refObject,
1463 const Selection& _targetObject) :
1464 coordSys(_coordSys),
1465 frame(NULL),
1466 targetObject(_targetObject)
1467 {
1468 frame = createFrame(_coordSys, _refObject, _targetObject);
1469 frame->addRef();
1470 }
1471
1472
1473 /*! Create a new ObserverFrame with the specified reference frame.
1474 * The coordinate system of this frame will be marked as unknown.
1475 */
ObserverFrame(const ReferenceFrame & f)1476 ObserverFrame::ObserverFrame(const ReferenceFrame &f) :
1477 coordSys(Unknown),
1478 frame(&f)
1479 {
1480 frame->addRef();
1481 }
1482
1483
1484 /*! Copy constructor. */
ObserverFrame(const ObserverFrame & f)1485 ObserverFrame::ObserverFrame(const ObserverFrame& f) :
1486 coordSys(f.coordSys),
1487 frame(f.frame),
1488 targetObject(f.targetObject)
1489 {
1490 frame->addRef();
1491 }
1492
1493
operator =(const ObserverFrame & f)1494 ObserverFrame& ObserverFrame::operator=(const ObserverFrame& f)
1495 {
1496 coordSys = f.coordSys;
1497 targetObject = f.targetObject;
1498
1499 // In case frames are the same, make sure we addref before releasing
1500 f.frame->addRef();
1501 frame->release();
1502 frame = f.frame;
1503
1504 return *this;
1505 }
1506
1507
~ObserverFrame()1508 ObserverFrame::~ObserverFrame()
1509 {
1510 if (frame != NULL)
1511 frame->release();
1512 }
1513
1514
1515 ObserverFrame::CoordinateSystem
getCoordinateSystem() const1516 ObserverFrame::getCoordinateSystem() const
1517 {
1518 return coordSys;
1519 }
1520
1521
1522 Selection
getRefObject() const1523 ObserverFrame::getRefObject() const
1524 {
1525 return frame->getCenter();
1526 }
1527
1528
1529 Selection
getTargetObject() const1530 ObserverFrame::getTargetObject() const
1531 {
1532 return targetObject;
1533 }
1534
1535
1536 const ReferenceFrame*
getFrame() const1537 ObserverFrame::getFrame() const
1538 {
1539 return frame;
1540 }
1541
1542
1543 UniversalCoord
convertFromUniversal(const UniversalCoord & uc,double tjd) const1544 ObserverFrame::convertFromUniversal(const UniversalCoord& uc, double tjd) const
1545 {
1546 return frame->convertFromUniversal(uc, tjd);
1547 }
1548
1549
1550 UniversalCoord
convertToUniversal(const UniversalCoord & uc,double tjd) const1551 ObserverFrame::convertToUniversal(const UniversalCoord& uc, double tjd) const
1552 {
1553 return frame->convertToUniversal(uc, tjd);
1554 }
1555
1556
1557 Quatd
convertFromUniversal(const Quatd & q,double tjd) const1558 ObserverFrame::convertFromUniversal(const Quatd& q, double tjd) const
1559 {
1560 return frame->convertFromUniversal(q, tjd);
1561 }
1562
1563
1564 Quatd
convertToUniversal(const Quatd & q,double tjd) const1565 ObserverFrame::convertToUniversal(const Quatd& q, double tjd) const
1566 {
1567 return frame->convertToUniversal(q, tjd);
1568 }
1569
1570
1571 /*! Convert a position from one frame to another.
1572 */
1573 UniversalCoord
convert(const ObserverFrame * fromFrame,const ObserverFrame * toFrame,const UniversalCoord & uc,double t)1574 ObserverFrame::convert(const ObserverFrame* fromFrame,
1575 const ObserverFrame* toFrame,
1576 const UniversalCoord& uc,
1577 double t)
1578 {
1579 // Perform the conversion fromFrame -> universal -> toFrame
1580 return toFrame->convertFromUniversal(fromFrame->convertToUniversal(uc, t), t);
1581 }
1582
1583
1584 /*! Convert an orientation from one frame to another.
1585 */
1586 Quatd
convert(const ObserverFrame * fromFrame,const ObserverFrame * toFrame,const Quatd & q,double t)1587 ObserverFrame::convert(const ObserverFrame* fromFrame,
1588 const ObserverFrame* toFrame,
1589 const Quatd& q,
1590 double t)
1591 {
1592 // Perform the conversion fromFrame -> universal -> toFrame
1593 return toFrame->convertFromUniversal(fromFrame->convertToUniversal(q, t), t);
1594 }
1595
1596
1597 // Create the ReferenceFrame for the specified observer frame parameters.
1598 ReferenceFrame*
createFrame(CoordinateSystem _coordSys,const Selection & _refObject,const Selection & _targetObject)1599 ObserverFrame::createFrame(CoordinateSystem _coordSys,
1600 const Selection& _refObject,
1601 const Selection& _targetObject)
1602 {
1603 switch (_coordSys)
1604 {
1605 case Universal:
1606 return new J2000EclipticFrame(Selection());
1607
1608 case Ecliptical:
1609 return new J2000EclipticFrame(_refObject);
1610
1611 case Equatorial:
1612 return new BodyMeanEquatorFrame(_refObject, _refObject);
1613
1614 case BodyFixed:
1615 return new BodyFixedFrame(_refObject, _refObject);
1616
1617 case PhaseLock:
1618 {
1619 return new TwoVectorFrame(_refObject,
1620 FrameVector::createRelativePositionVector(_refObject, _targetObject), 1,
1621 FrameVector::createRelativeVelocityVector(_refObject, _targetObject), 2);
1622 }
1623
1624 case Chase:
1625 {
1626 return new TwoVectorFrame(_refObject,
1627 FrameVector::createRelativeVelocityVector(_refObject, _refObject.parent()), 1,
1628 FrameVector::createRelativePositionVector(_refObject, _refObject.parent()), 2);
1629 }
1630
1631 case PhaseLock_Old:
1632 {
1633 FrameVector rotAxis(FrameVector::createConstantVector(Vec3d(0, 1, 0),
1634 new BodyMeanEquatorFrame(_refObject, _refObject)));
1635 return new TwoVectorFrame(_refObject,
1636 FrameVector::createRelativePositionVector(_refObject, _targetObject), 3,
1637 rotAxis, 2);
1638 }
1639
1640 case Chase_Old:
1641 {
1642 FrameVector rotAxis(FrameVector::createConstantVector(Vec3d(0, 1, 0),
1643 new BodyMeanEquatorFrame(_refObject, _refObject)));
1644
1645 return new TwoVectorFrame(_refObject,
1646 FrameVector::createRelativeVelocityVector(_refObject.parent(), _refObject), 3,
1647 rotAxis, 2);
1648 }
1649
1650 case ObserverLocal:
1651 // TODO: This is only used for computing up vectors for orientation; it does
1652 // define a proper frame for the observer position orientation.
1653 return new J2000EclipticFrame(Selection());
1654
1655 default:
1656 return new J2000EclipticFrame(_refObject);
1657 }
1658 }
1659
1660
1661