Skip to content

Commit c61d56c

Browse files
authored
Merge pull request #40 from geefr/fix-teleport-demo
Interaction example and user origin fixes
2 parents 67dd61c + 0f29c18 commit c61d56c

File tree

17 files changed

+431
-78
lines changed

17 files changed

+431
-78
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
.vscode
2+
build
3+

examples/interaction_locomotion/game.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ void Game::loadScene()
3232
_sceneRoot->addChild(_controllerRight);
3333

3434
_teleportMarker = vsg::Switch::create();
35-
_teleportMarker->addChild(false, teleport_marker());
35+
auto teleportMatrix = vsg::MatrixTransform::create();
36+
_teleportMarker->addChild(false, teleportMatrix);
37+
teleportMatrix->addChild(teleport_marker());
3638
_sceneRoot->addChild(_teleportMarker);
3739

3840
// User origin - The regular vsg scene / world space,
@@ -75,19 +77,26 @@ void Game::initVR()
7577

7678
void Game::initActions()
7779
{
78-
// Configure OpenXR action sets and pose bindings - These allow elements of the OpenXR device tree to be located and tracked in space,
80+
// Tracking the location of the user's headset is achieved by tracking the VIEW reference space
81+
// vsgvr provides a SpaceBinding class for this - Similar to the ActionPoseBindings the head's pose
82+
// will be tracked during rendering, and available when performing interactions
83+
_headPose = vsgvr::SpaceBinding::create(_xrInstance, XrReferenceSpaceType::XR_REFERENCE_SPACE_TYPE_VIEW);
84+
_vr->spaceBindings.push_back(_headPose);
85+
86+
// Input devices are tracked via ActionPoseBindings - Tracking elements from the OpenXR device tree in the session space,
7987
// along with binding the OpenXR input subsystem through to usable actions.
8088
_baseActionSet = vsgvr::ActionSet::create(_xrInstance, "controller_positions", "Controller Positions");
81-
// Pose bindings - One for each hand
89+
// Pose bindings
8290
_leftHandPose = vsgvr::ActionPoseBinding::create(_xrInstance, _baseActionSet, "left_hand", "Left Hand");
8391
_rightHandPose = vsgvr::ActionPoseBinding::create(_xrInstance, _baseActionSet, "right_hand", "Right Hand");
92+
8493
_baseActionSet->actions = {
8594
_leftHandPose,
8695
_rightHandPose,
8796
};
8897

89-
_interactions.emplace("teleport", new Interaction_teleport(_xrInstance, _leftHandPose, _teleportMarker, _ground));
90-
_interactions.emplace("slide", new Interaction_slide(_xrInstance, _leftHandPose, _ground));
98+
_interactions.emplace("teleport", new Interaction_teleport(_xrInstance, _headPose, _leftHandPose, _teleportMarker, _ground));
99+
_interactions.emplace("slide", new Interaction_slide(_xrInstance, _headPose, _leftHandPose, _ground));
91100

92101
// Ask OpenXR to suggest interaction bindings.
93102
// * If subpaths are used, list all paths that each action should be bound for

examples/interaction_locomotion/game.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <vsgvr/actions/Action.h>
77
#include <vsgvr/actions/ActionSet.h>
88
#include <vsgvr/actions/ActionPoseBinding.h>
9+
#include <vsgvr/actions/SpaceBinding.h>
910

1011
#include "interaction.h"
1112

@@ -48,6 +49,7 @@ class Game {
4849
vsg::ref_ptr<vsgvr::ActionSet> _baseActionSet;
4950
vsg::ref_ptr<vsgvr::ActionPoseBinding> _leftHandPose;
5051
vsg::ref_ptr<vsgvr::ActionPoseBinding> _rightHandPose;
52+
vsg::ref_ptr<vsgvr::SpaceBinding> _headPose;
5153

5254
std::map<std::string, std::unique_ptr<Interaction>> _interactions;
5355

examples/interaction_locomotion/interaction.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <vsgvr/actions/Action.h>
55
#include <vsgvr/actions/ActionSet.h>
66
#include <vsgvr/actions/ActionPoseBinding.h>
7+
#include <vsgvr/actions/SpaceBinding.h>
78

89
#include <list>
910
#include <map>

examples/interaction_locomotion/interactions/interaction_slide.cpp

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#include <iostream>
88

99
Interaction_slide::Interaction_slide(
10-
vsg::ref_ptr<vsgvr::Instance> xrInstance,
10+
vsg::ref_ptr<vsgvr::Instance> xrInstance,
11+
vsg::ref_ptr<vsgvr::SpaceBinding> headPose,
1112
vsg::ref_ptr<vsgvr::ActionPoseBinding> leftHandPose,
1213
vsg::ref_ptr<vsg::Group> ground)
13-
: _leftHandPose(leftHandPose)
14+
: _headPose(headPose)
15+
, _leftHandPose(leftHandPose)
1416
, _ground(ground)
1517
{
1618
_actionSet = vsgvr::ActionSet::create(xrInstance, "slide", "Slide");
@@ -45,18 +47,36 @@ Interaction_slide::~Interaction_slide() {}
4547

4648
void Interaction_slide::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, double deltaT)
4749
{
50+
bool updateOrigin = false;
51+
52+
// The player's position in vr space, at 'ground' level
53+
// In most cases this will simply be playerPosOrigin.z = 0,
54+
// though an intersection to the scene would be more appropriate in many cases
55+
auto playerPosUser = _headPose->getTransform() * vsg::dvec3{ 0.0, 0.0, 0.0 };
56+
playerPosUser.z = 0.0;
57+
58+
// Player's position in the scene - Strafing is performed in scene space
59+
auto newPositionScene = game.userOrigin()->userToScene() * playerPosUser;
60+
4861
if (_rotateAction->getStateValid())
4962
{
5063
auto state = _rotateAction->getStateFloat();
5164
if (state.isActive && fabs(state.currentState) > deadZone)
5265
{
5366
_rotation += state.currentState * rotateSensitivity * deltaT * -1.0;
54-
game.userOrigin()->orientation = vsg::dquat(_rotation, { 0.0, 0.0, 1.0 });
67+
updateOrigin = true;
5568
}
5669
}
5770

5871
if (_leftHandPose->getTransformValid() && _strafeXAction->getStateValid() && _strafeYAction->getStateValid())
5972
{
73+
// Direction vectors in scene space, based on left controller
74+
// (User may point controller to modify strafe direction)
75+
// Alternatively the head's pose could be used to determine forward,
76+
// but that would prevent strafing sideways while looking sideways
77+
//
78+
// Here all strafing is performed in the xy plane, ignoring height differences
79+
// of the ground.
6080
auto lt = game.userOrigin()->userToScene() * _leftHandPose->getTransform();
6181
auto lForward = (lt * vsg::dvec3{0.0, 0.0, -1.0}) - (lt * vsg::dvec3{0.0, 0.0, 0.0});
6282
lForward.z = 0;
@@ -73,8 +93,20 @@ void Interaction_slide::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, double
7393
if (vsg::length(d) > deadZone)
7494
{
7595
d *= strafeSensitivity * deltaT;
76-
game.userOrigin()->position += d;
96+
newPositionScene += d;
97+
updateOrigin = true;
7798
}
7899
}
79100
}
101+
102+
if( updateOrigin )
103+
{
104+
// Update the vsg scene's transform to strafe player as needed
105+
game.userOrigin()->setUserInScene(
106+
playerPosUser,
107+
newPositionScene,
108+
vsg::dquat(_rotation, { 0.0, 0.0, 1.0 }),
109+
{ 1.0, 1.0, 1.0 }
110+
);
111+
}
80112
}

examples/interaction_locomotion/interactions/interaction_slide.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ class Interaction_slide : public Interaction
99
{
1010
public:
1111
Interaction_slide() = delete;
12-
Interaction_slide(vsg::ref_ptr<vsgvr::Instance> xrInstance,
12+
Interaction_slide(vsg::ref_ptr<vsgvr::Instance> xrInstance,
13+
vsg::ref_ptr<vsgvr::SpaceBinding> headPose,
1314
vsg::ref_ptr<vsgvr::ActionPoseBinding> leftHandPose,
1415
vsg::ref_ptr<vsg::Group> ground);
1516

@@ -24,6 +25,7 @@ class Interaction_slide : public Interaction
2425
vsg::ref_ptr<vsgvr::Action> _strafeXAction;
2526
vsg::ref_ptr<vsgvr::Action> _strafeYAction;
2627
vsg::ref_ptr<vsgvr::Action> _rotateAction;
28+
vsg::ref_ptr<vsgvr::SpaceBinding> _headPose;
2729
vsg::ref_ptr<vsgvr::ActionPoseBinding> _leftHandPose;
2830

2931
vsg::ref_ptr<vsg::Group> _ground;

examples/interaction_locomotion/interactions/interaction_teleport.cpp

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#include <iostream>
88

99
Interaction_teleport::Interaction_teleport(vsg::ref_ptr<vsgvr::Instance> xrInstance,
10+
vsg::ref_ptr<vsgvr::SpaceBinding> headPose,
1011
vsg::ref_ptr<vsgvr::ActionPoseBinding> leftHandPose,
1112
vsg::ref_ptr<vsg::Switch> teleportTarget,
1213
vsg::ref_ptr<vsg::Group> ground)
13-
: _leftHandPose(leftHandPose)
14+
: _headPose(headPose)
15+
, _leftHandPose(leftHandPose)
1416
, _teleportTarget(teleportTarget)
1517
, _ground(ground)
1618
{
@@ -42,6 +44,9 @@ Interaction_teleport::~Interaction_teleport() {}
4244

4345
void Interaction_teleport::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, double deltaT)
4446
{
47+
auto applyRotation = false;
48+
auto applyTeleport = false;
49+
4550
if (_teleportAction->getStateValid())
4651
{
4752
auto state = _teleportAction->getStateBool();
@@ -50,6 +55,7 @@ void Interaction_teleport::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, dou
5055
_teleportButtonDown = true;
5156
}
5257
}
58+
5359
double rotThresh = 0.25;
5460
if (_rotateAction->getStateValid())
5561
{
@@ -62,11 +68,11 @@ void Interaction_teleport::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, dou
6268
if (_rotateActionState != 0)
6369
{
6470
_playerRotation += (- 15.0 * _rotateActionState);
65-
game.userOrigin()->orientation = vsg::dquat(vsg::radians(_playerRotation), { 0.0, 0.0, 1.0 });
71+
applyRotation = true;
6672
}
6773
}
6874
}
69-
75+
7076
if (_teleportButtonDown && _leftHandPose->getTransformValid())
7177
{
7278
// Raycast from controller aim to world, colliding with anything named "ground"
@@ -79,8 +85,8 @@ void Interaction_teleport::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, dou
7985

8086
auto intersector = vsg::LineSegmentIntersector::create(intersectStart, intersectEnd);
8187
// TODO: Intersect whole scene, or sub-scene matched on tags? For now we can't be anywhere but the ground plane
82-
// TODO: Intersections appear to be in reverse-distance order. Should double check this but intersections.back()
83-
// seems to always get the ground plane (and not fences, trees, houses, or the underside of the ground plane.
88+
// TODO: Should sort intersections as well - For now intersections.back() seems to always get the ground plane,
89+
// and not other geometry within the world.
8490
_ground->accept(*intersector);
8591
if (!intersector->intersections.empty())
8692
{
@@ -93,7 +99,7 @@ void Interaction_teleport::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, dou
9399
{
94100
if (auto m = child.node->cast<vsg::MatrixTransform>())
95101
{
96-
m->matrix = vsg::translate(_teleportPosition) * vsg::rotate(vsg::radians(90.0), {1.0, 0.0, 0.0});
102+
m->matrix = vsg::translate(_teleportPosition);
97103
}
98104
}
99105
}
@@ -112,7 +118,8 @@ void Interaction_teleport::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, dou
112118
if (_teleportTargetValid)
113119
{
114120
// Teleport position within the child scene
115-
game.userOrigin()->position = game.userOrigin()->userToScene() * _teleportPosition;
121+
// Here the origin is being moved, by the difference between the teleport target, and where the user currently is (left hand)
122+
applyTeleport = true;
116123
}
117124
_teleportButtonDown = false;
118125
_teleportTargetValid = false;
@@ -127,4 +134,34 @@ void Interaction_teleport::frame(vsg::ref_ptr<vsg::Group> scene, Game& game, dou
127134
if( fabs(state.currentState) < rotThresh) _rotateActionState = 0;
128135
}
129136
}
137+
138+
if( applyRotation || applyTeleport )
139+
{
140+
// The player's position in vr space, at 'ground' level
141+
// In this case we know the level if the ground from the teleport target
142+
auto playerPosOrigin = _headPose->getTransform() * vsg::dvec3{ 0.0, 0.0, 0.0 };
143+
playerPosOrigin.z = _teleportPosition.z;
144+
145+
vsg::dvec3 newPlayerPosScene;
146+
if (!applyTeleport)
147+
{
148+
// The player hasn't moved
149+
newPlayerPosScene = game.userOrigin()->userToScene() * playerPosOrigin;
150+
}
151+
else
152+
{
153+
// The player has moved - Center on the teleport target
154+
newPlayerPosScene = game.userOrigin()->userToScene() * _teleportPosition;
155+
}
156+
157+
// Set the transform from vr origin to position/rotation within scene
158+
// The initial translate is important as the user is rarely positioned
159+
// at the origin of their vr space.
160+
game.userOrigin()->setUserInScene(
161+
playerPosOrigin,
162+
newPlayerPosScene,
163+
vsg::dquat(vsg::radians(_playerRotation), { 0.0, 0.0, 1.0 }),
164+
{1.0, 1.0, 1.0}
165+
);
166+
}
130167
}

examples/interaction_locomotion/interactions/interaction_teleport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class Interaction_teleport : public Interaction
1010
public:
1111
Interaction_teleport() = delete;
1212
Interaction_teleport(vsg::ref_ptr<vsgvr::Instance> xrInstance,
13+
vsg::ref_ptr<vsgvr::SpaceBinding> headPose,
1314
vsg::ref_ptr<vsgvr::ActionPoseBinding> leftHandPose,
1415
vsg::ref_ptr<vsg::Switch> teleportTarget,
1516
vsg::ref_ptr<vsg::Group> ground);
@@ -23,11 +24,13 @@ class Interaction_teleport : public Interaction
2324

2425
vsg::dvec3 _teleportPosition = {0.0, 0.0, 0.0};
2526
vsg::ref_ptr<vsg::Switch> _teleportTarget;
27+
vsg::ref_ptr<vsgvr::SpaceBinding> _headPose;
2628
vsg::ref_ptr<vsgvr::ActionPoseBinding> _leftHandPose;
2729

2830
bool _teleportButtonDown = false;
2931
bool _teleportTargetValid = false;
3032
int _rotateActionState = 0; // -1 is rot left, 1 is rot right
33+
3134
double _playerRotation = 0.0;
3235

3336
vsg::ref_ptr<vsg::Group> _ground;

vsgvr/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
set( HEADERS_ACTIONS
33
include/vsgvr/actions/Action.h
44
include/vsgvr/actions/ActionPoseBinding.h
5+
include/vsgvr/actions/SpaceBinding.h
56
include/vsgvr/actions/ActionSet.h
67
)
78

@@ -25,6 +26,7 @@ set( HEADERS_XR
2526
set( SOURCES
2627
src/vsgvr/actions/Action.cpp
2728
src/vsgvr/actions/ActionPoseBinding.cpp
29+
src/vsgvr/actions/SpaceBinding.cpp
2830
src/vsgvr/actions/ActionSet.cpp
2931
src/vsgvr/app/Viewer.cpp
3032
src/vsgvr/app/UserOrigin.cpp
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
Copyright(c) 2022 Gareth Francis
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy of
5+
this software and associated documentation files (the "Software"), to deal in
6+
the Software without restriction, including without limitation the rights to
7+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8+
the Software, and to permit persons to whom the Software is furnished to do so,
9+
subject to the following conditions:
10+
11+
The above copyright notice and this permission notice shall be included in all
12+
copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
#pragma once
23+
24+
#include <vsg/core/Inherit.h>
25+
26+
#include <vsgvr/xr/Common.h>
27+
#include <vsgvr/xr/Instance.h>
28+
29+
namespace vsgvr {
30+
class Session;
31+
32+
/**
33+
* A binding class allowing an XrSpace to be tracked by the Viewer.
34+
* The specified reference space will be kept updated during rendering (along with any ActionPoseBindings)
35+
* in order to track elements such as the position of the headset (view) space, or user's local space.
36+
*/
37+
class VSGVR_DECLSPEC SpaceBinding : public vsg::Inherit<vsg::Object, SpaceBinding>
38+
{
39+
public:
40+
SpaceBinding(vsg::ref_ptr<Instance> instance, XrReferenceSpaceType spaceType);
41+
virtual ~SpaceBinding();
42+
43+
XrSpace getSpace() const { return _space; }
44+
45+
bool getTransformValid() const { return _transformValid; }
46+
vsg::dmat4 getTransform() const { return _transform; }
47+
XrReferenceSpaceType getSpaceType() const { return _spaceType; }
48+
49+
void createSpace(Session* session);
50+
void destroySpace();
51+
52+
void setSpaceLocation(XrSpaceLocation location);
53+
private:
54+
XrSpace _space = XR_NULL_HANDLE;
55+
XrReferenceSpaceType _spaceType;
56+
57+
bool _transformValid = false;
58+
vsg::dmat4 _transform;
59+
};
60+
}
61+
62+
EVSG_type_name(vsgvr::SpaceBinding);

0 commit comments

Comments
 (0)