blob: 3f1a18d755cf3df2f04e7d073fdf3c8ebd7cc5e2 [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"
23
24namespace android {
25namespace media {
26namespace {
27
28using Eigen::Quaternionf;
29using Eigen::Vector3f;
30
31class HeadTrackingProcessorImpl : public HeadTrackingProcessor {
32 public:
33 HeadTrackingProcessorImpl(const Options& options, HeadTrackingMode initialMode)
34 : mOptions(options),
35 mHeadPoseDriftCompensator(PoseDriftCompensator::Options{
36 .translationalDriftTimeConstant = options.translationalDriftTimeConstant,
37 .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant,
38 }),
39 mScreenPoseDriftCompensator(PoseDriftCompensator::Options{
40 .translationalDriftTimeConstant = options.translationalDriftTimeConstant,
41 .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant,
42 }),
43 mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout},
44 initialMode),
45 mRateLimiter(PoseRateLimiter::Options{
46 .maxTranslationalVelocity = options.maxTranslationalVelocity,
47 .maxRotationalVelocity = options.maxRotationalVelocity}) {}
48
49 void setDesiredMode(HeadTrackingMode mode) override { mModeSelector.setDesiredMode(mode); }
50
51 void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead,
52 const Twist3f& headTwist) override {
53 Pose3f predictedWorldToHead =
54 worldToHead * integrate(headTwist, mOptions.predictionDuration);
55 mHeadPoseDriftCompensator.setInput(timestamp, predictedWorldToHead);
56 mWorldToHeadTimestamp = timestamp;
57 }
58
59 void setWorldToScreenPose(int64_t timestamp, const Pose3f& worldToScreen) override {
60 mScreenPoseDriftCompensator.setInput(timestamp, worldToScreen);
61 mWorldToScreenTimestamp = timestamp;
62 }
63
64 void setScreenToStagePose(const Pose3f& screenToStage) override {
65 mModeSelector.setScreenToStagePose(screenToStage);
66 }
67
68 void setDisplayOrientation(float physicalToLogicalAngle) override {
69 if (mPhysicalToLogicalAngle != physicalToLogicalAngle) {
70 mRateLimiter.enable();
71 }
72 mPhysicalToLogicalAngle = physicalToLogicalAngle;
73 }
74
75 void calculate(int64_t timestamp) override {
76 if (mWorldToHeadTimestamp.has_value()) {
77 const Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
78 mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
79 mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
80 }
81
82 if (mWorldToScreenTimestamp.has_value()) {
83 const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput() *
84 Pose3f(rotateY(-mPhysicalToLogicalAngle));
85 mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
86 worldToLogicalScreen);
87 }
88
89 auto maybeScreenToHead = mScreenHeadFusion.calculate();
90 if (maybeScreenToHead.has_value()) {
91 mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
92 maybeScreenToHead->pose);
93 } else {
94 mModeSelector.setScreenToHeadPose(timestamp, std::nullopt);
95 }
96
97 HeadTrackingMode prevMode = mModeSelector.getActualMode();
98 mModeSelector.calculate(timestamp);
99 if (mModeSelector.getActualMode() != prevMode) {
100 // Mode has changed, enable rate limiting.
101 mRateLimiter.enable();
102 }
103 mRateLimiter.setTarget(mModeSelector.getHeadToStagePose());
104 mHeadToStagePose = mRateLimiter.calculatePose(timestamp);
105 }
106
107 Pose3f getHeadToStagePose() const override { return mHeadToStagePose; }
108
109 HeadTrackingMode getActualMode() const override { return mModeSelector.getActualMode(); }
110
Ytai Ben-Tsvi95b00c82021-08-27 15:27:08 -0700111 void recenter(bool recenterHead, bool recenterScreen) override {
112 if (recenterHead) {
113 mHeadPoseDriftCompensator.recenter();
114 }
115 if (recenterScreen) {
116 mScreenPoseDriftCompensator.recenter();
117 }
118
119 // If a sensor being recentered is included in the current mode, apply rate limiting to
120 // avoid discontinuities.
121 HeadTrackingMode mode = mModeSelector.getActualMode();
122 if ((recenterHead && (mode == HeadTrackingMode::WORLD_RELATIVE ||
123 mode == HeadTrackingMode::SCREEN_RELATIVE)) ||
124 (recenterScreen && mode == HeadTrackingMode::SCREEN_RELATIVE)) {
125 mRateLimiter.enable();
126 }
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700127 }
128
129 private:
130 const Options mOptions;
131 float mPhysicalToLogicalAngle = 0;
132 std::optional<int64_t> mWorldToHeadTimestamp;
133 std::optional<int64_t> mWorldToScreenTimestamp;
134 Pose3f mHeadToStagePose;
135 PoseDriftCompensator mHeadPoseDriftCompensator;
136 PoseDriftCompensator mScreenPoseDriftCompensator;
137 ScreenHeadFusion mScreenHeadFusion;
138 ModeSelector mModeSelector;
139 PoseRateLimiter mRateLimiter;
140};
141
142} // namespace
143
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700144std::unique_ptr<HeadTrackingProcessor> createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700145 const HeadTrackingProcessor::Options& options, HeadTrackingMode initialMode) {
146 return std::make_unique<HeadTrackingProcessorImpl>(options, initialMode);
147}
148
149} // namespace media
150} // namespace android