/
Scene_EyeGazeInteraction.cpp
113 lines (97 loc) · 5.64 KB
/
Scene_EyeGazeInteraction.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
#include "pch.h"
#include <XrSceneLib/PbrModelObject.h>
#include <XrSceneLib/Scene.h>
using namespace DirectX;
using namespace xr::math;
using namespace std::chrono;
using namespace std::chrono_literals;
//
// This sample shows the usage of eye gaze interaction extension.
// When the user is looking at a ball layed out in a circle around, the ball will rotate.
//
namespace {
constexpr float objectDiameter = 0.1f; // In meters
constexpr float layoutRadius = 1.5f; // In meters
constexpr int numberOfObjects = 36; // Number of objects for a full circle.
bool IsLookingAtObject(const XrPosef& gazeInAppSpace, const XrPosef& objectInAppSpace, float objectRadius) {
const XrPosef appSpaceInGaze = Pose::Invert(gazeInAppSpace);
const XrPosef objectInGaze = objectInAppSpace * appSpaceInGaze;
const float projectionOnZ = Normalize(objectInGaze.position).z;
const float distance = Length(objectInGaze.position);
const float angleTolerance = std::atan2(objectRadius, distance);
return projectionOnZ < -std::cos(angleTolerance);
}
struct EyeGazeInteractionScene : public engine::Scene {
EyeGazeInteractionScene(engine::Context& context)
: Scene(context)
, m_supportsEyeGazeAction(context.Extensions.XR_EXT_eye_gaze_interaction_enabled &&
context.System.EyeGazeInteractionProperties.supportsEyeGazeInteraction) {
if (m_supportsEyeGazeAction) {
sample::ActionSet& actionSet =
ActionContext().CreateActionSet("eye_gaze_interaction_scene_actions", "Eye Gaze Interaction Scene Actions");
auto gazeAction = actionSet.CreateAction("gaze_action", "Gaze Action", XR_ACTION_TYPE_POSE_INPUT, {});
ActionContext().SuggestInteractionProfileBindings("/interaction_profiles/ext/eye_gaze_interaction",
{
{gazeAction, "/user/eyes_ext/input/gaze_ext/pose"},
});
XrActionSpaceCreateInfo createInfo{XR_TYPE_ACTION_SPACE_CREATE_INFO};
createInfo.action = gazeAction;
createInfo.poseInActionSpace = Pose::Identity();
CHECK_XRCMD(xrCreateActionSpace(m_context.Session.Handle, &createInfo, m_gazeSpace.Put(xrDestroySpace)));
} else {
// Use VIEW reference space to simulate eye gaze when the system doesn't support
XrReferenceSpaceCreateInfo createInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO};
createInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW;
createInfo.poseInReferenceSpace = Pose::Identity();
CHECK_XRCMD(xrCreateReferenceSpace(m_context.Session.Handle, &createInfo, m_gazeSpace.Put(xrDestroySpace)));
}
m_gazeObject = AddObject(engine::CreateObject());
// Draw a small axis object 2 meters in front to visualize the orientation of gaze
m_gazeLookAtAxis = AddObject(engine::CreateAxis(m_context.PbrResources, 0.05f));
m_gazeLookAtAxis->Pose() = Pose::Translation({0, 0, -2});
m_gazeLookAtAxis->SetParent(m_gazeObject);
auto randomColor = []() -> Pbr::RGBAColor {
constexpr float normalize = 1.0f / RAND_MAX;
return {rand() * normalize, rand() * normalize, rand() * normalize, 1};
};
// Draw a circle of objects around the user, and they will rotate when user is looking at them.
const float angleDistance = XM_2PI / numberOfObjects;
for (int i = 0; i < numberOfObjects; i++) {
auto object = AddObject(engine::CreateSphere(m_context.PbrResources, objectDiameter, 3, randomColor()));
object->Pose().position.x = layoutRadius * std::sin(i * angleDistance);
object->Pose().position.z = layoutRadius * std::cos(i * angleDistance);
object->Motion.SetRotation({0, 0, 1}, XM_2PI); // Rotate around per second
m_lookAtObjects.emplace_back(std::move(object));
}
}
void OnUpdate(const engine::FrameTime& frameTime) override {
XrSpaceLocation location{XR_TYPE_SPACE_LOCATION};
CHECK_XRCMD(xrLocateSpace(m_gazeSpace.Get(), m_context.AppSpace, frameTime.PredictedDisplayTime, &location));
if (Pose::IsPoseValid(location)) {
m_gazeObject->SetVisible(true);
m_gazeObject->Pose() = location.pose;
for (auto& object : m_lookAtObjects) {
XrPosef objectInAppSpace;
StoreXrPose(&objectInAppSpace, object->WorldTransform());
object->Motion.Enabled = IsLookingAtObject(location.pose, objectInAppSpace, objectDiameter / 2);
}
} else {
m_gazeObject->SetVisible(false);
for (auto& object : m_lookAtObjects) {
object->Motion.Enabled = false;
}
}
}
private:
const bool m_supportsEyeGazeAction{false};
xr::SpaceHandle m_gazeSpace;
std::shared_ptr<engine::Object> m_gazeObject;
std::shared_ptr<engine::Object> m_gazeLookAtAxis;
std::vector<std::shared_ptr<engine::Object>> m_lookAtObjects;
};
} // namespace
std::unique_ptr<engine::Scene> TryCreateEyeGazeInteractionScene(engine::Context& context) {
return std::make_unique<EyeGazeInteractionScene>(context);
}