diff --git a/modules/touch/ext/levmarq.cpp b/modules/touch/ext/levmarq.cpp index a179fb9ec5..b0add0228a 100644 --- a/modules/touch/ext/levmarq.cpp +++ b/modules/touch/ext/levmarq.cpp @@ -30,11 +30,11 @@ OTHER DEALINGS IN THE SOFTWARE. // set parameters required by levmarq() to default values void levmarq_init(LMstat *lmstat) { - lmstat->verbose = 1; + lmstat->verbose = 0; lmstat->max_it = 5000; lmstat->init_lambda = 1e-6; - lmstat->up_factor = 10; - lmstat->down_factor = 10; + lmstat->up_factor = 5; + lmstat->down_factor = 5; lmstat->target_derr = 1e-12; } diff --git a/modules/touch/include/TouchInteraction.h b/modules/touch/include/TouchInteraction.h index c81bbcdfdb..d1a0edf507 100644 --- a/modules/touch/include/TouchInteraction.h +++ b/modules/touch/include/TouchInteraction.h @@ -101,6 +101,7 @@ class TouchInteraction : public properties::PropertyOwner ~TouchInteraction(); void update(const std::vector& list, std::vector& lastProcessed); + void manipulate(const std::vector& list); void trace(const std::vector& list); void interpret(const std::vector& list, const std::vector& lastProcessed); void accelerate(const std::vector& list, const std::vector& lastProcessed); diff --git a/modules/touch/src/TouchInteraction.cpp b/modules/touch/src/TouchInteraction.cpp index 1198f3e62a..bba7227341 100644 --- a/modules/touch/src/TouchInteraction.cpp +++ b/modules/touch/src/TouchInteraction.cpp @@ -82,151 +82,8 @@ TouchInteraction::TouchInteraction() TouchInteraction::~TouchInteraction() { } void TouchInteraction::update(const std::vector& list, std::vector& lastProcessed) { - if (_directTouchMode && _selected.size() > 0 && list.size() == _selected.size()) { // code below should just be a function call - - // Returns the screen point s(xi,par) dependant the transform M(par) and object point xi - auto distToMinimize = [](double* par, int x, void* fdata) { - FunctionData* ptr = reinterpret_cast(fdata); - - // Apply transform to camera and find the new screen point of the updated camera state - double q[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; // { vec2 globalRot, zoom, roll, vec2 localRot } - for (int i = 0; i < ptr->nDOF; ++i) - q[i] = par[i]; - - using namespace glm; - // Create variables from current state - dvec3 camPos = ptr->camera->positionVec3(); - dvec3 centerPos = ptr->node->worldPosition(); - - dvec3 directionToCenter = normalize(centerPos - camPos); - dvec3 centerToCamera = camPos - centerPos; - dvec3 lookUp = ptr->camera->lookUpVectorWorldSpace(); - dvec3 camDirection = ptr->camera->viewDirectionWorldSpace(); - - // Make a representation of the rotation quaternion with local and global rotations - dmat4 lookAtMat = lookAt( - dvec3(0, 0, 0), - directionToCenter, - normalize(camDirection + lookUp)); // To avoid problem with lookup in up direction - dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat))); - dquat localCamRot = inverse(globalCamRot) * ptr->camera->rotationQuaternion(); - - { // Roll - dquat camRollRot = angleAxis(q[3], dvec3(0.0, 0.0, 1.0)); - localCamRot = localCamRot * camRollRot; - } - { // Panning (local rotation) - dvec3 eulerAngles(q[5], q[4], 0); - dquat rotationDiff = dquat(eulerAngles); - localCamRot = localCamRot * rotationDiff; - } - { // Orbit (global rotation) - dvec3 eulerAngles(q[1], q[0], 0); - dquat rotationDiffCamSpace = dquat(eulerAngles); - - dquat rotationDiffWorldSpace = globalCamRot * rotationDiffCamSpace * inverse(globalCamRot); - dvec3 rotationDiffVec3 = centerToCamera * rotationDiffWorldSpace - centerToCamera; - camPos += rotationDiffVec3; - - dvec3 centerToCamera = camPos - centerPos; - directionToCenter = normalize(-centerToCamera); - dvec3 lookUpWhenFacingCenter = globalCamRot * dvec3(ptr->camera->lookUpVectorCameraSpace()); - dmat4 lookAtMat = lookAt( - dvec3(0, 0, 0), - directionToCenter, - lookUpWhenFacingCenter); - globalCamRot = normalize(quat_cast(inverse(lookAtMat))); - } - { // Zooming - camPos += directionToCenter * q[2]; - } - - // Update the camera state - Camera cam = *(ptr->camera); - cam.setPositionVec3(camPos); - cam.setRotation(globalCamRot * localCamRot); - - // we now have a new position and orientation of camera, project surfacePoint to the new screen to get distance to minimize - glm::dvec2 newScreenPoint = ptr->castToNDC(ptr->selectedPoints.at(x), cam, ptr->node, ptr->aspectRatio); - - return glm::length(ptr->screenPoints.at(x) - newScreenPoint); - }; - // Gradient (finite derivative) of distToMinimize w.r.t par - auto gradient = [](double* g, double* par, int x, void* fdata) { - FunctionData* ptr = reinterpret_cast(fdata); - double f0 = ptr->distToMinimize(par, x, fdata); - double f1, der, h; - double* dPar = new double[ptr->nDOF]; - - for (int i = 0; i < ptr->nDOF; ++i) { - for (int j = 0; j < ptr->nDOF; ++j) { - dPar[j] = par[j]; - } - h = (i == 2) ? 1e-4: 1e-10; - dPar[i] += h; - f1 = ptr->distToMinimize(dPar, x, fdata); - der = (f1 - f0) / h; - g[i] = (i > 1 && i < 4) ? der : der / abs(der); - } - delete[] dPar; - }; - - SceneGraphNode* node = _selected.at(0).node; - auto castToNDC = [](glm::dvec3 vec, Camera& camera, SceneGraphNode* node, double aspectRatio) { - glm::dvec3 backToScreenSpace = glm::inverse(camera.rotationQuaternion()) - * glm::normalize(((node->rotationMatrix() * vec) + node->worldPosition() - camera.positionVec3())); - backToScreenSpace *= (-3.2596558 / backToScreenSpace.z); - backToScreenSpace.x /= aspectRatio; - - return glm::dvec2(backToScreenSpace); - }; - - - const int nFingers = list.size(); - int nDOF = std::min(nFingers * 2, 6); - double* par = new double[nDOF]; - for (int i = 0; i < nDOF; ++i) { // initial values of q or 0.0? (ie current model or no rotation/translation) - par[i] = 0.0; - } - std::vector selectedPoints; - std::vector screenPoints; - for (const SelectedBody& sb : _selected) { - selectedPoints.push_back(sb.coordinates); - - std::vector::const_iterator c = find_if(list.begin(), list.end(), [&sb](const TuioCursor& c) { return c.getSessionID() == sb.id; }); - double xCo = 2 * (c->getX() - 0.5); - double yCo = -2 * (c->getY() - 0.5); // normalized -1 to 1 coordinates on screen - screenPoints.push_back(glm::dvec2(xCo, yCo)); - } - glm::dvec2 res = OsEng.windowWrapper().currentWindowResolution(); - FunctionData fData = { selectedPoints, screenPoints, nDOF, castToNDC, distToMinimize, _camera, node, res.x / res.y }; - void* dataPtr = reinterpret_cast(&fData); - - levmarq_init(&_lmstat); - bool success = levmarq(nDOF, par, nFingers, NULL, distToMinimize, gradient, dataPtr, &_lmstat); // finds best transform values and stores them in par - - if (success) { // if good values were found set new camera state - _vel.globalRot = glm::dvec2(par[0], par[1]); - if (nDOF > 2) { - _vel.zoom = par[2]; - _vel.localRoll = par[3]; - if (nDOF > 4) { - _vel.localRot = glm::dvec2(par[4], par[5]); - } - } - step(1); - } - - - // debugging - /*std::ostringstream os; - for (int i = 0; i < nDOF; ++i) { - os << par[i] << ", "; - } - std::cout << "Levmarq success after " << _lmstat.final_it << " iterations. Values: " << os.str() << "\n";*/ - - // cleanup - delete[] par; + if (_directTouchMode && _selected.size() > 0 && list.size() == _selected.size()) { + manipulate(list); } trace(list); if (!_directTouchMode) { @@ -242,6 +99,156 @@ void TouchInteraction::update(const std::vector& list, std::vector

