Skip to content

Commit 35ac17e

Browse files
committed
Make right drag follow mouse
1 parent 55abf77 commit 35ac17e

File tree

6 files changed

+103
-14
lines changed

6 files changed

+103
-14
lines changed

src/celengine/observer.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
// of the License, or (at your option) any later version.
1010

1111
#include <numeric>
12+
#include <optional>
1213
#include <celmath/geomutil.h>
14+
#include <celmath/intersect.h>
1315
#include <celmath/mathlib.h>
1416
#include <celmath/solve.h>
1517
#include "body.h"
@@ -29,7 +31,10 @@ namespace math = celestia::math;
2931

3032
#define VELOCITY_CHANGE_TIME 0.25f
3133

32-
static Vector3d slerp(double t, const Vector3d& v0, const Vector3d& v1)
34+
namespace
35+
{
36+
37+
Vector3d slerp(double t, const Vector3d& v0, const Vector3d& v1)
3338
{
3439
double r0 = v0.norm();
3540
double r1 = v1.norm();
@@ -46,6 +51,19 @@ static Vector3d slerp(double t, const Vector3d& v0, const Vector3d& v1)
4651
return (cos(theta * t) * u + sin(theta * t) * v) * math::lerp(t, r0, r1);
4752
}
4853

54+
std::optional<Eigen::Vector3d> getNearIntersectionPoint(const Eigen::ParametrizedLine<double, 3>& ray,
55+
const math::Sphered& sphere)
56+
{
57+
double distance;
58+
bool intersects = math::testIntersection(ray, sphere, distance);
59+
if (intersects)
60+
return ray.origin() + distance * ray.direction();
61+
else
62+
return std::nullopt;
63+
}
64+
65+
}
66+
4967

5068
/*! Notes on the Observer class
5169
* The values position and orientation are in observer's reference frame. positionUniv
@@ -909,6 +927,50 @@ void Observer::orbit(const Selection& selection, const Quaternionf& q)
909927
}
910928
}
911929

930+
/*! Orbit around the reference object (if there is one.) rotating the object with intersection point from
931+
* a direction to another. If there is no intersection point, return false.
932+
*/
933+
bool Observer::orbit(const Selection& selection, const Eigen::Vector3f &from, const Eigen::Vector3f &to)
934+
{
935+
Selection center = frame->getRefObject();
936+
if (center.empty())
937+
{
938+
if (selection.empty())
939+
return false;
940+
center = selection;
941+
}
942+
943+
double radius = center.radius();
944+
if (radius <= 0.0)
945+
return false;
946+
947+
// Get the focus position (center of rotation) in frame
948+
// coordinates; in order to make this function work in all
949+
// frames of reference, it's important to work in frame
950+
// coordinates.
951+
UniversalCoord focusPosition = center.getPosition(getTime());
952+
focusPosition = frame->convertFromUniversal(focusPosition, getTime());
953+
954+
Eigen::Vector3d objectCenter = focusPosition.offsetFromKm(position);
955+
956+
// Get the rays adjusted to orientation
957+
Eigen::Vector3d transformedFrom = getOrientation().conjugate() * from.cast<double>();
958+
Eigen::Vector3d transformedTo = getOrientation().conjugate() * to.cast<double>();
959+
960+
// Find intersections for the rays, if no intersection, return false
961+
math::Sphered sphere { objectCenter, radius };
962+
auto orbitStartPosition = getNearIntersectionPoint(Eigen::ParametrizedLine<double, 3>(Eigen::Vector3d::Zero(), transformedFrom), sphere);
963+
if (!orbitStartPosition.has_value())
964+
return false;
965+
966+
auto orbitEndPosition = getNearIntersectionPoint(Eigen::ParametrizedLine<double, 3>(Eigen::Vector3d::Zero(), transformedTo), sphere);
967+
if (!orbitEndPosition.has_value())
968+
return false;
969+
970+
orbit(selection, Eigen::Quaterniond::FromTwoVectors(transformedOrientation * (orbitStartPosition.value() - objectCenter), transformedOrientation * (orbitEndPosition.value() - objectCenter)).cast<float>());
971+
return true;
972+
}
973+
912974

