blob: f2f15df9ab9b34d6237383a61d3ecbefdb7448ca [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{
Ytai Ben-Tsvi4cb1e482022-01-06 11:22:05 -080045 .defaultValue = false,
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -080046 .windowDuration = options.autoRecenterWindowDuration,
47 .translationalThreshold = options.autoRecenterTranslationalThreshold,
48 .rotationalThreshold = options.autoRecenterRotationalThreshold,
49 }),
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080050 mScreenStillnessDetector(StillnessDetector::Options{
Ytai Ben-Tsvi4cb1e482022-01-06 11:22:05 -080051 .defaultValue = true,
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080052 .windowDuration = options.screenStillnessWindowDuration,
53 .translationalThreshold = options.screenStillnessTranslationalThreshold,
54 .rotationalThreshold = options.screenStillnessRotationalThreshold,
55 }),
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070056 mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout},
57 initialMode),
58 mRateLimiter(PoseRateLimiter::Options{
59 .maxTranslationalVelocity = options.maxTranslationalVelocity,
60 .maxRotationalVelocity = options.maxRotationalVelocity}) {}
61
62 void setDesiredMode(HeadTrackingMode mode) override { mModeSelector.setDesiredMode(mode); }
63
64 void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead,
65 const Twist3f& headTwist) override {
66 Pose3f predictedWorldToHead =
67 worldToHead * integrate(headTwist, mOptions.predictionDuration);
68 mHeadPoseDriftCompensator.setInput(timestamp, predictedWorldToHead);
Ytai Ben-Tsvi5e6c0f32022-01-19 08:20:39 -080069 mHeadStillnessDetector.setInput(timestamp, predictedWorldToHead);
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070070 mWorldToHeadTimestamp = timestamp;
71 }
72
73 void setWorldToScreenPose(int64_t timestamp, const Pose3f& worldToScreen) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070074 if (mPhysicalToLogicalAngle != mPendingPhysicalToLogicalAngle) {
75 // We're introducing an artificial discontinuity. Enable the rate limiter.
76 mRateLimiter.enable();
77 mPhysicalToLogicalAngle = mPendingPhysicalToLogicalAngle;
78 }
79
Ytai Ben-Tsvi5e6c0f32022-01-19 08:20:39 -080080 Pose3f worldToLogicalScreen = worldToScreen * Pose3f(rotateY(-mPhysicalToLogicalAngle));
81 mScreenPoseDriftCompensator.setInput(timestamp, worldToLogicalScreen);
82 mScreenStillnessDetector.setInput(timestamp, worldToLogicalScreen);
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070083 mWorldToScreenTimestamp = timestamp;
84 }
85
86 void setScreenToStagePose(const Pose3f& screenToStage) override {
87 mModeSelector.setScreenToStagePose(screenToStage);
88 }
89
90 void setDisplayOrientation(float physicalToLogicalAngle) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070091 mPendingPhysicalToLogicalAngle = physicalToLogicalAngle;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070092 }
93
94 void calculate(int64_t timestamp) override {
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080095 // Handle the screen first, since it might trigger a recentering of the head.
96 if (mWorldToScreenTimestamp.has_value()) {
97 const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput();
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -080098 bool screenStable = mScreenStillnessDetector.calculate(timestamp);
99 mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
100 // Whenever the screen is unstable, recenter the head pose.
101 if (!screenStable) {
102 recenter(true, false);
103 }
104 mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
105 worldToLogicalScreen);
106 }
107
108 // Handle head.
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700109 if (mWorldToHeadTimestamp.has_value()) {
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800110 Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800111 // Auto-recenter.
112 if (mHeadStillnessDetector.calculate(timestamp)) {
113 recenter(true, false);
114 worldToHead = mHeadPoseDriftCompensator.getOutput();
115 }
116
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700117 mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
118 mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
119 }
120
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700121 auto maybeScreenToHead = mScreenHeadFusion.calculate();
122 if (maybeScreenToHead.has_value()) {
123 mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
124 maybeScreenToHead->pose);
125 } else {
126 mModeSelector.setScreenToHeadPose(timestamp, std::nullopt);
127 }
128
129 HeadTrackingMode prevMode = mModeSelector.getActualMode();
130 mModeSelector.calculate(timestamp);
131 if (mModeSelector.getActualMode() != prevMode) {
132 // Mode has changed, enable rate limiting.
133 mRateLimiter.enable();
134 }
135 mRateLimiter.setTarget(mModeSelector.getHeadToStagePose());
136 mHeadToStagePose = mRateLimiter.calculatePose(timestamp);
137 }
138
139 Pose3f getHeadToStagePose() const override { return mHeadToStagePose; }
140
141 HeadTrackingMode getActualMode() const override { return mModeSelector.getActualMode(); }
142
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700143 void recenter(bool recenterHead, bool recenterScreen) override {
144 if (recenterHead) {
145 mHeadPoseDriftCompensator.recenter();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800146 mHeadStillnessDetector.reset();
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700147 }
148 if (recenterScreen) {
149 mScreenPoseDriftCompensator.recenter();
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800150 mScreenStillnessDetector.reset();
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700151 }
152
153 // If a sensor being recentered is included in the current mode, apply rate limiting to
154 // avoid discontinuities.
155 HeadTrackingMode mode = mModeSelector.getActualMode();
156 if ((recenterHead && (mode == HeadTrackingMode::WORLD_RELATIVE ||
157 mode == HeadTrackingMode::SCREEN_RELATIVE)) ||
158 (recenterScreen && mode == HeadTrackingMode::SCREEN_RELATIVE)) {
159 mRateLimiter.enable();
160 }
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700161 }
162
163 private:
164 const Options mOptions;
165 float mPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -0700166 // We store the physical to logical angle as "pending" until the next world-to-screen sample it
167 // applies to arrives.
168 float mPendingPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700169 std::optional<int64_t> mWorldToHeadTimestamp;
170 std::optional<int64_t> mWorldToScreenTimestamp;
171 Pose3f mHeadToStagePose;
172 PoseDriftCompensator mHeadPoseDriftCompensator;
173 PoseDriftCompensator mScreenPoseDriftCompensator;
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800174 StillnessDetector mHeadStillnessDetector;
Ytai Ben-Tsvic7e8a482021-12-20 13:26:34 -0800175 StillnessDetector mScreenStillnessDetector;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700176 ScreenHeadFusion mScreenHeadFusion;
177 ModeSelector mModeSelector;
178 PoseRateLimiter mRateLimiter;
179};
180
181} // namespace
182
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700183std::unique_ptr<HeadTrackingProcessor> createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700184 const HeadTrackingProcessor::Options& options, HeadTrackingMode initialMode) {
185 return std::make_unique<HeadTrackingProcessorImpl>(options, initialMode);
186}
187
188} // namespace media
189} // namespace android