blob: c748db15100d6b2459697743abe3e9e528d41091 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/scenic/lib/input/helper.h"
#include "src/ui/lib/escher/util/type_utils.h"
#include "src/ui/scenic/lib/gfx/resources/compositor/layer.h"
#include "src/ui/scenic/lib/gfx/resources/compositor/layer_stack.h"
namespace scenic_impl {
namespace input {
using fuchsia::ui::input::PointerEvent;
escher::ray4 CreateScreenPerpendicularRay(float x, float y) {
// We set the elevation for the origin point, and Z value for the direction,
// such that we start above the scene and point into the scene.
//
// For hit testing, these values work in conjunction with
// Camera::ProjectRayIntoScene to create an appropriate ray4 that works
// correctly with the hit tester.
//
// TODO(38389): Scenic used to surface left-handed z, so |layer.cc| contains vestigial logic that
// flips z. As such, "taking a step back" translates to "positive Z origin" and "look at the
// scene" translates to "negative Z direction". We should be able to remove that flip and restore
// Vulkan's z-in semantics. Similarly since hit testing originates from the camera it should not
// be necessary to step back from the camera for the hit ray.
//
// During hit testing, we translate an arbitrary pointer's (x,y) device-space
// coordinates to a View's (x', y') model-space coordinates.
return {
.origin = {x, y, 1, 1}, // Origin as homogeneous point.
.direction = {0, 0, -1, 0},
};
}
PointerEvent ClonePointerWithCoords(const PointerEvent& event, const escher::vec2& coords) {
PointerEvent clone;
fidl::Clone(event, &clone);
clone.x = coords.x;
clone.y = coords.y;
return clone;
}
escher::vec2 PointerCoords(const PointerEvent& event) { return {event.x, event.y}; }
escher::vec2 TransformPointerCoords(const escher::vec2& pointer, const glm::mat4 transform) {
const escher::ray4 screen_ray = CreateScreenPerpendicularRay(pointer.x, pointer.y);
const escher::ray4 local_ray = transform * screen_ray;
// We treat distance as 0 to simplify; otherwise the formula is:
// hit = homogenize(local_ray.origin + distance * local_ray.direction);
escher::vec2 hit(escher::homogenize(local_ray.origin));
FXL_VLOG(2) << "Coordinate transform (device->view): (" << screen_ray.origin.x << ", "
<< screen_ray.origin.y << ")->(" << hit.x << ", " << hit.y << ")";
return hit;
}
// Finds (Vulkan) normalized device coordinates with respect to the (single) layer. This is intended
// for magnification gestures.
escher::vec2 NormalizePointerCoords(const escher::vec2& pointer,
const gfx::LayerStackPtr& layer_stack) {
if (layer_stack->layers().empty()) {
return {0, 0};
}
// RootPresenter only owns one layer per presentation/layer stack. To support multiple layers,
// we'd need to partition the input space. So, for now to simplify things we'll treat the layer
// size as display dimensions, and if we ever find more than one layer in a stack, we should
// worry.
FXL_DCHECK(layer_stack->layers().size() == 1)
<< "Multiple GFX layers; multi-layer input dispatch not implemented.";
const gfx::Layer& layer = **layer_stack->layers().begin();
return {
layer.width() > 0 ? 2.f * pointer.x / layer.width() - 1 : 0,
layer.height() > 0 ? 2.f * pointer.y / layer.height() - 1 : 0,
};
}
PointerEvent BuildLocalPointerEvent(const PointerEvent& pointer_event, const glm::mat4& transform) {
return ClonePointerWithCoords(pointer_event,
TransformPointerCoords(PointerCoords(pointer_event), transform));
}
} // namespace input
} // namespace scenic_impl