1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/vr/elements/viewport_aware_root.h"
#include <cmath>
#include "base/numerics/angle_conversions.h"
#include "chrome/browser/vr/pose_util.h"
namespace vr {
namespace {
bool ElementHasVisibleChildren(UiElement* element) {
for (auto& child : element->children()) {
// Note that we do NOT use IsVisible here. IsVisible takes inherited opacity
// into consideration. However, the parent element (the viewport aware root
// element) might be invisible at first due to opacity animation, which
// makes all children becomes invisible even if it has children that become
// visible immediately when animation starts.
if (child->opacity() > 0.f) {
if (child->draw_phase() != kPhaseNone)
return true;
if (ElementHasVisibleChildren(child.get()))
return true;
}
}
return false;
}
} // namespace
// static
const float ViewportAwareRoot::kViewportRotationTriggerDegrees = 55.0f;
ViewportAwareRoot::ViewportAwareRoot() {
SetTransitionedProperties({OPACITY});
}
ViewportAwareRoot::~ViewportAwareRoot() = default;
bool ViewportAwareRoot::OnBeginFrame(const gfx::Transform& head_pose) {
// head_pose is head_from_world. Invert to get world_from_head.
gfx::Vector3dF look_at = vr::GetForwardVector(head_pose);
bool changed = AdjustRotationForHeadPose(look_at);
if (recenter_on_rotate_) {
// Pose data has been validated already.
gfx::Transform world_from_head = head_pose.GetCheckedInverse();
gfx::Point3F head_pos_in_world_space =
world_from_head.MapPoint(gfx::Point3F());
changed = AdjustTranslation(head_pos_in_world_space.x(),
head_pos_in_world_space.z(), changed);
}
return changed;
}
bool ViewportAwareRoot::AdjustTranslation(float head_in_world_x,
float head_in_world_z,
bool did_rotate) {
gfx::Point3F center_point_in_world =
LocalTransform().MapPoint(gfx::Point3F());
gfx::Vector2dF offset = {head_in_world_x - center_point_in_world.x(),
head_in_world_z - center_point_in_world.z()};
const float kMinTranslationLength =
1.2f; // If you move 1.2m, we'll recenter.
if (did_rotate || offset.Length() > kMinTranslationLength) {
SetTranslate(head_in_world_x, center_point_in_world.y(), head_in_world_z);
return true;
}
return false;
}
bool ViewportAwareRoot::AdjustRotationForHeadPose(
const gfx::Vector3dF& look_at) {
DCHECK(!look_at.IsZero());
bool rotated = false;
bool has_visible_children = HasVisibleChildren();
if (has_visible_children && !children_visible_) {
Reset();
rotated = true;
}
children_visible_ = has_visible_children;
if (!children_visible_)
return false;
gfx::Vector3dF rotated_center_vector =
LocalTransform().MapVector(gfx::Vector3dF(0.f, 0.f, -1.0f));
gfx::Vector3dF top_projected_look_at{look_at.x(), 0.f, look_at.z()};
float degrees = gfx::ClockwiseAngleBetweenVectorsInDegrees(
top_projected_look_at, rotated_center_vector, {0.f, 1.0f, 0.f});
if (degrees <= kViewportRotationTriggerDegrees ||
degrees >= 360.0f - kViewportRotationTriggerDegrees) {
return rotated;
}
viewport_aware_total_rotation_ += degrees;
viewport_aware_total_rotation_ = fmod(viewport_aware_total_rotation_, 360.0f);
SetRotate(0.f, 1.f, 0.f, base::DegToRad(viewport_aware_total_rotation_));
// Immediately hide the element.
SetVisibleImmediately(false);
// Fade it back in.
SetVisible(true);
return true;
}
void ViewportAwareRoot::Reset() {
viewport_aware_total_rotation_ = 0.f;
x_center = 0;
z_center = 0;
SetRotate(0.f, 1.f, 0.f, base::DegToRad(viewport_aware_total_rotation_));
}
bool ViewportAwareRoot::HasVisibleChildren() {
return ElementHasVisibleChildren(this);
}
} // namespace vr
|