blob: 257ee4258bee16ee2b89503b98f4ba2c7db92439 [file] [log] [blame]
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -07001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"){}
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "media/HeadTrackingProcessor.h"
18
19#include "ModeSelector.h"
20#include "PoseDriftCompensator.h"
21#include "QuaternionUtil.h"
22#include "ScreenHeadFusion.h"
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -080023#include "StillnessDetector.h"
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070024
25namespace android {
26namespace media {
27namespace {
28
29using Eigen::Quaternionf;
30using Eigen::Vector3f;
31
32class HeadTrackingProcessorImpl : public HeadTrackingProcessor {
33 public:
34 HeadTrackingProcessorImpl(const Options& options, HeadTrackingMode initialMode)
35 : mOptions(options),
36 mHeadPoseDriftCompensator(PoseDriftCompensator::Options{
37 .translationalDriftTimeConstant = options.translationalDriftTimeConstant,
38 .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant,
39 }),
40 mScreenPoseDriftCompensator(PoseDriftCompensator::Options{
41 .translationalDriftTimeConstant = options.translationalDriftTimeConstant,
42 .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant,
43 }),
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -080044 mHeadStillnessDetector(StillnessDetector::Options{
45 .windowDuration = options.autoRecenterWindowDuration,
46 .translationalThreshold = options.autoRecenterTranslationalThreshold,
47 .rotationalThreshold = options.autoRecenterRotationalThreshold,
48 }),
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080049 mScreenStillnessDetector(StillnessDetector::Options{
50 .windowDuration = options.screenStillnessWindowDuration,
51 .translationalThreshold = options.screenStillnessTranslationalThreshold,
52 .rotationalThreshold = options.screenStillnessRotationalThreshold,
53 }),
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070054 mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout},
55 initialMode),
56 mRateLimiter(PoseRateLimiter::Options{
57 .maxTranslationalVelocity = options.maxTranslationalVelocity,
58 .maxRotationalVelocity = options.maxRotationalVelocity}) {}
59
60 void setDesiredMode(HeadTrackingMode mode) override { mModeSelector.setDesiredMode(mode); }
61
62 void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead,
63 const Twist3f& headTwist) override {
64 Pose3f predictedWorldToHead =
65 worldToHead * integrate(headTwist, mOptions.predictionDuration);
66 mHeadPoseDriftCompensator.setInput(timestamp, predictedWorldToHead);
67 mWorldToHeadTimestamp = timestamp;
68 }
69
70 void setWorldToScreenPose(int64_t timestamp, const Pose3f& worldToScreen) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070071 if (mPhysicalToLogicalAngle != mPendingPhysicalToLogicalAngle) {
72 // We're introducing an artificial discontinuity. Enable the rate limiter.
73 mRateLimiter.enable();
74 mPhysicalToLogicalAngle = mPendingPhysicalToLogicalAngle;
75 }
76
77 mScreenPoseDriftCompensator.setInput(
78 timestamp, worldToScreen * Pose3f(rotateY(-mPhysicalToLogicalAngle)));
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070079 mWorldToScreenTimestamp = timestamp;
80 }
81
82 void setScreenToStagePose(const Pose3f& screenToStage) override {
83 mModeSelector.setScreenToStagePose(screenToStage);
84 }
85
86 void setDisplayOrientation(float physicalToLogicalAngle) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070087 mPendingPhysicalToLogicalAngle = physicalToLogicalAngle;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070088 }
89
90 void calculate(int64_t timestamp) override {
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080091 // Handle the screen first, since it might trigger a recentering of the head.
92 if (mWorldToScreenTimestamp.has_value()) {
93 const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput();
94 mScreenStillnessDetector.setInput(mWorldToScreenTimestamp.value(),
95 worldToLogicalScreen);
96 bool screenStable = mScreenStillnessDetector.calculate(timestamp);
97 mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
98 // Whenever the screen is unstable, recenter the head pose.
99 if (!screenStable) {
100 recenter(true, false);
101 }
102 mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
103 worldToLogicalScreen);
104 }
105
106 // Handle head.
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700107 if (mWorldToHeadTimestamp.has_value()) {
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800108 Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
109 mHeadStillnessDetector.setInput(mWorldToHeadTimestamp.value(), worldToHead);
110 // Auto-recenter.
111 if (mHeadStillnessDetector.calculate(timestamp)) {
112 recenter(true, false);
113 worldToHead = mHeadPoseDriftCompensator.getOutput();
114 }
115
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700116 mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
117 mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
118 }
119
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700120 auto maybeScreenToHead = mScreenHeadFusion.calculate();
121 if (maybeScreenToHead.has_value()) {
122 mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
123 maybeScreenToHead->pose);
124 } else {
125 mModeSelector.setScreenToHeadPose(timestamp, std::nullopt);
126 }
127
128 HeadTrackingMode prevMode = mModeSelector.getActualMode();
129 mModeSelector.calculate(timestamp);
130 if (mModeSelector.getActualMode() != prevMode) {
131 // Mode has changed, enable rate limiting.
132 mRateLimiter.enable();
133 }
134 mRateLimiter.setTarget(mModeSelector.getHeadToStagePose());
135 mHeadToStagePose = mRateLimiter.calculatePose(timestamp);
136 }
137
138 Pose3f getHeadToStagePose() const override { return mHeadToStagePose; }
139
140 HeadTrackingMode getActualMode() const override { return mModeSelector.getActualMode(); }
141
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700142 void recenter(bool recenterHead, bool recenterScreen) override {
143 if (recenterHead) {
144 mHeadPoseDriftCompensator.recenter();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800145 mHeadStillnessDetector.reset();
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700146 }
147 if (recenterScreen) {
148 mScreenPoseDriftCompensator.recenter();
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800149 mScreenStillnessDetector.reset();
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700150 }
151
152 // If a sensor being recentered is included in the current mode, apply rate limiting to
153 // avoid discontinuities.
154 HeadTrackingMode mode = mModeSelector.getActualMode();
155 if ((recenterHead && (mode == HeadTrackingMode::WORLD_RELATIVE ||
156 mode == HeadTrackingMode::SCREEN_RELATIVE)) ||
157 (recenterScreen && mode == HeadTrackingMode::SCREEN_RELATIVE)) {
158 mRateLimiter.enable();
159 }
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700160 }
161
162 private:
163 const Options mOptions;
164 float mPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -0700165 // We store the physical to logical angle as "pending" until the next world-to-screen sample it
166 // applies to arrives.
167 float mPendingPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700168 std::optional<int64_t> mWorldToHeadTimestamp;
169 std::optional<int64_t> mWorldToScreenTimestamp;
170 Pose3f mHeadToStagePose;
171 PoseDriftCompensator mHeadPoseDriftCompensator;
172 PoseDriftCompensator mScreenPoseDriftCompensator;
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800173 StillnessDetector mHeadStillnessDetector;
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800174 StillnessDetector mScreenStillnessDetector;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700175 ScreenHeadFusion mScreenHeadFusion;
176 ModeSelector mModeSelector;
177 PoseRateLimiter mRateLimiter;
178};
179
180} // namespace
181
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700182std::unique_ptr<HeadTrackingProcessor> createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700183 const HeadTrackingProcessor::Options& options, HeadTrackingMode initialMode) {
184 return std::make_unique<HeadTrackingProcessorImpl>(options, initialMode);
185}
186
187} // namespace media
188} // namespace android