diff --git a/apps/OpenSpace/ext/sgct b/apps/OpenSpace/ext/sgct index 606a5ba8d6..4301011f99 160000 --- a/apps/OpenSpace/ext/sgct +++ b/apps/OpenSpace/ext/sgct @@ -1 +1 @@ -Subproject commit 606a5ba8d655e4e0e9a64451cd06a8ea1e10e859 +Subproject commit 4301011f990c76fcb598245b5b6dac7637e692ac diff --git a/ext/ghoul b/ext/ghoul index b9013de0cb..692af309c6 160000 --- a/ext/ghoul +++ b/ext/ghoul @@ -1 +1 @@ -Subproject commit b9013de0cbfceb211f2ee85640f2be98427c980c +Subproject commit 692af309c607d2e6455718738bf2b8cea0ba8455 diff --git a/src/navigation/path.cpp b/src/navigation/path.cpp index f805ae2659..38c5991a3c 100644 --- a/src/navigation/path.cpp +++ b/src/navigation/path.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -235,51 +236,70 @@ glm::dquat Path::lookAtTargetsRotation(double t) const { double Path::speedAlongPath(double traveledDistance) const { const glm::dvec3 endNodePos = _end.node()->worldPosition(); const glm::dvec3 startNodePos = _start.node()->worldPosition(); - const CameraPose prevPose = interpolatedPose(traveledDistance); const double distanceToEndNode = glm::distance(prevPose.position, endNodePos); const double distanceToStartNode = glm::distance(prevPose.position, startNodePos); // Decide which is the closest node - SceneGraphNode* closestNode = _start.node(); - glm::dvec3 closestPos = startNodePos; - - if (distanceToEndNode < distanceToStartNode) { - closestPos = endNodePos; - closestNode = _end.node(); + bool endNodeIsClosest = (distanceToEndNode < distanceToStartNode); + if (_start.nodeIdentifier() == _end.nodeIdentifier()) { + endNodeIsClosest = traveledDistance > (0.5 * pathLength()); } + const SceneGraphNode* closestNode = endNodeIsClosest ? _end.node() : _start.node(); + const glm::dvec3 closestPos = closestNode->worldPosition(); + const glm::dvec3 closestEdgePos = + endNodeIsClosest ? _end.position() : _start.position(); + + // Distance to the surface at the target position + const glm::dvec3 edgePosModelSpace = glm::dvec3( + glm::inverse(closestNode->modelTransform()) * glm::dvec4(closestEdgePos, 1.0) + ); + const SurfacePositionHandle posHandle = + closestNode->calculateSurfacePositionHandle(edgePosModelSpace); + + const glm::dvec3 centerToActualSurfaceModelSpace = + posHandle.centerToReferenceSurface + + posHandle.referenceSurfaceOutDirection * posHandle.heightToSurface; + + const glm::dvec3 centerToActualSurface = + glm::dmat3(closestNode->modelTransform()) * centerToActualSurfaceModelSpace; + + // Compute speed based on distance to object surface const double distanceToClosestNode = glm::distance(closestPos, prevPose.position); - double speed = distanceToClosestNode; + double distanceToSurface = distanceToClosestNode - glm::length(centerToActualSurface); + double speed = distanceToSurface; - // Dampen speed in beginning of path - double startUpDistance = 2.0 * _start.node()->boundingSphere(); - if (startUpDistance < Epsilon) { // zero bounding sphere - startUpDistance = glm::distance(_start.position(), startNodePos); + const double edgePosToSurfaceDistance = + glm::distance(closestEdgePos, closestPos) - glm::length(centerToActualSurface); + + // Dampen speed in beginning and end of path + double dampeningDistance = 2.0 * edgePosToSurfaceDistance; + + // If the path is short, use another dampening approach, based on the boudningsphere + if (pathLength() < dampeningDistance) { + dampeningDistance = closestNode->boundingSphere(); } - if (traveledDistance < startUpDistance) { - speed *= traveledDistance / startUpDistance + 0.01; - } + // Buffer to prevent zero speed + constexpr const double buffer = 0.04; - // Dampen speed in end of path - // Note: this leads to problems when the full length of the path is really big - double closeUpDistance = 2.0 * _end.node()->boundingSphere(); - if (closeUpDistance < Epsilon) { // zero bounding sphere - closeUpDistance = glm::distance(_end.position(), endNodePos); + if (traveledDistance < dampeningDistance) { + speed *= ghoul::sineEaseOut(traveledDistance / dampeningDistance) + buffer; } - - if (traveledDistance > (pathLength() - closeUpDistance)) { - const double remainingDistance = pathLength() - traveledDistance; - speed *= remainingDistance / closeUpDistance + 0.01; + else if (traveledDistance > (pathLength() - dampeningDistance)) { + double remainingDistance = pathLength() - traveledDistance; + // If the path length estimate is not completely correct, the remaining distance + // might be negative. Prevent that from causing problems with the speed. + if (remainingDistance < 0.0) { + remainingDistance = 0.1; + } + speed *= ghoul::sineEaseOut(remainingDistance / dampeningDistance) + buffer; } // TODO: also dampen speed based on curvature, or make sure the curve has a rounder // shape - // TODO: check for when path is shorter than the starUpDistance or closeUpDistance - // variables - return _speedFactorFromDuration * speed; } diff --git a/tests/property/test_property_listproperties.cpp b/tests/property/test_property_listproperties.cpp index a7c0161060..4040a8aec2 100644 --- a/tests/property/test_property_listproperties.cpp +++ b/tests/property/test_property_listproperties.cpp @@ -110,7 +110,7 @@ TEST_CASE("StringListProperty: Get Lua Value", "[stringlistproperty]") { p.getLuaValue(L); REQUIRE(ghoul::lua::luaValueToString(L, 1) == - "{ 1.000000 = a, 2.000000 = b, 3.000000 = c }" + "{ [1] = \"a\", [2] = \"b\", [3] = \"c\" }" ); } @@ -221,7 +221,7 @@ TEST_CASE("IntListProperty: Get Lua Value", "[intlistproperty]") { p.getLuaValue(L); REQUIRE(ghoul::lua::luaValueToString(L, 1) == - "{ 1.000000 = 1.000000, 2.000000 = 2.000000, 3.000000 = 3.000000 }" + "{ [1] = 1.000000, [2] = 2.000000, [3] = 3.000000 }" ); } @@ -332,7 +332,7 @@ TEST_CASE("DoubleListProperty: Get Lua Value", "[doublelistproperty]") { p.getLuaValue(L); REQUIRE(ghoul::lua::luaValueToString(L, 1) == - "{ 1.000000 = 1.000000, 2.000000 = 2.000000, 3.000000 = 3.000000 }" + "{ [1] = 1.000000, [2] = 2.000000, [3] = 3.000000 }" ); } diff --git a/tests/test_lua_createsinglecolorimage.cpp b/tests/test_lua_createsinglecolorimage.cpp index 8c6f8120e6..dbe0e3ca08 100644 --- a/tests/test_lua_createsinglecolorimage.cpp +++ b/tests/test_lua_createsinglecolorimage.cpp @@ -60,7 +60,9 @@ TEST_CASE("CreateSingleColorImage: Faulty 1st input type", "[createsinglecolorim CHECK_THROWS_WITH( openspace::luascriptfunctions::createSingleColorImage(L), - Catch::Matchers::Contains("parameter 1 was not the expected type") + Catch::Matchers::Contains( + "Expected type 'String' for parameter 1 but got wrong type 'Table'" + ) ); } @@ -71,7 +73,9 @@ TEST_CASE("CreateSingleColorImage: Faulty 2nd input type", "[createsinglecolorim CHECK_THROWS_WITH( openspace::luascriptfunctions::createSingleColorImage(L), - Catch::Matchers::Contains("parameter 2 was not the expected type") + Catch::Matchers::Contains( + "Expected type 'Table' for parameter 2 but got wrong type 'String'" + ) ); }