913975
/*! Exponential camera dolly--move toward or away from the selected object
914976
* at a rate dependent on the observer's distance from the object.

src/celengine/observer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ class Observer
148148
void update(double dt, double timeScale);
149149

150150
void orbit(const Selection&, const Eigen::Quaternionf &q);
151+
bool orbit(const Selection&, const Eigen::Vector3f &from, const Eigen::Vector3f &to);
151152
void rotate(const Eigen::Quaternionf &q);
152153
void changeOrbitDistance(const Selection&, float d);
153154
void setTargetSpeed(float s);

src/celengine/simulation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ void Simulation::orbit(const Eigen::Quaternionf& q)
258258
activeObserver->orbit(selection, q);
259259
}
260260

261+
// Orbit around the selection (if there is one.) This involves changing
262+
// both the observer's position and orientation.
263+
bool Simulation::orbit(const Eigen::Vector3f& from, const Eigen::Vector3f& to)
264+
{
265+
return activeObserver->orbit(selection, from, to);
266+
}
261267

262268
// Exponential camera dolly--move toward or away from the selected object
263269
// at a rate dependent on the observer's distance from the object.

src/celengine/simulation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Simulation
5050

5151
Universe* getUniverse() const;
5252

53+
bool orbit(const Eigen::Vector3f& from, const Eigen::Vector3f& to);
5354
void orbit(const Eigen::Quaternionf& q);
5455
void rotate(const Eigen::Quaternionf& q);
5556
void changeOrbitDistance(float d);

src/celestia/celestiacore.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ void CelestiaCore::mouseButtonDown(float x, float y, int button)
412412
newLocation.y = y;
413413
dragLocation = newLocation;
414414

415+
dragStartFromSurface = std::nullopt;
416+
415417
#ifdef CELX
416418
if (m_script != nullptr)
417419
{
@@ -435,6 +437,7 @@ void CelestiaCore::mouseButtonDown(float x, float y, int button)
435437
void CelestiaCore::mouseButtonUp(float x, float y, int button)
436438
{
437439
dragLocation = std::nullopt;
440+
dragStartFromSurface = std::nullopt;
438441

439442
// Four pixel tolerance for picking
440443
float obsPickTolerance = sim->getActiveObserver()->getFOV() / static_cast<float>(metrics.height) * this->pickTolerance;
@@ -656,21 +659,36 @@ void CelestiaCore::mouseMove(float dx, float dy, int modifiers)
656659
flash(fmt::sprintf(_("Magnitude limit: %.2f"), sim->getFaintestVisible()));
657660
}
658661
}
659-
else if ((modifiers & RightButton) != 0)
660-
{
661-
// Adjust the rotation rate based on the distance from the reference object.
662-
float coarseness = ComputeRotationCoarseness(*sim);
663-
664-
Quaternionf q = math::XRotation(dy / static_cast<float>(metrics.height) * coarseness) *
665-
math::YRotation(dx / static_cast<float>(metrics.width) * coarseness);
666-
sim->orbit(q);
667-
}
668662
else if (oldLocation.has_value() && dragLocation.has_value())
669663
{
670664
auto view = viewManager->activeView();
671665
auto oldPickRay = getPickRay(oldLocation.value().x, oldLocation.value().y, view);
672666
auto newPickRay = getPickRay(dragLocation.value().x, dragLocation.value().y, view);
673-
sim->rotate(Eigen::Quaternionf::FromTwoVectors(oldPickRay, newPickRay));
667+
if ((modifiers & RightButton) != 0)
668+
{
669+
if (bool isDragStartDetermined = dragStartFromSurface.has_value(); !isDragStartDetermined || dragStartFromSurface.value())
670+
{
671+
// The drag started from the surface or we are not sure yet, try to perform a surface drag
672+
bool surfaceDrag = sim->orbit(oldPickRay, newPickRay);
673+
674+
if (!isDragStartDetermined)
675+
dragStartFromSurface = surfaceDrag;
676+
}
677+
678+
if (!dragStartFromSurface.value())
679+
{
680+
// Adjust the rotation rate based on the distance from the reference object.
681+
float coarseness = ComputeRotationCoarseness(*sim);
682+
683+
Quaternionf q = math::XRotation(dy / static_cast<float>(metrics.height) * coarseness) *
684+
math::YRotation(dx / static_cast<float>(metrics.width) * coarseness);
685+
sim->orbit(q);
686+
}
687+
}
688+
else
689+
{
690+
sim->rotate(Eigen::Quaternionf::FromTwoVectors(oldPickRay, newPickRay));
691+
}
674692
}
675693

676694
mouseMotion += abs(dy) + abs(dx);
@@ -2220,8 +2238,8 @@ Eigen::Vector3f CelestiaCore::getPickRay(float x, float y, const celestia::View
22202238
float pickY;
22212239
float aspectRatio = static_cast<float>(metrics.width) / static_cast<float>(metrics.height);
22222240
view->mapWindowToView(x / static_cast<float>(metrics.width),
2223-
y / static_cast<float>(metrics.height),
2224-
pickX, pickY);
2241+
y / static_cast<float>(metrics.height),
2242+
pickX, pickY);
22252243
pickX *= aspectRatio;
22262244
if (isViewportEffectUsed)
22272245
viewportEffect->distortXY(pickX, pickY);

src/celestia/celestiacore.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,8 @@ class CelestiaCore // : public Watchable<CelestiaCore>
500500
float x;
501501
float y;
502502
};
503-
std::optional<MouseLocation> dragLocation{ std::nullopt };
503+
std::optional<MouseLocation> dragLocation { std::nullopt };
504+
std::optional<bool> dragStartFromSurface { std::nullopt };
504505

505506
std::unique_ptr<ViewportEffect> viewportEffect { nullptr };
506507
bool isViewportEffectUsed { false };

0 commit comments

Comments
 (0)