blob: ae5567db5907d2fe31fa75504ea878716d8454c7 [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 <gtest/gtest.h>
20
21#include "QuaternionUtil.h"
22#include "TestUtil.h"
23
24namespace android {
25namespace media {
26namespace {
27
28using Eigen::Quaternionf;
29using Eigen::Vector3f;
30using Options = HeadTrackingProcessor::Options;
31
32TEST(HeadTrackingProcessor, Initial) {
33 for (auto mode : {HeadTrackingMode::STATIC, HeadTrackingMode::WORLD_RELATIVE,
34 HeadTrackingMode::SCREEN_RELATIVE}) {
35 std::unique_ptr<HeadTrackingProcessor> processor =
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -070036 createHeadTrackingProcessor(Options{}, mode);
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070037 processor->calculate(0);
38 EXPECT_EQ(processor->getActualMode(), HeadTrackingMode::STATIC);
39 EXPECT_EQ(processor->getHeadToStagePose(), Pose3f());
40 }
41}
42
43TEST(HeadTrackingProcessor, BasicComposition) {
44 const Pose3f worldToHead{{1, 2, 3}, Quaternionf::UnitRandom()};
45 const Pose3f worldToScreen{{4, 5, 6}, Quaternionf::UnitRandom()};
46 const Pose3f screenToStage{{7, 8, 9}, Quaternionf::UnitRandom()};
47 const float physicalToLogical = M_PI_2;
48
49 std::unique_ptr<HeadTrackingProcessor> processor =
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -070050 createHeadTrackingProcessor(Options{}, HeadTrackingMode::SCREEN_RELATIVE);
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070051 processor->setWorldToHeadPose(0, worldToHead, Twist3f());
52 processor->setWorldToScreenPose(0, worldToScreen);
53 processor->setScreenToStagePose(screenToStage);
54 processor->setDisplayOrientation(physicalToLogical);
55 processor->calculate(0);
56 ASSERT_EQ(processor->getActualMode(), HeadTrackingMode::SCREEN_RELATIVE);
57 EXPECT_EQ(processor->getHeadToStagePose(), worldToHead.inverse() * worldToScreen *
58 Pose3f(rotateY(-physicalToLogical)) *
59 screenToStage);
60
61 processor->setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
62 processor->calculate(0);
63 ASSERT_EQ(processor->getActualMode(), HeadTrackingMode::WORLD_RELATIVE);
64 EXPECT_EQ(processor->getHeadToStagePose(), worldToHead.inverse() * screenToStage);
65
66 processor->setDesiredMode(HeadTrackingMode::STATIC);
67 processor->calculate(0);
68 ASSERT_EQ(processor->getActualMode(), HeadTrackingMode::STATIC);
69 EXPECT_EQ(processor->getHeadToStagePose(), screenToStage);
70}
71
72TEST(HeadTrackingProcessor, Prediction) {
73 const Pose3f worldToHead{{1, 2, 3}, Quaternionf::UnitRandom()};
74 const Twist3f headTwist{{4, 5, 6}, quaternionToRotationVector(Quaternionf::UnitRandom()) / 10};
75 const Pose3f worldToScreen{{4, 5, 6}, Quaternionf::UnitRandom()};
76
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -070077 std::unique_ptr<HeadTrackingProcessor> processor = createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -070078 Options{.predictionDuration = 2.f}, HeadTrackingMode::WORLD_RELATIVE);
79 processor->setWorldToHeadPose(0, worldToHead, headTwist);
80 processor->setWorldToScreenPose(0, worldToScreen);
81 processor->calculate(0);
82 ASSERT_EQ(processor->getActualMode(), HeadTrackingMode::WORLD_RELATIVE);
83 EXPECT_EQ(processor->getHeadToStagePose(), (worldToHead * integrate(headTwist, 2.f)).inverse());
84
85 processor->setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE);
86 processor->calculate(0);
87 ASSERT_EQ(processor->getActualMode(), HeadTrackingMode::SCREEN_RELATIVE);
88 EXPECT_EQ(processor->getHeadToStagePose(),
89 (worldToHead * integrate(headTwist, 2.f)).inverse() * worldToScreen);
90
91 processor->setDesiredMode(HeadTrackingMode::STATIC);
92 processor->calculate(0);
93 ASSERT_EQ(processor->getActualMode(), HeadTrackingMode::STATIC);
94 EXPECT_EQ(processor->getHeadToStagePose(), Pose3f());
95}
96
97TEST(HeadTrackingProcessor, SmoothModeSwitch) {
98 const Pose3f targetHeadToWorld = Pose3f({4, 0, 0}, rotateZ(M_PI / 2));
99
Ytai Ben-Tsviedbab3d2021-08-16 11:27:52 -0700100 std::unique_ptr<HeadTrackingProcessor> processor = createHeadTrackingProcessor(
Ytai Ben-Tsvicbee7d42021-06-15 00:39:31 -0700101 Options{.maxTranslationalVelocity = 1}, HeadTrackingMode::STATIC);
102
103 processor->calculate(0);
104
105 processor->setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
106 processor->setWorldToHeadPose(0, targetHeadToWorld.inverse(), Twist3f());
107
108 // We're expecting a gradual move to the target.
109 processor->calculate(0);
110 EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, processor->getActualMode());
111 EXPECT_EQ(processor->getHeadToStagePose(), Pose3f());
112
113 processor->calculate(2);
114 EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, processor->getActualMode());
115 EXPECT_EQ(processor->getHeadToStagePose(), Pose3f({2, 0, 0}, rotateZ(M_PI / 4)));
116
117 processor->calculate(4);
118 EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, processor->getActualMode());
119 EXPECT_EQ(processor->getHeadToStagePose(), targetHeadToWorld);
120
121 // Now that we've reached the target, we should no longer be rate limiting.
122 processor->setWorldToHeadPose(4, Pose3f(), Twist3f());
123 processor->calculate(5);
124 EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, processor->getActualMode());
125 EXPECT_EQ(processor->getHeadToStagePose(), Pose3f());
126}
127
128} // namespace
129} // namespace media
130} // namespace android