blob: dd2244addf9b356df1e63660c71b9e1fdfe36046 [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-Tsvicbee7d42021-06-15 00:39:31 -070049 mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout},
50 initialMode),
51 mRateLimiter(PoseRateLimiter::Options{
52 .maxTranslationalVelocity = options.maxTranslationalVelocity,
53 .maxRotationalVelocity = options.maxRotationalVelocity}) {}
54
55 void setDesiredMode(HeadTrackingMode mode) override { mModeSelector.setDesiredMode(mode); }
56
57 void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead,
58 const Twist3f& headTwist) override {
59 Pose3f predictedWorldToHead =
60 worldToHead * integrate(headTwist, mOptions.predictionDuration);
61 mHeadPoseDriftCompensator.setInput(timestamp, predictedWorldToHead);
62 mWorldToHeadTimestamp = timestamp;
63 }
64
65 void setWorldToScreenPose(int64_t timestamp, const Pose3f& worldToScreen) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070066 if (mPhysicalToLogicalAngle != mPendingPhysicalToLogicalAngle) {
67 // We're introducing an artificial discontinuity. Enable the rate limiter.
68 mRateLimiter.enable();
69 mPhysicalToLogicalAngle = mPendingPhysicalToLogicalAngle;
70 }
71
72 mScreenPoseDriftCompensator.setInput(
73 timestamp, worldToScreen * Pose3f(rotateY(-mPhysicalToLogicalAngle)));
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070074 mWorldToScreenTimestamp = timestamp;
75 }
76
77 void setScreenToStagePose(const Pose3f& screenToStage) override {
78 mModeSelector.setScreenToStagePose(screenToStage);
79 }
80
81 void setDisplayOrientation(float physicalToLogicalAngle) override {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -070082 mPendingPhysicalToLogicalAngle = physicalToLogicalAngle;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070083 }
84
85 void calculate(int64_t timestamp) override {
86 if (mWorldToHeadTimestamp.has_value()) {
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -080087 Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
88 mHeadStillnessDetector.setInput(mWorldToHeadTimestamp.value(), worldToHead);
89 // Auto-recenter.
90 if (mHeadStillnessDetector.calculate(timestamp)) {
91 recenter(true, false);
92 worldToHead = mHeadPoseDriftCompensator.getOutput();
93 }
94
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070095 mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
96 mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
97 }
98
99 if (mWorldToScreenTimestamp.has_value()) {
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -0700100 const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput();
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700101 mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
102 worldToLogicalScreen);
103 }
104
105 auto maybeScreenToHead = mScreenHeadFusion.calculate();
106 if (maybeScreenToHead.has_value()) {
107 mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
108 maybeScreenToHead->pose);
109 } else {
110 mModeSelector.setScreenToHeadPose(timestamp, std::nullopt);
111 }
112
113 HeadTrackingMode prevMode = mModeSelector.getActualMode();
114 mModeSelector.calculate(timestamp);
115 if (mModeSelector.getActualMode() != prevMode) {
116 // Mode has changed, enable rate limiting.
117 mRateLimiter.enable();
118 }
119 mRateLimiter.setTarget(mModeSelector.getHeadToStagePose());
120 mHeadToStagePose = mRateLimiter.calculatePose(timestamp);
121 }
122
123 Pose3f getHeadToStagePose() const override { return mHeadToStagePose; }
124
125 HeadTrackingMode getActualMode() const override { return mModeSelector.getActualMode(); }
126
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700127 void recenter(bool recenterHead, bool recenterScreen) override {
128 if (recenterHead) {
129 mHeadPoseDriftCompensator.recenter();
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800130 mHeadStillnessDetector.reset();
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700131 }
132 if (recenterScreen) {
133 mScreenPoseDriftCompensator.recenter();
134 }
135
136 // If a sensor being recentered is included in the current mode, apply rate limiting to
137 // avoid discontinuities.
138 HeadTrackingMode mode = mModeSelector.getActualMode();
139 if ((recenterHead && (mode == HeadTrackingMode::WORLD_RELATIVE ||
140 mode == HeadTrackingMode::SCREEN_RELATIVE)) ||
141 (recenterScreen && mode == HeadTrackingMode::SCREEN_RELATIVE)) {
142 mRateLimiter.enable();
143 }
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700144 }
145
146 private:
147 const Options mOptions;
148 float mPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvi7901bdd2021-09-08 16:11:07 -0700149 // We store the physical to logical angle as "pending" until the next world-to-screen sample it
150 // applies to arrives.
151 float mPendingPhysicalToLogicalAngle = 0;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700152 std::optional<int64_t> mWorldToHeadTimestamp;
153 std::optional<int64_t> mWorldToScreenTimestamp;
154 Pose3f mHeadToStagePose;
155 PoseDriftCompensator mHeadPoseDriftCompensator;
156 PoseDriftCompensator mScreenPoseDriftCompensator;
Ytai Ben-Tsvi44e7c3d2021-12-15 16:04:01 -0800157 StillnessDetector mHeadStillnessDetector;
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700158 ScreenHeadFusion mScreenHeadFusion;
159 ModeSelector mModeSelector;
160 PoseRateLimiter mRateLimiter;
161};
162
163} // namespace
164
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700165std::unique_ptr<HeadTrackingProcessor> createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700166 const HeadTrackingProcessor::Options& options, HeadTrackingMode initialMode) {
167 return std::make_unique<HeadTrackingProcessorImpl>(options, initialMode);
168}
169
170} // namespace media
171} // namespace android