From 1cf7e96de14d046197adaf8527dc6ec8bd5fbc18 Mon Sep 17 00:00:00 2001 From: Chris Laurel Date: Mon, 26 Feb 2001 20:38:08 +0000 Subject: [PATCH] Fixed planet picking to actually compute ray-sphere intersections. --- src/simulation.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/simulation.cpp b/src/simulation.cpp index e06f77c1..78da199a 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -405,13 +405,14 @@ void Simulation::setSelection(const Selection& sel) struct PlanetPickInfo { float cosClosestAngle; + double closestDistance; Body* closestBody; Vec3d direction; Point3d origin; double jd; }; -bool PlanetPickTraversal(Body* body, void* info) +bool ApproxPlanetPickTraversal(Body* body, void* info) { PlanetPickInfo* pickInfo = (PlanetPickInfo*) info; @@ -429,6 +430,32 @@ bool PlanetPickTraversal(Body* body, void* info) } +// Perform an intersection test between the pick ray and a body +bool ExactPlanetPickTraversal(Body* body, void* info) +{ + PlanetPickInfo* pickInfo = (PlanetPickInfo*) info; + + Point3d bpos = body->getHeliocentricPosition(pickInfo->jd); + Vec3d bodyDir = bpos - pickInfo->origin; + + // This intersection test naively assumes that the body is spherical. + double v = bodyDir * pickInfo->direction; + double disc = square(body->getRadius()) - ((bodyDir * bodyDir) - square(v)); + + if (disc > 0.0) + { + double distance = v - sqrt(disc); + if (distance > 0.0 && distance < pickInfo->closestDistance) + { + pickInfo->closestDistance = distance; + pickInfo->closestBody = body; + } + } + + return true; +} + + Selection Simulation::pickPlanet(Observer& observer, Star& sun, SolarSystem& solarSystem, @@ -444,10 +471,28 @@ Selection Simulation::pickPlanet(Observer& observer, pickInfo.origin = astro::heliocentricPosition(observer.getPosition(), sun.getPosition()); pickInfo.cosClosestAngle = -1.0f; + pickInfo.closestDistance = 1.0e50; pickInfo.closestBody = NULL; pickInfo.jd = simTime / 86400.0f; - solarSystem.getPlanets()->traverse(PlanetPickTraversal, (void*) &pickInfo); + // First see if there's a planet that the pick ray intersects. + // Select the closest planet intersected. + solarSystem.getPlanets()->traverse(ExactPlanetPickTraversal, + (void*) &pickInfo); + if (pickInfo.closestBody != NULL) + { + selection = Selection(pickInfo.closestBody); + return selection; + } + + // If no planet was intersected by the pick ray, choose the planet + // with the smallest angular separation from the pick ray. Very distant + // planets are likley to fail the intersection test even if the user + // clicks on a pixel where the planet's disc has been rendered--in order + // to make distant planets visible on the screen at all, their apparent size + // has to be greater than their actual disc size. + solarSystem.getPlanets()->traverse(ApproxPlanetPickTraversal, + (void*) &pickInfo); if (pickInfo.cosClosestAngle > cos(degToRad(0.5f))) selection = Selection(pickInfo.closestBody); else