Skip to content

Commit 2ff5288

Browse files
authored
Merge pull request #134 from hlrs-vis/teleport
Add new teleport navigation mode
2 parents 61635f7 + 28355bd commit 2ff5288

9 files changed

Lines changed: 371 additions & 35 deletions
10.8 KB
Binary file not shown.

src/OpenCOVER/cover/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ SET(COVER_HEADERS
4848
coVRMessageSender.h
4949
coVRMSController.h
5050
coVRNavigationManager.h
51+
coVRNavigationProvider.h
5152
coVRPartner.h
5253
coVRPlugin.h
5354
coVRPluginList.h
@@ -82,6 +83,7 @@ SET(COVER_HEADERS
8283
VRVruiButtons.h
8384
VRVruiRenderInterface.h
8485
VRWindow.h
86+
TeleportNavigationProvider.h
8587
)
8688

8789
SET(COVER_SOURCES
@@ -119,6 +121,7 @@ SET(COVER_SOURCES
119121
coVRMessageSender.cpp
120122
coVRMSController.cpp
121123
coVRNavigationManager.cpp
124+
coVRNavigationProvider.cpp
122125
coVRPartner.cpp
123126
coVRPlugin.cpp
124127
coVRPluginList.cpp
@@ -153,6 +156,7 @@ SET(COVER_SOURCES
153156
VRVruiButtons.cpp
154157
VRVruiRenderInterface.cpp
155158
VRWindow.cpp
159+
TeleportNavigationProvider.cpp
156160
#coVRDePee.cpp
157161
#coVRDePeePass.cpp
158162
)

src/OpenCOVER/cover/OpenCOVER.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -668,9 +668,7 @@ bool OpenCOVER::init()
668668
coVRShaderList::instance()->update();
669669
VRViewer::instance()->setSceneData(cover->getScene());
670670

671-
Input::instance()->update(); // requires scenegraph
672-
673-
cover->setScale(coCoviseConfig::getFloat("COVER.DefaultScaleFactor", 1.f));
671+
Input::instance()->update(); // requires scenegraph
674672

675673
bool haveWindows = VRWindow::instance()->config();
676674
haveWindows = coVRMSController::instance()->allReduceOr(haveWindows);
@@ -709,6 +707,9 @@ bool OpenCOVER::init()
709707
hud->setText2("loading plugins");
710708
hud->redraw();
711709

710+
coVRNavigationManager::instance();
711+
cover->setScale(coCoviseConfig::getFloat("COVER.DefaultScaleFactor", 1.f));
712+
712713
if (m_loadVistlePlugin)
713714
{
714715
loadFiles = false;
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
/* This file is part of COVISE.
2+
3+
You can use it under the terms of the GNU Lesser General Public License
4+
version 2.1 or later, see lgpl-2.1.txt.
5+
6+
* License: LGPL 2+ */
7+
8+
#include "TeleportNavigationProvider.h"
9+
#include <OpenVRUI/coInteractionManager.h>
10+
#include <cmath>
11+
#include <cover/VRSceneGraph.h>
12+
#include <cover/coIntersection.h>
13+
#include <cover/coVRFileManager.h>
14+
#include <cover/coVRNavigationManager.h>
15+
#include <cover/coVRPluginSupport.h>
16+
#include <osg/MatrixTransform>
17+
#include <osg/PolygonOffset>
18+
#include <osg/Switch>
19+
#include <osg/Vec3>
20+
#include <osg/Matrix>
21+
22+
using namespace opencover;
23+
24+
static osg::Vec3 X = osg::Vec3(1, 0, 0);
25+
static osg::Vec3 UP = osg::Vec3(0, 0, 1);
26+
27+
TeleportNavigationProvider::TeleportNavigationProvider()
28+
: coVRNavigationProvider("Teleport", nullptr)
29+
, interactionPoint(vrui::coInteraction::ButtonA, "ProbeMode", vrui::coInteraction::Navigation)
30+
, interactionTurn(vrui::coInteraction::ButtonB, "ProbeMode", vrui::coInteraction::Navigation)
31+
, triggerMouse(vrui::coInteraction::ButtonA, "MouseTeleport")
32+
, triggerWheel(vrui::coInteraction::Wheel, "MouseScroll")
33+
{
34+
triggerMouse.setGroup(vrui::coInteraction::GroupNavigation);
35+
36+
switch_ = new osg::Switch;
37+
cover->getObjectsRoot()->addChild(switch_);
38+
39+
transform = new osg::MatrixTransform;
40+
switch_->addChild(transform);
41+
42+
icon = coVRFileManager::instance()->loadFile("share/covise/icons/teleport_target.glb", nullptr, transform);
43+
44+
setVisible(false);
45+
}
46+
47+
TeleportNavigationProvider::~TeleportNavigationProvider()
48+
{
49+
}
50+
51+
void TeleportNavigationProvider::setEnabled(bool enabled)
52+
{
53+
if (isEnabled() == enabled)
54+
return;
55+
56+
coVRNavigationProvider::setEnabled(enabled);
57+
coIntersection::instance()->isectAllNodes(enabled);
58+
59+
if (enabled)
60+
{
61+
vrui::coInteractionManager::the()->registerInteraction(&interactionPoint);
62+
vrui::coInteractionManager::the()->registerInteraction(&interactionTurn);
63+
vrui::coInteractionManager::the()->registerInteraction(&triggerMouse);
64+
vrui::coInteractionManager::the()->registerInteraction(&triggerWheel);
65+
}
66+
else
67+
{
68+
vrui::coInteractionManager::the()->unregisterInteraction(&interactionPoint);
69+
vrui::coInteractionManager::the()->unregisterInteraction(&interactionTurn);
70+
vrui::coInteractionManager::the()->unregisterInteraction(&triggerMouse);
71+
vrui::coInteractionManager::the()->unregisterInteraction(&triggerWheel);
72+
}
73+
}
74+
75+
/**
76+
* Computes where to place the objects, based on where we want to teleport to.
77+
*/
78+
osg::Matrix computeNewObjectsTransform(const osg::Matrix &targetTransform, bool includeViewer = false)
79+
{
80+
// We'll need this later.
81+
auto scaleTransform = cover->getObjectsScale()->getMatrix();
82+
83+
// The `referenceTransform` is location of the viewer's feet in the stage.
84+
// We use about 1.5m eye height for now.
85+
auto referenceTransform = osg::Matrix::scale(1000, 1000, 1000) * osg::Matrix::translate(0, 0, -1500);
86+
87+
// If `includeViewer` is true, the viewer matrix is included in the
88+
// reference transform, which is useful when navigating with the mouse on a
89+
// 2D screen, for it places the camera, not the stage origin, into the
90+
// target location. In VR, we want to align the stage origin with the
91+
// teleport target (such that head-tracked orientation and offset are
92+
// ignored), so we do not include the viewer matrix.
93+
if (includeViewer)
94+
{
95+
referenceTransform = referenceTransform * cover->getViewerMat();
96+
}
97+
98+
// This equation aligns the "reference transform" (viewer's feet) with the
99+
// global targetTransform (normal left-to-right matrix order).
100+
//
101+
// ReferenceTransform = newObjectsTransform * scaleTransform * targetTransform
102+
//
103+
// We rearrange it to compute only the new objectsTransform:
104+
//
105+
// ~newObjectsTransform * ReferenceTransform = scaleTransform * targetTransform
106+
// ~newObjectsTransform = scaleTransform * targetTransform * ~ReferenceTransform
107+
// newObjectsTransform = ~(scaleTransform * targetTransform * ~ReferenceTransform)
108+
//
109+
// This is implemented below (notice the inverted order of operations
110+
// because of OSG's matrix structure):
111+
112+
return osg::Matrix::inverse(osg::Matrix::inverse(referenceTransform) * targetTransform * scaleTransform);
113+
}
114+
115+
bool TeleportNavigationProvider::update()
116+
{
117+
bool enabledAndValid = isEnabledAndValid();
118+
setVisible(enabledAndValid);
119+
120+
if (!enabledAndValid)
121+
{
122+
return true;
123+
}
124+
125+
// Adjust turn angle based on the mouse wheel
126+
turn_angle += triggerWheel.getWheelCount() * M_PI / 8;
127+
128+
// Adjust turn angle based using pointer secondary action and swipe left/right
129+
if (interactionTurn.wasStarted())
130+
{
131+
oldHandMatrix = cover->getPointerMat();
132+
}
133+
else if (interactionTurn.isRunning())
134+
{
135+
// See how the hand was moved and compute the angle it turned around the Z axis.
136+
auto handMatrix = cover->getPointerMat();
137+
auto handMovmentSinceLastFrame = handMatrix * osg::Matrix::inverse(oldHandMatrix);
138+
auto c = handMovmentSinceLastFrame.getRotate() * X;
139+
auto angle_change = (c.y() || c.x()) ? atan2(c.y(), c.x()) : 0.f;
140+
141+
// Turn the target location much more than the sweeping motion from the
142+
// pointer/hand.
143+
turn_angle += angle_change * 10.f;
144+
145+
oldHandMatrix = handMatrix;
146+
}
147+
148+
osg::Matrix currentObjectsTransform = cover->getObjectsXform()->getMatrix();
149+
150+
// Compute the current object's rotation around the Z axis, we want the
151+
// target angle to be the same as the current angle for `turn_angle = 0`.
152+
osg::Quat q = currentObjectsTransform.getRotate();
153+
osg::Vec3 f = q * X;
154+
float current_angle = f.x() || f.y() ? -atan2(f.y(), f.x()) : 0.0;
155+
156+
// The target angle around Z is the sum of the current angle and the
157+
// (relative) turn.
158+
float target_angle = current_angle + turn_angle;
159+
160+
osg::Vec3 position = cover->getIntersectionHitPointWorld();
161+
162+
// The intersection position is relative to the world (stage), so we need
163+
// to turn it into object coordinates, as we attached the indicator to the object root.
164+
osg::Matrix objectsRootTransform = osg::computeWorldToLocal(cover->getObjectsRoot()->getParentalNodePaths().at(0));
165+
166+
auto targetTransform = osg::Matrix::rotate(target_angle, UP) * osg::Matrix::translate(position * objectsRootTransform + UP * 0.001f);
167+
168+
// Set the indicator transform, scaling and rotating it (because the loaded
169+
// model is a unit circle and points in the wrong direction).
170+
float ringSize = 0.4;
171+
auto ringScale = osg::Matrix::scale(ringSize, ringSize, ringSize);
172+
transform->setMatrix(ringScale * osg::Matrix::rotate(M_PI / 2, UP) * targetTransform);
173+
174+
bool mouseTriggered = triggerMouse.wasStopped();
175+
bool pointerTriggered = interactionPoint.wasStopped();
176+
177+
if (mouseTriggered || pointerTriggered)
178+
{
179+
// Apply the movement transform
180+
cover->getObjectsXform()->setMatrix(computeNewObjectsTransform(targetTransform, mouseTriggered));
181+
182+
// Reset turn_angle for the next jump
183+
turn_angle = 0.f;
184+
}
185+
186+
return true;
187+
}
188+
189+
bool TeleportNavigationProvider::isEnabledAndValid()
190+
{
191+
if (!isEnabled())
192+
return false;
193+
194+
if (!cover->getIntersectedNode())
195+
return false;
196+
197+
osg::Vec3 normal = cover->getIntersectionHitPointWorldNormal();
198+
normal.normalize();
199+
200+
// Get and decompose the object's space transform
201+
osg::Matrix objectsRootTransform = osg::computeWorldToLocal(cover->getObjectsRoot()->getParentalNodePaths().at(0));
202+
osg::Vec3f translation, scale;
203+
osg::Quat rotation, so;
204+
objectsRootTransform.decompose(translation, rotation, scale, so);
205+
206+
// Rotate the normal into object space
207+
normal = rotation * normal;
208+
209+
// If the normal in object space doesn't roughly point upwards, we don't
210+
// want to teleport there.
211+
if (normal * UP < 0.9f)
212+
return false;
213+
214+
return true;
215+
}
216+
217+
void TeleportNavigationProvider::setVisible(bool visible)
218+
{
219+
if (visible)
220+
switch_->setAllChildrenOn();
221+
else
222+
switch_->setAllChildrenOff();
223+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* This file is part of COVISE.
2+
3+
You can use it under the terms of the GNU Lesser General Public License
4+
version 2.1 or later, see lgpl-2.1.txt.
5+
6+
* License: LGPL 2+ */
7+
8+
#ifndef _TELEPORT_NAVIGATION_PROVIDER_H
9+
#define _TELEPORT_NAVIGATION_PROVIDER_H
10+
11+
#include <OpenVRUI/coMouseButtonInteraction.h>
12+
#include <OpenVRUI/coNavInteraction.h>
13+
#include <osg/MatrixTransform>
14+
#include <osg/ref_ptr>
15+
16+
#include "coVRNavigationProvider.h"
17+
18+
namespace opencover {
19+
20+
class TeleportNavigationProvider : public opencover::coVRNavigationProvider
21+
{
22+
public:
23+
TeleportNavigationProvider();
24+
virtual ~TeleportNavigationProvider();
25+
26+
virtual void setEnabled(bool enabled);
27+
28+
bool update();
29+
30+
private:
31+
bool isEnabledAndValid();
32+
void setVisible(bool visible);
33+
34+
osg::ref_ptr<osg::Switch> switch_;
35+
osg::ref_ptr<osg::MatrixTransform> transform;
36+
osg::ref_ptr<osg::Node> icon;
37+
float turn_angle = 0.0;
38+
bool was_visible = false;
39+
40+
vrui::coNavInteraction interactionPoint;
41+
vrui::coNavInteraction interactionTurn;
42+
vrui::coMouseButtonInteraction triggerMouse; ///< trigger interaction
43+
vrui::coMouseButtonInteraction triggerWheel; ///< adjust turn angle
44+
45+
osg::Matrix oldHandMatrix;
46+
};
47+
48+
}
49+
50+
#endif

src/OpenCOVER/cover/coVRNavigationManager.cpp

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* This file is part of COVISE.
1+
/* This file is part of COVISE.
22
33
You can use it under the terms of the GNU Lesser General Public License
44
version 2.1 or later, see lgpl-2.1.txt.
@@ -116,21 +116,6 @@ static float mouseScreenHeight()
116116
return Input::instance()->mouse()->screenHeight();
117117
}
118118

119-
coVRNavigationProvider::coVRNavigationProvider(const std::string n, coVRPlugin* p)
120-
{
121-
name = n;
122-
plugin = p;
123-
ID = -1;
124-
}
125-
coVRNavigationProvider::~coVRNavigationProvider()
126-
{
127-
}
128-
129-
void coVRNavigationProvider::setEnabled(bool state)
130-
{
131-
enabled = state;
132-
}
133-
134119
coVRNavigationManager *coVRNavigationManager::instance()
135120
{
136121
if (!s_instance)
@@ -206,13 +191,17 @@ void coVRNavigationManager::init()
206191
setRotationPointVisible(rotationPointVisible);
207192

208193
setNavMode(XForm);
194+
195+
registerNavigationProvider(&teleportNavigationProvider);
209196
}
210197

211198
coVRNavigationManager::~coVRNavigationManager()
212199
{
213200
if (cover->debugLevel(2))
214201
fprintf(stderr, "\ndelete coVRNavigationManager\n");
215202

203+
unregisterNavigationProvider(&teleportNavigationProvider);
204+
216205
if (interactionA->isRegistered())
217206
{
218207
coInteractionManager::the()->unregisterInteraction(interactionA);
@@ -1603,6 +1592,8 @@ void coVRNavigationManager::update()
16031592
{
16041593
(*it)->preFrame();
16051594
}
1595+
1596+
teleportNavigationProvider.update();
16061597
}
16071598

16081599
void coVRNavigationManager::setNavMode(std::string modeName)

0 commit comments

Comments
 (0)