Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

observer.cpp

Go to the documentation of this file.
00001 // observer.cpp
00002 //
00003 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <celmath/mathlib.h>
00011 #include <celmath/solve.h>
00012 #include "observer.h"
00013 #include "simulation.h"
00014 
00015 using namespace std;
00016 
00017 
00018 #define LY 9466411842000.000
00019 #define VELOCITY_CHANGE_TIME      0.25f
00020 
00021 
00022 static Vec3d slerp(double t, const Vec3d& v0, const Vec3d& v1)
00023 {
00024     double r0 = v0.length();
00025     double r1 = v1.length();
00026     Vec3d u = v0 / r0;
00027     Vec3d n = u ^ (v1 / r1);
00028     n.normalize();
00029     Vec3d v = n ^ u;
00030     if (v * v1 < 0.0)
00031         v = -v;
00032 
00033     double cosTheta = u * (v1 / r1);
00034     double theta = acos(cosTheta);
00035 
00036     return (cos(theta * t) * u + sin(theta * t) * v) * Mathd::lerp(t, r0, r1);
00037 }
00038 
00039 
00040 Observer::Observer() :
00041     simTime(0.0),
00042     velocity(0.0, 0.0, 0.0),
00043     angularVelocity(0.0f, 0.0f, 0.0f),
00044     realTime(0.0),
00045     targetSpeed(0.0),
00046     targetVelocity(0.0, 0.0, 0.0),
00047     initialVelocity(0.0, 0.0, 0.0),
00048     beginAccelTime(0.0),
00049     observerMode(Free),
00050     trackingOrientation(1.0f, 0.0f, 0.0f, 0.0f),
00051     fov((float) (PI / 4.0)),
00052     reverseFlag(false),
00053     locationFilter(~0)
00054 {
00055 }
00056 
00057 
00058 double Observer::getTime() const
00059 {
00060     return simTime;
00061 };
00062 
00063 void Observer::setTime(double jd)
00064 {
00065     simTime = jd;
00066 }
00067 
00068 
00069 UniversalCoord Observer::getPosition() const
00070 {
00071     // TODO: Optimize this!  Dirty bit should be set by Simulation::setTime and
00072     // by Observer::update(), Observer::setOrientation(), Observer::setPosition()
00073     return frame.toUniversal(situation, getTime()).translation;
00074 }
00075 
00076 
00077 Point3d Observer::getRelativePosition(const Point3d& p) const
00078 {
00079     BigFix x(p.x);
00080     BigFix y(p.y);
00081     BigFix z(p.z);
00082 
00083     UniversalCoord position = getPosition();
00084     double dx = (double) (position.x - x);
00085     double dy = (double) (position.y - y);
00086     double dz = (double) (position.z - z);
00087 
00088     return Point3d(dx / LY, dy / LY, dz / LY);
00089 }
00090 
00091 
00092 Quatf Observer::getOrientation() const
00093 {
00094     Quatd q = frame.toUniversal(situation, getTime()).rotation;
00095     return Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z);
00096 }
00097 
00098 
00099 void Observer::setOrientation(const Quatf& q)
00100 {
00101     RigidTransform rt = frame.toUniversal(situation, getTime());
00102     rt.rotation = Quatd(q.w, q.x, q.y, q.z);
00103     situation = frame.fromUniversal(rt, getTime());
00104 }
00105 
00106 
00107 void Observer::setOrientation(const Quatd& q)
00108 {
00109     setOrientation(Quatf((float) q.w, (float) q.x, (float) q.y, (float) q.z));
00110 }
00111 
00112 
00113 Vec3d Observer::getVelocity() const
00114 {
00115     return velocity;
00116 }
00117 
00118 
00119 void Observer::setVelocity(const Vec3d& v)
00120 {
00121     velocity = v;
00122 }
00123 
00124 
00125 Vec3f Observer::getAngularVelocity() const
00126 {
00127     return angularVelocity;
00128 }
00129 
00130 
00131 void Observer::setAngularVelocity(const Vec3f& v)
00132 {
00133     angularVelocity = v;
00134 }
00135 
00136 
00137 void Observer::setPosition(const Point3d& p)
00138 {
00139     setPosition(UniversalCoord(p));
00140 }
00141 
00142 
00143 void Observer::setPosition(const UniversalCoord& p)
00144 {
00145     RigidTransform rt = frame.toUniversal(situation, getTime());
00146     rt.translation = p;
00147     situation = frame.fromUniversal(rt, getTime());
00148 }
00149 
00150 
00151 RigidTransform Observer::getSituation() const
00152 {
00153     return frame.toUniversal(situation, getTime());
00154 }
00155                         
00156 
00157 
00158 void Observer::setSituation(const RigidTransform& xform)
00159 {
00160     situation = frame.fromUniversal(xform, getTime());
00161 }
00162 
00163 
00164 Vec3d toUniversal(const Vec3d& v,
00165                   const Observer& observer,
00166                   const Selection& sel,
00167                   double t,
00168                   astro::CoordinateSystem frame)
00169 {
00170     switch (frame)
00171     {
00172     case astro::ObserverLocal:
00173         {
00174             Quatf q = observer.getOrientation();
00175             Quatd qd(q.w, q.x, q.y, q.z);
00176             return v * qd.toMatrix3();
00177         }                        
00178 
00179         
00180     case astro::Geographic:
00181         if (sel.getType() != Selection::Type_Body)
00182             return v;
00183         else
00184             return v * sel.body()->getGeographicToHeliocentric(t);
00185         
00186     case astro::Equatorial:
00187         if (sel.getType() != Selection::Type_Body)
00188             return v;
00189         else
00190             return v * sel.body()->getLocalToHeliocentric(t);
00191 
00192     case astro::Ecliptical:
00193         // TODO: Multiply this by the planetary system's ecliptic orientation,
00194         // once this field is added.
00195         return v;
00196 
00197     case astro::Universal:
00198         return v;
00199 
00200     default:
00201         // assert(0);
00202         return v;
00203     }
00204 }
00205 
00206 
00207 static Quatf lookAt(Point3f from, Point3f to, Vec3f up)
00208 {
00209     Vec3f n = to - from;
00210     n.normalize();
00211     Vec3f v = n ^ up;
00212     v.normalize();
00213     Vec3f u = v ^ n;
00214     return Quatf(Mat3f(v, u, -n));
00215 }
00216 
00217 
00218 double Observer::getArrivalTime() const
00219 {
00220     if (observerMode != Travelling)
00221         return realTime;
00222     else
00223         return journey.startTime + journey.duration;
00224 }
00225 
00226 
00227 // Tick the simulation by dt seconds
00228 void Observer::update(double dt, double timeScale)
00229 {
00230     realTime += dt;
00231     simTime += (dt / 86400.0) * timeScale;
00232 
00233     if (observerMode == Travelling)
00234     {
00235         float t = (float) clamp((realTime - journey.startTime) / journey.duration);
00236 
00237         Vec3d jv = journey.to - journey.from;
00238         UniversalCoord p;
00239 
00240         // Another interpolation method . . . accelerate exponentially,
00241         // maintain a constant velocity for a period of time, then
00242         // decelerate.  The portion of the trip spent accelerating is
00243         // controlled by the parameter journey.accelTime; a value of 1 means
00244         // that the entire first half of the trip will be spent accelerating
00245         // and there will be no coasting at constant velocity.
00246         {
00247             double u = t < 0.5 ? t * 2 : (1 - t) * 2;
00248             double x;
00249             if (u < journey.accelTime)
00250             {
00251                 x = exp(journey.expFactor * u) - 1.0;
00252             }
00253             else
00254             {
00255                 x = exp(journey.expFactor * journey.accelTime) *
00256                     (journey.expFactor * (u - journey.accelTime) + 1) - 1;
00257             }
00258 
00259             if (journey.traj == Linear)
00260             {
00261                 Vec3d v = jv;
00262                 if (v.length() == 0.0)
00263                 {
00264                     p = journey.from;
00265                 }
00266                 else
00267                 {
00268                     v.normalize();
00269                     if (t < 0.5)
00270                         p = journey.from + v * astro::kilometersToMicroLightYears(x);
00271                     else
00272                         p = journey.to - v * astro::kilometersToMicroLightYears(x);
00273                 }
00274             }
00275             else if (journey.traj == GreatCircle)
00276             {
00277                 Selection centerObj = frame.refObject;
00278                 if (centerObj.body() != NULL)
00279                 {
00280                     Body* body = centerObj.body();
00281                     if (body->getSystem())
00282                     {
00283                         if (body->getSystem()->getPrimaryBody() != NULL)
00284                             centerObj = Selection(body->getSystem()->getPrimaryBody());
00285                         else
00286                             centerObj = Selection(body->getSystem()->getStar());
00287                     }
00288                 }
00289 
00290                 UniversalCoord ufrom  = frame.toUniversal(RigidTransform(journey.from, Quatf(1.0f)), simTime).translation;
00291                 UniversalCoord uto    = frame.toUniversal(RigidTransform(journey.to, Quatf(1.0f)), simTime).translation;
00292                 UniversalCoord origin = centerObj.getPosition(simTime);
00293                 Vec3d v0 = ufrom - origin;
00294                 Vec3d v1 = uto - origin;
00295 
00296                 if (jv.length() == 0.0)
00297                 {
00298                     p = journey.from;
00299                 }
00300                 else
00301                 {
00302                     x = astro::kilometersToMicroLightYears(x / jv.length());
00303                     Vec3d v;
00304 
00305                     if (t < 0.5)
00306                         v = slerp(x, v0, v1);
00307                     else
00308                         v = slerp(x, v1, v0);
00309 
00310                     p = origin + v;
00311                     p = frame.fromUniversal(RigidTransform(p, Quatf(1.0f)), simTime).translation;
00312                 }
00313             }
00314             else if (journey.traj == CircularOrbit)
00315             {
00316                 Selection centerObj = frame.refObject;
00317 
00318                 UniversalCoord ufrom  = frame.toUniversal(RigidTransform(journey.from, Quatf(1.0f)), simTime).translation;
00319                 UniversalCoord uto    = frame.toUniversal(RigidTransform(journey.to, Quatf(1.0f)), simTime).translation;
00320                 UniversalCoord origin = centerObj.getPosition(simTime);
00321                 Vec3d v0 = ufrom - origin;
00322                 Vec3d v1 = uto - origin;
00323 
00324                 if (jv.length() == 0.0)
00325                 {
00326                     p = journey.from;
00327                 }
00328                 else
00329                 {
00330                     //x = astro::kilometersToMicroLightYears(x / jv.length());
00331                     Quatd q0(1.0);
00332                     Quatd q1(journey.rotation1.w, journey.rotation1.x,
00333                              journey.rotation1.y, journey.rotation1.z);
00334                     p = origin + v0 * Quatd::slerp(q0, q1, t).toMatrix3();
00335                                               
00336                     p = frame.fromUniversal(RigidTransform(p, Quatf(1.0f)),
00337                                             simTime).translation;
00338                 }
00339             }
00340         }
00341 
00342         // Spherically interpolate the orientation over the first half
00343         // of the journey.
00344         Quatf orientation;
00345         if (t >= journey.startInterpolation && t < journey.endInterpolation )
00346         {
00347             // Smooth out the interpolation to avoid jarring changes in
00348             // orientation
00349             double v;
00350             if (journey.traj == CircularOrbit)
00351             {
00352                 // In circular orbit mode, interpolation of orientation must
00353                 // match the interpolation of position.
00354                 v = t;
00355             }
00356             else
00357             {
00358                 v = pow(sin((t - journey.startInterpolation) /
00359                             (journey.endInterpolation - journey.startInterpolation) * PI / 2), 2);
00360             }
00361 
00362             // Be careful to choose the shortest path when interpolating
00363             if (norm(journey.initialOrientation - journey.finalOrientation) <
00364                 norm(journey.initialOrientation + journey.finalOrientation))
00365             {
00366                 orientation = Quatf::slerp(journey.initialOrientation,
00367                                            journey.finalOrientation, (float)v);
00368             }
00369             else
00370             {
00371                 orientation = Quatf::slerp(journey.initialOrientation,
00372                                            -journey.finalOrientation,(float)v);
00373             }
00374         }
00375         else if (t < journey.startInterpolation)
00376         {
00377             orientation = journey.initialOrientation;
00378         }
00379         else // t >= endInterpolation
00380         {
00381             orientation = journey.finalOrientation;
00382         }
00383 
00384         situation = RigidTransform(p, orientation);
00385 
00386         // If the journey's complete, reset to manual control
00387         if (t == 1.0f)
00388         {
00389             if (journey.traj != CircularOrbit)
00390                 situation = RigidTransform(journey.to, journey.finalOrientation);
00391             observerMode = Free;
00392             setVelocity(Vec3d(0, 0, 0));
00393 //            targetVelocity = Vec3d(0, 0, 0);
00394         }
00395     }
00396 
00397     if (getVelocity() != targetVelocity)
00398     {
00399         double t = clamp((realTime - beginAccelTime) / VELOCITY_CHANGE_TIME);
00400         setVelocity(getVelocity() * (1.0 - t) + targetVelocity * t);
00401     }
00402 
00403     // Update the position
00404     situation.translation = situation.translation + getVelocity() * dt;
00405 
00406     if (observerMode == Free)
00407     {
00408         // Update the observer's orientation
00409         Vec3f fAV = getAngularVelocity();
00410         Vec3d AV(fAV.x, fAV.y, fAV.z);
00411         Quatd dr = 0.5 * (AV * situation.rotation);
00412         situation.rotation += dt * dr;
00413         situation.rotation.normalize();
00414     }
00415 
00416     if (!trackObject.empty())
00417     {
00418         Vec3f up = Vec3f(0, 1, 0) * getOrientation().toMatrix3();
00419         Vec3d vn = trackObject.getPosition(getTime()) - getPosition();
00420         Point3f to((float) vn.x, (float) vn.y, (float) vn.z);
00421         setOrientation(lookAt(Point3f(0, 0, 0), to, up));
00422     }
00423 }
00424 
00425 
00426 Selection Observer::getTrackedObject() const
00427 {
00428     return trackObject;
00429 }
00430 
00431 
00432 void Observer::setTrackedObject(const Selection& sel)
00433 {
00434     trackObject = sel;
00435 }
00436 
00437 
00438 const string& Observer::getDisplayedSurface() const
00439 {
00440     return displayedSurface;
00441 }
00442 
00443 
00444 void Observer::setDisplayedSurface(const string& surf)
00445 {
00446     displayedSurface = surf;
00447 }
00448 
00449 
00450 uint32 Observer::getLocationFilter() const
00451 {
00452     return locationFilter;
00453 }
00454 
00455 
00456 void Observer::setLocationFilter(uint32 _locationFilter)
00457 {
00458     locationFilter = _locationFilter;
00459 }
00460 
00461 
00462 void Observer::reverseOrientation()
00463 {
00464     Quatf q = getOrientation();
00465     q.yrotate((float) PI);
00466     setOrientation(q);
00467     reverseFlag = !reverseFlag;
00468 }
00469 
00470 
00471 
00472 struct TravelExpFunc : public unary_function<double, double>
00473 {
00474     double dist, s;
00475 
00476     TravelExpFunc(double d, double _s) : dist(d), s(_s) {};
00477 
00478     double operator()(double x) const
00479     {
00480         // return (1.0 / x) * (exp(x / 2.0) - 1.0) - 0.5 - dist / 2.0;
00481         return exp(x * s) * (x * (1 - s) + 1) - 1 - dist;
00482     }
00483 };
00484 
00485 
00486 void Observer::computeGotoParameters(const Selection& destination,
00487                                      JourneyParams& jparams,
00488                                      double gotoTime,
00489                                      double startInter,
00490                                      double endInter,
00491                                      Vec3d offset,
00492                                      astro::CoordinateSystem offsetFrame,
00493                                      Vec3f up,
00494                                      astro::CoordinateSystem upFrame)
00495 {
00496     if (frame.coordSys == astro::PhaseLock)
00497     {
00498         setFrame(FrameOfReference(astro::Ecliptical, destination));
00499     }
00500     else
00501     {
00502         setFrame(FrameOfReference(frame.coordSys, destination));
00503     }
00504 
00505     UniversalCoord targetPosition = destination.getPosition(getTime());
00506     Vec3d v = targetPosition - getPosition();
00507     v.normalize();
00508 
00509     jparams.traj = Linear;
00510     jparams.duration = gotoTime;
00511     jparams.startTime = realTime;
00512 
00513     // Right where we are now . . .
00514     jparams.from = getPosition();
00515 
00516     offset = toUniversal(offset, *this, destination, getTime(), offsetFrame);
00517     jparams.to = targetPosition + offset;
00518 
00519     Vec3d upd(up.x, up.y, up.z);
00520     upd = toUniversal(upd, *this, destination, getTime(), upFrame);
00521     Vec3f upf = Vec3f((float) upd.x, (float) upd.y, (float) upd.z);
00522 
00523     jparams.initialOrientation = getOrientation();
00524     Vec3d vn = targetPosition - jparams.to;
00525     Point3f focus((float) vn.x, (float) vn.y, (float) vn.z);
00526     jparams.finalOrientation = lookAt(Point3f(0, 0, 0), focus, upf);
00527     jparams.startInterpolation = min(startInter, endInter);
00528     jparams.endInterpolation   = max(startInter, endInter);
00529 
00530     jparams.accelTime = 0.5;
00531     double distance = astro::microLightYearsToKilometers(jparams.from.distanceTo(jparams.to)) / 2.0;
00532     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, jparams.accelTime),
00533                                                0.0001, 100.0,
00534                                                1e-10);
00535     jparams.expFactor = sol.first;
00536 
00537     // Convert to frame coordinates
00538     RigidTransform from(jparams.from, jparams.initialOrientation);
00539     from = frame.fromUniversal(from, getTime());
00540     jparams.from = from.translation;
00541     jparams.initialOrientation= Quatf((float) from.rotation.w,
00542                                       (float) from.rotation.x,
00543                                       (float) from.rotation.y,
00544                                       (float) from.rotation.z);
00545     RigidTransform to(jparams.to, jparams.finalOrientation);
00546     to = frame.fromUniversal(to, getTime());
00547     jparams.to = to.translation;
00548     jparams.finalOrientation= Quatf((float) to.rotation.w,
00549                                     (float) to.rotation.x,
00550                                     (float) to.rotation.y,
00551                                     (float) to.rotation.z);
00552 }
00553 
00554 
00555 void Observer::computeGotoParametersGC(const Selection& destination,
00556                                        JourneyParams& jparams,
00557                                        double gotoTime,
00558                                        double startInter,
00559                                        double endInter,
00560                                        Vec3d offset,
00561                                        astro::CoordinateSystem offsetFrame,
00562                                        Vec3f up,
00563                                        astro::CoordinateSystem upFrame,
00564                                        const Selection& centerObj)
00565 {
00566     setFrame(FrameOfReference(frame.coordSys, destination));
00567 
00568     UniversalCoord targetPosition = destination.getPosition(getTime());
00569     Vec3d v = targetPosition - getPosition();
00570     v.normalize();
00571 
00572     jparams.traj = GreatCircle;
00573     jparams.duration = gotoTime;
00574     jparams.startTime = realTime;
00575 
00576     jparams.centerObject = centerObj;
00577 
00578     // Right where we are now . . .
00579     jparams.from = getPosition();
00580 
00581     offset = toUniversal(offset, *this, destination, getTime(), offsetFrame);
00582     jparams.to = targetPosition + offset;
00583 
00584     Vec3d upd(up.x, up.y, up.z);
00585     upd = toUniversal(upd, *this, destination, getTime(), upFrame);
00586     Vec3f upf = Vec3f((float) upd.x, (float) upd.y, (float) upd.z);
00587 
00588     jparams.initialOrientation = getOrientation();
00589     Vec3d vn = targetPosition - jparams.to;
00590     Point3f focus((float) vn.x, (float) vn.y, (float) vn.z);
00591     jparams.finalOrientation = lookAt(Point3f(0, 0, 0), focus, upf);
00592     jparams.startInterpolation = min(startInter, endInter);
00593     jparams.endInterpolation   = max(startInter, endInter);
00594 
00595     jparams.accelTime = 0.5;
00596     double distance = astro::microLightYearsToKilometers(jparams.from.distanceTo(jparams.to)) / 2.0;
00597     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, jparams.accelTime),
00598                                                0.0001, 100.0,
00599                                                1e-10);
00600     jparams.expFactor = sol.first;
00601 
00602     // Convert to frame coordinates
00603     RigidTransform from(jparams.from, jparams.initialOrientation);
00604     from = frame.fromUniversal(from, getTime());
00605     jparams.from = from.translation;
00606     jparams.initialOrientation= Quatf((float) from.rotation.w,
00607                                       (float) from.rotation.x,
00608                                       (float) from.rotation.y,
00609                                       (float) from.rotation.z);
00610     RigidTransform to(jparams.to, jparams.finalOrientation);
00611     to = frame.fromUniversal(to, getTime());
00612     jparams.to = to.translation;
00613     jparams.finalOrientation= Quatf((float) to.rotation.w,
00614                                     (float) to.rotation.x,
00615                                     (float) to.rotation.y,
00616                                     (float) to.rotation.z);
00617 }
00618 
00619 
00620 void Observer::computeCenterParameters(const Selection& destination,
00621                                        JourneyParams& jparams,
00622                                        double centerTime)
00623 {
00624     UniversalCoord targetPosition = destination.getPosition(getTime());
00625 
00626     jparams.duration = centerTime;
00627     jparams.startTime = realTime;
00628     jparams.traj = Linear;
00629 
00630     // Don't move through space, just rotate the camera
00631     jparams.from = getPosition();
00632     jparams.to = jparams.from;
00633 
00634     Vec3f up = Vec3f(0, 1, 0) * getOrientation().toMatrix4();
00635 
00636     jparams.initialOrientation = getOrientation();
00637     Vec3d vn = targetPosition - jparams.to;
00638     Point3f focus((float) vn.x, (float) vn.y, (float) vn.z);
00639     jparams.finalOrientation = lookAt(Point3f(0, 0, 0), focus, up);
00640     jparams.startInterpolation = 0;
00641     jparams.endInterpolation   = 1;
00642 
00643 
00644     jparams.accelTime = 0.5;
00645     jparams.expFactor = 0;
00646 
00647     // Convert to frame coordinates
00648     RigidTransform from(jparams.from, jparams.initialOrientation);
00649     from = frame.fromUniversal(from, getTime());
00650     jparams.from = from.translation;
00651     jparams.initialOrientation= Quatf((float) from.rotation.w,
00652                                       (float) from.rotation.x,
00653                                       (float) from.rotation.y,
00654                                       (float) from.rotation.z);
00655 
00656     RigidTransform to(jparams.to, jparams.finalOrientation);
00657     to = frame.fromUniversal(to, getTime());
00658     jparams.to = to.translation;
00659     jparams.finalOrientation= Quatf((float) to.rotation.w,
00660                                     (float) to.rotation.x,
00661                                     (float) to.rotation.y,
00662                                     (float) to.rotation.z);
00663 }
00664 
00665 
00666 void Observer::computeCenterCOParameters(const Selection& destination,
00667                                          JourneyParams& jparams,
00668                                          double centerTime)
00669 {
00670     jparams.duration = centerTime;
00671     jparams.startTime = realTime;
00672     jparams.traj = CircularOrbit;
00673 
00674     jparams.centerObject = frame.refObject;
00675     jparams.expFactor = 0.5;
00676 
00677     Vec3d v = destination.getPosition(getTime()) - getPosition();
00678     Vec3f wf = Vec3f(0.0, 0.0, -1.0) * getOrientation().toMatrix3();
00679     Vec3d w(wf.x, wf.y, wf.z);
00680     v.normalize();
00681 
00682     Vec3d n = w ^ v;
00683     double nl = n.length();
00684     double angle = 0.0;
00685     if (nl > 0.0)
00686     {
00687         double cosAngle = w * v;
00688         if (cosAngle < 1.0 - 1e-8)
00689         {
00690             if (cosAngle > 1e-8 - 1.0)
00691                 angle = acos(cosAngle);
00692             else
00693                 angle = PI;
00694         }
00695         else
00696         {
00697             angle = 0.0;
00698         }
00699 
00700         n = n / nl;
00701     }
00702     else
00703     {
00704         n = Vec3d(1.0, 0.0, 0.0);
00705     }
00706 
00707     Selection centerObj = frame.refObject;
00708     UniversalCoord centerPos = centerObj.getPosition(getTime());
00709     UniversalCoord targetPosition = destination.getPosition(getTime());
00710 
00711     Quatd qd;
00712     qd.setAxisAngle(n, -angle);
00713     Quatf q((float) qd.w, (float) qd.x, (float) qd.y, (float) qd.z);
00714 
00715     jparams.from = getPosition();
00716     jparams.to = centerPos + ((getPosition() - centerPos) * qd.toMatrix3());
00717     jparams.initialOrientation = getOrientation();
00718     jparams.finalOrientation = getOrientation() * q;
00719     
00720     jparams.startInterpolation = 0.0;
00721     jparams.endInterpolation = 1.0;
00722 
00723     jparams.rotation1 = q;
00724 
00725     // Convert to frame coordinates
00726     RigidTransform from(jparams.from, jparams.initialOrientation);
00727     from = frame.fromUniversal(from, getTime());
00728     jparams.from = from.translation;
00729     jparams.initialOrientation= Quatf((float) from.rotation.w,
00730                                       (float) from.rotation.x,
00731                                       (float) from.rotation.y,
00732                                       (float) from.rotation.z);
00733 
00734     RigidTransform to(jparams.to, jparams.finalOrientation);
00735     to = frame.fromUniversal(to, getTime());
00736     jparams.to = to.translation;
00737     jparams.finalOrientation= Quatf((float) to.rotation.w,
00738                                     (float) to.rotation.x,
00739                                     (float) to.rotation.y,
00740                                     (float) to.rotation.z);
00741 }
00742 
00743 
00744 Observer::ObserverMode Observer::getMode() const
00745 {
00746     return observerMode;
00747 }
00748 
00749 
00750 // Center the selection by moving on a circular orbit arround
00751 // the primary body (refObject).
00752 void Observer::centerSelectionCO(const Selection& selection, double centerTime)
00753 {
00754     if (!selection.empty() && !frame.refObject.empty())
00755     {
00756         computeCenterCOParameters(selection, journey, centerTime);
00757         observerMode = Travelling;
00758     }
00759 }
00760 
00761 
00762 void Observer::setMode(Observer::ObserverMode mode)
00763 {
00764     observerMode = mode;
00765 }
00766 
00767 
00768 void Observer::setFrame(const FrameOfReference& _frame)
00769 {
00770     RigidTransform transform = frame.toUniversal(situation, getTime());
00771     if (observerMode == Travelling)
00772     {
00773         RigidTransform from = frame.toUniversal(RigidTransform(journey.from, journey.initialOrientation), getTime());
00774         RigidTransform to = frame.toUniversal(RigidTransform(journey.to, journey.finalOrientation), getTime());
00775 
00776         frame = _frame;
00777         situation = frame.fromUniversal(transform, getTime());
00778 
00779         from = frame.fromUniversal(from, getTime());
00780         journey.from = from.translation;
00781         journey.initialOrientation = Quatf((float) from.rotation.w,
00782                                            (float) from.rotation.x,
00783                                            (float) from.rotation.y,
00784                                            (float) from.rotation.z);
00785         to = frame.fromUniversal(to, getTime());
00786         journey.to = to.translation;
00787         journey.finalOrientation = Quatf((float) to.rotation.w,
00788                                          (float) to.rotation.x,
00789                                          (float) to.rotation.y,
00790                                          (float) to.rotation.z);
00791     }
00792     else
00793     {
00794         frame = _frame;
00795         situation = frame.fromUniversal(transform, getTime());
00796     }
00797 }
00798 
00799 
00800 FrameOfReference Observer::getFrame() const
00801 {
00802     return frame;
00803 }
00804 
00805 
00806 // Rotate the observer about its center.
00807 void Observer::rotate(Quatf q)
00808 {
00809     Quatd qd(q.w, q.x, q.y, q.z);
00810     situation.rotation = qd * situation.rotation;
00811 }
00812 
00813 
00814 // Orbit around the reference object (if there is one.)  This involves changing
00815 // both the observer's position and orientation.
00816 void Observer::orbit(const Selection& selection, Quatf q)
00817 {
00818     Selection center = frame.refObject;
00819     if (center.empty() && !selection.empty())
00820     {
00821         center = selection;
00822         setFrame(FrameOfReference(frame.coordSys, center));
00823     }
00824 
00825     if (!center.empty())
00826     {
00827         // Get the focus position (center of rotation) in frame
00828         // coordinates; in order to make this function work in all
00829         // frames of reference, it's important to work in frame
00830         // coordinates.
00831         UniversalCoord focusPosition = center.getPosition(getTime());
00832         focusPosition = frame.fromUniversal(RigidTransform(focusPosition), getTime()).translation;
00833 
00834         // v = the vector from the observer's position to the focus
00835         Vec3d v = situation.translation - focusPosition;
00836 
00837         // Get a double precision version of the rotation
00838         Quatd qd(q.w, q.x, q.y, q.z);
00839 
00840         // To give the right feel for rotation, we want to premultiply
00841         // the current orientation by q.  However, because of the order in
00842         // which we apply transformations later on, we can't pre-multiply.
00843         // To get around this, we compute a rotation q2 such
00844         // that q1 * r = r * q2.
00845         Quatd qd2 = ~situation.rotation * qd * situation.rotation;
00846         qd2.normalize();
00847 
00848         // Roundoff errors will accumulate and cause the distance between
00849         // viewer and focus to drift unless we take steps to keep the
00850         // length of v constant.
00851         double distance = v.length();
00852         v = v * qd2.toMatrix3();
00853         v.normalize();
00854         v *= distance;
00855 
00856         situation.rotation = situation.rotation * qd2;
00857         situation.translation = focusPosition + v;
00858     }
00859 }
00860 
00861 
00862 // Exponential camera dolly--move toward or away from the selected object
00863 // at a rate dependent on the observer's distance from the object.
00864 void Observer::changeOrbitDistance(const Selection& selection, float d)
00865 {
00866     Selection center = frame.refObject;
00867     if (center.empty() && !selection.empty())
00868     {
00869         center = selection;
00870         setFrame(FrameOfReference(frame.coordSys, center));
00871     }
00872 
00873     if (!center.empty())
00874     {
00875         UniversalCoord focusPosition = center.getPosition(getTime());
00876         
00877         double size = center.radius();
00878 
00879         // Somewhat arbitrary parameters to chosen to give the camera movement
00880         // a nice feel.  They should probably be function parameters.
00881         double minOrbitDistance = astro::kilometersToMicroLightYears(size);
00882         double naturalOrbitDistance = astro::kilometersToMicroLightYears(4.0 * size);
00883 
00884         // Determine distance and direction to the selected object
00885         Vec3d v = getPosition() - focusPosition;
00886         double currentDistance = v.length();
00887 
00888         // TODO: This is sketchy . . .
00889         if (currentDistance < minOrbitDistance)
00890             minOrbitDistance = currentDistance * 0.5;
00891 
00892         if (currentDistance >= minOrbitDistance && naturalOrbitDistance != 0)
00893         {
00894             double r = (currentDistance - minOrbitDistance) / naturalOrbitDistance;
00895             double newDistance = minOrbitDistance + naturalOrbitDistance * exp(log(r) + d);
00896             v = v * (newDistance / currentDistance);
00897             RigidTransform framePos = frame.fromUniversal(RigidTransform(focusPosition + v),
00898                                                           getTime());
00899             situation.translation = framePos.translation;
00900         }
00901     }
00902 }
00903 
00904 
00905 void Observer::setTargetSpeed(float s)
00906 {
00907     targetSpeed = s;
00908     Vec3f v;
00909     if (reverseFlag)
00910         s = -s;
00911     if (trackObject.empty())
00912     {
00913         trackingOrientation = getOrientation();
00914         // Generate vector for velocity using current orientation
00915         // and specified speed.
00916         v = Vec3f(0, 0, -s) * getOrientation().toMatrix4();
00917     }
00918     else
00919     {
00920         // Use tracking orientation vector to generate target velocity
00921         v = Vec3f(0, 0, -s) * trackingOrientation.toMatrix4();
00922     }
00923 
00924     targetVelocity = Vec3d(v.x, v.y, v.z);
00925     initialVelocity = getVelocity();
00926     beginAccelTime = realTime;
00927 }
00928 
00929 
00930 float Observer::getTargetSpeed()
00931 {
00932     return (float) targetSpeed;
00933 }
00934 
00935 
00936 void Observer::gotoJourney(const JourneyParams& params)
00937 {
00938     journey = params;
00939     double distance = astro::microLightYearsToKilometers(journey.from.distanceTo(journey.to)) / 2.0;
00940     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, journey.accelTime),
00941                                                0.0001, 100.0,
00942                                                1e-10);
00943     journey.expFactor = sol.first;
00944     journey.startTime = realTime;
00945     observerMode = Travelling;
00946 }
00947 
00948 void Observer::gotoSelection(const Selection& selection,
00949                              double gotoTime,
00950                              Vec3f up,
00951                              astro::CoordinateSystem upFrame)
00952 {
00953     gotoSelection(selection, gotoTime, 0.0, 0.5, up, upFrame);
00954 }
00955 
00956 
00957 // Return the preferred distance for viewing an object
00958 static double getPreferredDistance(const Selection& selection)
00959 {
00960     switch (selection.getType())
00961     {
00962     case Selection::Type_Body:
00963         return 5.0 * selection.radius();
00964     case Selection::Type_DeepSky:
00965         return 5.0 * selection.radius();
00966     case Selection::Type_Star:
00967         if (selection.star()->getVisibility())
00968             return 100.0 * selection.radius();
00969         else
00970             return astro::AUtoKilometers(1.0);
00971     case Selection::Type_Location:
00972         {
00973             double maxDist = getPreferredDistance(selection.location()->getParentBody());
00974             return max(min(selection.location()->getSize() * 50.0, maxDist),
00975                        1.0);
00976         }
00977     default:
00978         return 1.0;
00979     }
00980 }
00981 
00982 
00983 // Given an object and its current distance from the camera, determine how
00984 // close we should go on the next goto.
00985 static double getOrbitDistance(const Selection& selection,
00986                                double currentDistance)
00987 {
00988     // If further than 10 times the preferrred distance, goto the
00989     // preferred distance.  If closer, zoom in 10 times closer or to the
00990     // minimum distance.
00991     double maxDist = astro::kilometersToMicroLightYears(getPreferredDistance(selection));
00992     double minDist = astro::kilometersToMicroLightYears(1.01 * selection.radius());
00993     double dist = (currentDistance > maxDist * 10.0) ? maxDist : currentDistance * 0.1;
00994 
00995     return max(dist, minDist);
00996 }
00997 
00998 
00999 void Observer::gotoSelection(const Selection& selection,
01000                              double gotoTime,
01001                              double startInter,
01002                              double endInter,
01003                              Vec3f up,
01004                              astro::CoordinateSystem upFrame)
01005 {
01006     if (!selection.empty())
01007     {
01008         UniversalCoord pos = selection.getPosition(getTime());
01009         Vec3d v = pos - getPosition();
01010         double distance = v.length();
01011 
01012         double orbitDistance = getOrbitDistance(selection, distance);
01013 
01014         computeGotoParameters(selection, journey, gotoTime,
01015                               startInter, endInter,
01016                               v * -(orbitDistance / distance),
01017                               astro::Universal,
01018                               up, upFrame);
01019         observerMode = Travelling;
01020     }
01021 }
01022 
01023 
01024 // Like normal goto, except we'll follow a great circle trajectory.  Useful
01025 // for travelling between surface locations, where we'd rather not go straight
01026 // through the middle of a planet.
01027 void Observer::gotoSelectionGC(const Selection& selection,
01028                                double gotoTime,
01029                                double startInter,
01030                                double endInter,
01031                                Vec3f up,
01032                                astro::CoordinateSystem upFrame)
01033 {
01034     if (!selection.empty())
01035     {
01036         Selection centerObj = selection.parent();
01037 
01038         UniversalCoord pos = selection.getPosition(getTime());
01039         Vec3d v = pos - centerObj.getPosition(getTime());
01040         double distanceToCenter = v.length();
01041         Vec3d viewVec = pos - getPosition();
01042         double orbitDistance = getOrbitDistance(selection,
01043                                                 viewVec.length());
01044         if (selection.location() != NULL)
01045         {
01046             Selection parent = selection.parent();
01047             double maintainDist = astro::kilometersToMicroLightYears(getPreferredDistance(parent));
01048             Vec3d parentPos = parent.getPosition(getTime()) - getPosition();
01049             double parentDist = parentPos.length() -
01050                 astro::kilometersToMicroLightYears(parent.radius());
01051 
01052             if (parentDist <= maintainDist && parentDist > orbitDistance)
01053             {
01054                 orbitDistance = parentDist;
01055             }
01056         }
01057 
01058         computeGotoParametersGC(selection, journey, gotoTime,
01059                                 //startInter, endInter,
01060                                 0.25, 0.75,
01061                                 v * (orbitDistance / distanceToCenter),
01062                                 astro::Universal,
01063                                 up, upFrame,
01064                                 centerObj);
01065         observerMode = Travelling;
01066     }
01067 }
01068 
01069 
01070 void Observer::gotoSelection(const Selection& selection,
01071                              double gotoTime,
01072                              double distance,
01073                              Vec3f up,
01074                              astro::CoordinateSystem upFrame)
01075 {
01076     if (!selection.empty())
01077     {
01078         UniversalCoord pos = selection.getPosition(getTime());
01079         // The destination position lies along the line between the current
01080         // position and the star
01081         Vec3d v = pos - getPosition();
01082         v.normalize();
01083 
01084         computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75,
01085                               v * -distance * 1e6, astro::Universal,
01086                               up, upFrame);
01087         observerMode = Travelling;
01088     }
01089 }
01090 
01091 
01092 void Observer::gotoSelectionGC(const Selection& selection,
01093                                double gotoTime,
01094                                double distance,
01095                                Vec3f up,
01096                                astro::CoordinateSystem upFrame)
01097 {
01098     if (!selection.empty())
01099     {
01100         Selection centerObj = selection.parent();
01101 
01102         UniversalCoord pos = selection.getPosition(getTime());
01103         Vec3d v = pos - centerObj.getPosition(getTime());
01104         v.normalize();
01105 
01106         // The destination position lies along a line extended from the center
01107         // object to the target object
01108         computeGotoParametersGC(selection, journey, gotoTime, 0.25, 0.75,
01109                                 v * -distance * 1e6, astro::Universal,
01110                                 up, upFrame,
01111                                 centerObj);
01112         observerMode = Travelling;
01113     }
01114 }
01115 
01116 
01117 void Observer::gotoSelectionLongLat(const Selection& selection,
01118                                     double gotoTime,
01119                                     double distance,
01120                                     float longitude,
01121                                     float latitude,
01122                                     Vec3f up)
01123 {
01124     if (!selection.empty())
01125     {
01126         double phi = -latitude + PI / 2;
01127         double theta = longitude - PI;
01128         double x = cos(theta) * sin(phi);
01129         double y = cos(phi);
01130         double z = -sin(theta) * sin(phi);
01131         computeGotoParameters(selection, journey, gotoTime, 0.25, 0.75,
01132                               Vec3d(x, y, z) * distance * 1e6, astro::Geographic,
01133                               up, astro::Geographic);
01134         observerMode = Travelling;
01135     }
01136 }
01137 
01138 
01139 void Observer::gotoLocation(const RigidTransform& transform,
01140                             double duration)
01141 {
01142     journey.startTime = realTime;
01143     journey.duration = duration;
01144     
01145     RigidTransform from(getPosition(), getOrientation());
01146     from = frame.fromUniversal(from, getTime());
01147     journey.from = from.translation;
01148     journey.initialOrientation= Quatf((float) from.rotation.w, (float) from.rotation.x,
01149                                       (float) from.rotation.y, (float) from.rotation.z);
01150     
01151     journey.to = transform.translation;
01152     journey.finalOrientation = Quatf((float) transform.rotation.w,
01153                                      (float) transform.rotation.x,
01154                                      (float) transform.rotation.y,
01155                                      (float) transform.rotation.z);
01156     journey.startInterpolation = 0.25f;
01157     journey.endInterpolation   = 0.75f;
01158 
01159 
01160     journey.accelTime = 0.5;
01161     double distance = astro::microLightYearsToKilometers(journey.from.distanceTo(journey.to)) / 2.0;
01162     pair<double, double> sol = solve_bisection(TravelExpFunc(distance, journey.accelTime),
01163                                                0.0001, 100.0,
01164                                                1e-10);
01165     journey.expFactor = sol.first;
01166 
01167     observerMode = Travelling;
01168 }
01169 
01170 
01171 void Observer::getSelectionLongLat(const Selection& selection,
01172                                    double& distance,
01173                                    double& longitude,
01174                                    double& latitude)
01175 {
01176     // Compute distance (km) and lat/long (degrees) of observer with
01177     // respect to currently selected object.
01178     if (!selection.empty())
01179     {
01180         FrameOfReference refFrame(astro::Geographic, selection);
01181         RigidTransform xform = refFrame.fromUniversal(RigidTransform(getPosition(), getOrientation()),
01182                                                       getTime());
01183 
01184         Point3d pos = (Point3d) xform.translation;
01185 
01186         distance = pos.distanceFromOrigin();
01187         longitude = -radToDeg(atan2(-pos.z, -pos.x));
01188         latitude = radToDeg(PI/2 - acos(pos.y / distance));
01189 
01190         // Convert distance from light years to kilometers.
01191         distance = astro::microLightYearsToKilometers(distance);
01192     }
01193 }
01194 
01195 
01196 void Observer::gotoSurface(const Selection& sel, double duration)
01197 {
01198     Vec3d vd = getPosition() - sel.getPosition(getTime());
01199     Vec3f vf((float) vd.x, (float) vd.y, (float) vd.z);
01200     vf.normalize();
01201     Vec3f viewDir = Vec3f(0, 0, -1) * getOrientation().toMatrix3();
01202     Vec3f up = Vec3f(0, 1, 0) * getOrientation().toMatrix3();
01203     Quatf q = getOrientation();
01204     if (vf * viewDir < 0.0f)
01205     {
01206         q = lookAt(Point3f(0, 0, 0), Point3f(0.0f, 0.0f, 0.0f) + up, vf);
01207     }
01208     else
01209     {
01210     }
01211     
01212     FrameOfReference frame(astro::Geographic, sel);
01213     RigidTransform rt = frame.fromUniversal(RigidTransform(getPosition(), q),
01214                                             getTime());
01215 
01216     double height = 1.0001 * astro::kilometersToMicroLightYears(sel.radius());
01217     Vec3d dir = rt.translation - Point3d(0.0, 0.0, 0.0);
01218     dir.normalize();
01219     dir *= height;
01220 
01221     rt.translation = UniversalCoord(dir.x, dir.y, dir.z);
01222     gotoLocation(rt, duration);
01223 };
01224 
01225 
01226 void Observer::cancelMotion()
01227 {
01228     observerMode = Free;
01229 }
01230 
01231 
01232 void Observer::centerSelection(const Selection& selection, double centerTime)
01233 {
01234     if (!selection.empty())
01235     {
01236         computeCenterParameters(selection, journey, centerTime);
01237         observerMode = Travelling;
01238     }
01239 }
01240 
01241 
01242 void Observer::follow(const Selection& selection)
01243 {
01244     if (!selection.empty())
01245     {
01246         setFrame(FrameOfReference(astro::Ecliptical, selection));
01247     }
01248 }
01249 
01250 
01251 void Observer::geosynchronousFollow(const Selection& selection)
01252 {
01253     if (selection.body() != NULL ||
01254         selection.location() != NULL ||
01255         selection.star() != NULL)
01256     {
01257         setFrame(FrameOfReference(astro::Geographic, selection));
01258     }
01259 }
01260 
01261 
01262 void Observer::phaseLock(const Selection& selection)
01263 {
01264     if (frame.refObject.body() != NULL)
01265     {
01266         if (selection == frame.refObject)
01267         {
01268             setFrame(FrameOfReference(astro::PhaseLock, selection,
01269                                       Selection(selection.body()->getSystem()->getStar())));
01270         }
01271         else
01272         {
01273             setFrame(FrameOfReference(astro::PhaseLock, frame.refObject, selection));
01274         }
01275     }
01276     else if (frame.refObject.star() != NULL)
01277     {
01278         if (selection != frame.refObject)
01279         {
01280             setFrame(FrameOfReference(astro::PhaseLock, frame.refObject, selection));
01281         }
01282     }
01283 }
01284 
01285 void Observer::chase(const Selection& selection)
01286 {
01287     if (selection.body() != NULL)
01288     {
01289         setFrame(FrameOfReference(astro::Chase, selection));
01290     }
01291     else if (selection.star() != NULL)
01292     {
01293         setFrame(FrameOfReference(astro::Chase, selection));
01294     }
01295 }
01296 
01297 
01298 float Observer::getFOV() const
01299 {
01300     return fov;
01301 }
01302 
01303 
01304 void Observer::setFOV(float _fov)
01305 {
01306     fov = _fov;
01307 }
01308 
01309 
01310 Vec3f Observer::getPickRay(float x, float y) const
01311 {
01312     float s = 2 * (float) tan(fov / 2.0);
01313 
01314     Vec3f pickDirection = Vec3f(x * s, y * s, -1.0f);
01315     pickDirection.normalize();
01316 
01317     return pickDirection;
01318 }

Generated on Sat Jan 14 22:30:28 2006 for Celestia by  doxygen 1.4.1