& list) { + // Returns the screen point s(xi,par) dependant the transform M(par) and object point xi + auto distToMinimize = [](double* par, int x, void* fdata) { + FunctionData* ptr = reinterpret_cast(fdata); + + // Apply transform to camera and find the new screen point of the updated camera state + double q[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; // { vec2 globalRot, zoom, roll, vec2 localRot } + for (int i = 0; i < ptr->nDOF; ++i) { + q[i] = par[i]; + } + + using namespace glm; + // Create variables from current state + dvec3 camPos = ptr->camera->positionVec3(); + dvec3 centerPos = ptr->node->worldPosition(); + + dvec3 directionToCenter = normalize(centerPos - camPos); + dvec3 centerToCamera = camPos - centerPos; + dvec3 lookUp = ptr->camera->lookUpVectorWorldSpace(); + dvec3 camDirection = ptr->camera->viewDirectionWorldSpace(); + + // Make a representation of the rotation quaternion with local and global rotations + dmat4 lookAtMat = lookAt( + dvec3(0, 0, 0), + directionToCenter, + normalize(camDirection + lookUp)); // To avoid problem with lookup in up direction + dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat))); + dquat localCamRot = inverse(globalCamRot) * ptr->camera->rotationQuaternion(); + + { // Roll + dquat camRollRot = angleAxis(q[3], dvec3(0.0, 0.0, 1.0)); + localCamRot = localCamRot * camRollRot; + } + { // Panning (local rotation) + dvec3 eulerAngles(q[5], q[4], 0); + dquat rotationDiff = dquat(eulerAngles); + localCamRot = localCamRot * rotationDiff; + } + { // Orbit (global rotation) + dvec3 eulerAngles(q[1], q[0], 0); + dquat rotationDiffCamSpace = dquat(eulerAngles); + + dquat rotationDiffWorldSpace = globalCamRot * rotationDiffCamSpace * inverse(globalCamRot); + dvec3 rotationDiffVec3 = centerToCamera * rotationDiffWorldSpace - centerToCamera; + camPos += rotationDiffVec3; + + dvec3 centerToCamera = camPos - centerPos; + directionToCenter = normalize(-centerToCamera); + dvec3 lookUpWhenFacingCenter = globalCamRot * dvec3(ptr->camera->lookUpVectorCameraSpace()); + dmat4 lookAtMat = lookAt( + dvec3(0, 0, 0), + directionToCenter, + lookUpWhenFacingCenter); + globalCamRot = normalize(quat_cast(inverse(lookAtMat))); + } + { // Zooming + camPos += directionToCenter * q[2]; + } + + // Update the camera state + Camera cam = *(ptr->camera); + cam.setPositionVec3(camPos); + cam.setRotation(globalCamRot * localCamRot); + + // we now have a new position and orientation of camera, project surfacePoint to the new screen to get distance to minimize + glm::dvec2 newScreenPoint = ptr->castToNDC(ptr->selectedPoints.at(x), cam, ptr->node, ptr->aspectRatio); + + return glm::length(ptr->screenPoints.at(x) - newScreenPoint); + }; + // Gradient (finite derivative) of distToMinimize w.r.t par + auto gradient = [](double* g, double* par, int x, void* fdata) { + FunctionData* ptr = reinterpret_cast(fdata); + double f0 = ptr->distToMinimize(par, x, fdata); + double f1, der, minStep = 1e-11; + glm::dvec3 camPos = ptr->camera->positionVec3(); + glm::dvec3 selectedPoint = (ptr->node->rotationMatrix() * ptr->selectedPoints.at(x)) + ptr->node->worldPosition(); + double h = minStep * glm::distance(camPos, selectedPoint); + double* dPar = new double[ptr->nDOF]; + + for (int i = 0; i < ptr->nDOF; ++i) { + for (int j = 0; j < ptr->nDOF; ++j) { + dPar[j] = par[j]; + } + h = (i == 2) ? 1e-4 : h; // the 'zoom'-DOF is so big a smaller step creates NAN + dPar[i] += h; + f1 = ptr->distToMinimize(dPar, x, fdata); + der = (f1 - f0) / h; + g[i] = (i > 1 && i < 4) ? der : der / abs(der); + } + delete[] dPar; + }; + + SceneGraphNode* node = _selected.at(0).node; + auto castToNDC = [](glm::dvec3 vec, Camera& camera, SceneGraphNode* node, double aspectRatio) { + glm::dvec3 backToScreenSpace = glm::inverse(camera.rotationQuaternion()) + * glm::normalize(((node->rotationMatrix() * vec) + node->worldPosition() - camera.positionVec3())); + backToScreenSpace *= (-3.2596558 / backToScreenSpace.z); + backToScreenSpace.x /= aspectRatio; + + return glm::dvec2(backToScreenSpace); + }; + + const int nFingers = list.size(); + int nDOF = std::min(nFingers * 2, 6); + double* par = new double[nDOF]; + for (int i = 0; i < nDOF; ++i) { // initial values of q or 0.0? (ie current model or no rotation/translation) + par[i] = 0.0; + } + std::vector selectedPoints; + std::vector screenPoints; + for (const SelectedBody& sb : _selected) { + selectedPoints.push_back(sb.coordinates); + + std::vector::const_iterator c = find_if(list.begin(), list.end(), [&sb](const TuioCursor& c) { return c.getSessionID() == sb.id; }); + double xCo = 2 * (c->getX() - 0.5); + double yCo = -2 * (c->getY() - 0.5); // normalized -1 to 1 coordinates on screen + screenPoints.push_back(glm::dvec2(xCo, yCo)); + } + glm::dvec2 res = OsEng.windowWrapper().currentWindowResolution(); + FunctionData fData = { selectedPoints, screenPoints, nDOF, castToNDC, distToMinimize, _camera, node, res.x / res.y }; + void* dataPtr = reinterpret_cast(&fData); + + levmarq_init(&_lmstat); + bool success = levmarq(nDOF, par, nFingers, NULL, distToMinimize, gradient, dataPtr, &_lmstat); // finds best transform values and stores them in par + + if (success) { // if good values were found set new camera state + _vel.globalRot = glm::dvec2(par[0], par[1]); + if (nDOF > 2) { + _vel.zoom = par[2]; + _vel.localRoll = par[3]; + if (nDOF > 4) { + _vel.localRot = glm::dvec2(par[4], par[5]); + } + } + step(1); + } + + // debugging + /*std::ostringstream os; + for (int i = 0; i < nDOF; ++i) { + os << par[i] << ", "; + } + std::cout << "Levmarq success after " << _lmstat.final_it << " iterations. Values: " << os.str() << "\n";*/ + + // cleanup + delete[] par; +} + +// Traces the touch input into the scene and finds the surface coordinates of touched planets (if occuring) void TouchInteraction::trace(const std::vector& list) { //trim list to only contain visible nodes that make sense @@ -305,6 +312,7 @@ void TouchInteraction::trace(const std::vector& list) { _selected = newSelected; } +// Interprets the input gesture to a specific interaction void TouchInteraction::interpret(const std::vector& list, const std::vector& lastProcessed) { double dist = 0; double lastDist = 0; @@ -373,6 +381,7 @@ void TouchInteraction::interpret(const std::vector& list, const std: } } +// Calculate how much interpreted interaction should change the camera state (based on _vel) void TouchInteraction::accelerate(const std::vector& list, const std::vector& lastProcessed) { TuioCursor cursor = list.at(0); if (!_action.rot || !_action.pick) { @@ -420,10 +429,30 @@ void TouchInteraction::accelerate(const std::vector& list, const std _focusNode = _selected.at(0).node; // rotate camera to look at new focus OsEng.interactionHandler().setFocusNode(_focusNode); // cant do setFocusNode since TouchInteraction is not subclass of InteractionMode glm::dvec3 camToFocus = glm::normalize(_focusNode->worldPosition() - _camera->positionVec3()); - double angleX = glm::orientedAngle(_camera->viewDirectionWorldSpace(), camToFocus, glm::normalize(_camera->rotationQuaternion() * _camera->lookUpVectorWorldSpace())); - double angleY = glm::orientedAngle(_camera->viewDirectionWorldSpace(), camToFocus, glm::normalize(_camera->rotationQuaternion() * glm::dvec3(1,0,0))); - //std::cout << "x: " << angleX << ", y: " << angleY << "\n"; - _vel.localRot = _sensitivity.localRot * glm::dvec2(-angleX, -angleY); + glm::dvec3 camForward = glm::normalize(_camera->viewDirectionWorldSpace()); + glm::dvec3 camUp = glm::normalize(_camera->lookUpVectorWorldSpace()); + glm::dvec3 camRight = glm::normalize(glm::cross(camUp, camForward)); + + + glm::dvec2 angles = glm::dvec2(0.0); + + + glm::dvec3 directionToCenter = normalize(_focusNode->worldPosition() - _camera->positionVec3()); + glm::dvec3 lookUp = _camera->lookUpVectorWorldSpace(); + glm::dvec3 camDirection = _camera->viewDirectionWorldSpace(); + + // Make a representation of the rotation quaternion with local and global rotations + glm::dmat4 lookAtMat = lookAt( + glm::dvec3(0, 0, 0), + directionToCenter, + glm::normalize(camDirection + lookUp)); // To avoid problem with lookup in up direction + glm::dquat globalCamRot = normalize(quat_cast(inverse(lookAtMat))); + glm::dquat localCamRot = inverse(globalCamRot) * _camera->rotationQuaternion() * glm::dquat(glm::dvec3(angles.y, angles.x, 0.0)); + + _camera->setRotation(globalCamRot * localCamRot); + /// + + } else { // should zoom in to current _selected.coordinates position double dist = glm::distance(_camera->positionVec3(), _camera->focusPositionVec3()) - _focusNode->boundingSphere(); @@ -439,6 +468,7 @@ void TouchInteraction::accelerate(const std::vector& list, const std } } +// Main update call, calculates the new orientation and position for the camera depending on _vel and dt. Called every frame void TouchInteraction::step(double dt) { using namespace glm; @@ -522,6 +552,7 @@ void TouchInteraction::step(double dt) { } } +// Might not be needed with direct-manipulation void TouchInteraction::configSensitivities(double dist) { // Configurates sensitivities to appropriate values when the camera is close to the focus node. std::shared_ptr gbim = diff --git a/openspace.cfg b/openspace.cfg index cf331c42ce..6790350464 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -26,7 +26,7 @@ return { -- Sets the scene that is to be loaded by OpenSpace. A scene file is a description -- of all entities that will be visible during an instance of OpenSpace - Scene = "${SCENE}/globebrowsing.scene", + Scene = "${SCENE}/default.scene", -- Scene = "${SCENE}/globebrowsing.scene", -- Scene = "${SCENE}/rosetta.scene", -- Scene = "${SCENE}/dawn.scene",