@@ -408,8 +408,9 @@ createFrame(ObserverFrame::CoordinateSystem _coordSys,
408408 * updates due to an active goto operation.
409409 */
410410
411- Observer::Observer () :
412- frame(std::make_shared<ObserverFrame>())
411+ Observer::Observer (const std::shared_ptr<celestia::engine::ObserverSettings>& settings) :
412+ frame(std::make_shared<ObserverFrame>()),
413+ settings(settings)
413414{
414415 updateUniversal ();
415416}
@@ -438,6 +439,7 @@ Observer::Observer(const Observer& o) :
438439 zoom(o.zoom),
439440 alternateZoom(o.alternateZoom),
440441 reverseFlag(o.reverseFlag),
442+ settings(o.settings),
441443 locationFilter(o.locationFilter),
442444 displayedSurface(o.displayedSurface)
443445{
@@ -470,6 +472,7 @@ Observer& Observer::operator=(const Observer& o)
470472 zoom = o.zoom ;
471473 alternateZoom = o.alternateZoom ;
472474 reverseFlag = o.reverseFlag ;
475+ settings = o.settings ;
473476 locationFilter = o.locationFilter ;
474477 displayedSurface = o.displayedSurface ;
475478
@@ -1505,26 +1508,53 @@ Observer::getSelectionLongLat(const Selection& selection,
15051508void
15061509Observer::gotoSurface (const Selection& sel, double duration)
15071510{
1508- Eigen::Vector3d v = getPosition ().offsetFromKm (sel.getPosition (getTime ()));
1509- v.normalize ();
1510-
1511- Eigen::Vector3d viewDir = originalOrientationUniv.conjugate () * -Eigen::Vector3d::UnitZ ();
1512- Eigen::Vector3d up = originalOrientationUniv.conjugate () * Eigen::Vector3d::UnitY ();
1513- Eigen::Quaterniond q = originalOrientationUniv;
1514- if (v.dot (viewDir) < 0.0 )
1515- {
1516- q = math::LookAt<double >(Eigen::Vector3d::Zero (), up, v);
1517- }
1518-
15191511 ObserverFrame selFrame (ObserverFrame::CoordinateSystem::BodyFixed, sel);
15201512 UniversalCoord bfPos = selFrame.convertFromUniversal (positionUniv, getTime ());
1521- q = selFrame.convertFromUniversal (q, getTime ());
15221513
1514+ // Calculate the surface normal at the landing point (radial direction from center)
1515+ Eigen::Vector3d surfaceNormal = bfPos.offsetFromKm (UniversalCoord::Zero ()).normalized ();
1516+
1517+ // Position the observer just above the surface
15231518 double height = 1.0001 * sel.radius ();
1524- Eigen::Vector3d dir = bfPos.offsetFromKm (UniversalCoord::Zero ()).normalized () * height;
1525- UniversalCoord nearSurfacePoint = UniversalCoord::Zero ().offsetKm (dir);
1519+ UniversalCoord nearSurfacePoint = UniversalCoord::Zero ().offsetKm (surfaceNormal * height);
1520+
1521+ Eigen::Quaterniond toOrientation;
1522+ if (celestia::util::is_set (settings->flags , celestia::engine::ObserverFlags::AlignCameraToSurfaceOnLand))
1523+ {
1524+ // Get the current orientation in the body-fixed frame
1525+ Eigen::Quaterniond currentOrientation = selFrame.convertFromUniversal (originalOrientationUniv, getTime ());
1526+
1527+ // Use the window's up direction (UnitY) as the forward direction
1528+ Eigen::Vector3d windowUp = currentOrientation.conjugate () * Eigen::Vector3d::UnitY ();
1529+
1530+ // Project the window's up direction onto the surface plane to get the tangent direction
1531+ Eigen::Vector3d tangentDirection = windowUp - surfaceNormal * surfaceNormal.dot (windowUp);
15261532
1527- gotoLocation (nearSurfacePoint, q, duration);
1533+ // Fallback if the projection is too small. In this case the planet is
1534+ // below or above us (likely not within viewport) so we can just use an
1535+ // arbitrary orientation.
1536+ if (tangentDirection.norm () < 0.1 )
1537+ {
1538+ Eigen::Vector3d reference = (std::abs (surfaceNormal.dot (Eigen::Vector3d::UnitY ())) < 0.9 ) ? Eigen::Vector3d::UnitY () : Eigen::Vector3d::UnitX ();
1539+ tangentDirection = surfaceNormal.cross (reference);
1540+ }
1541+
1542+ tangentDirection.normalize ();
1543+ toOrientation = math::LookAt<double >(Eigen::Vector3d::Zero (), tangentDirection, surfaceNormal);
1544+ }
1545+ else
1546+ {
1547+ Eigen::Vector3d v = getPosition ().offsetFromKm (sel.getPosition (getTime ()));
1548+ Eigen::Vector3d viewDir = originalOrientationUniv.conjugate () * -Eigen::Vector3d::UnitZ ();
1549+ Eigen::Vector3d up = originalOrientationUniv.conjugate () * Eigen::Vector3d::UnitY ();
1550+ toOrientation = originalOrientationUniv;
1551+ if (v.dot (viewDir) < 0.0 )
1552+ {
1553+ toOrientation = math::LookAt<double >(Eigen::Vector3d::Zero (), up, v);
1554+ }
1555+ toOrientation = selFrame.convertFromUniversal (toOrientation, getTime ());
1556+ }
1557+ gotoLocation (nearSurfacePoint, toOrientation, duration);
15281558}
15291559
15301560void
0 commit comments