| // Copyright 2017 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 "garnet/lib/ui/gfx/resources/compositor/layer.h" |
| |
| #include "garnet/lib/ui/gfx/engine/hit_tester.h" |
| #include "garnet/lib/ui/gfx/resources/camera.h" |
| #include "garnet/lib/ui/gfx/resources/compositor/layer_stack.h" |
| #include "garnet/lib/ui/gfx/resources/renderers/renderer.h" |
| #include "garnet/lib/ui/scenic/util/error_reporter.h" |
| #include "src/ui/lib/escher/util/type_utils.h" |
| |
| namespace scenic_impl { |
| namespace gfx { |
| |
| const ResourceTypeInfo Layer::kTypeInfo = {ResourceType::kLayer, "Layer"}; |
| |
| Layer::Layer(Session* session, ResourceId id) |
| : Resource(session, id, Layer::kTypeInfo), translation_(0) {} |
| |
| Layer::~Layer() = default; |
| |
| bool Layer::SetRenderer(RendererPtr renderer) { |
| // TODO(SCN-249): if layer content is already specified as an image, clear it |
| // before setting the renderer. Or call it an error, and require the client |
| // to explicitly clear it first. |
| renderer_ = std::move(renderer); |
| return true; |
| } |
| |
| bool Layer::SetSize(const escher::vec2& size) { |
| if (size.x <= 0 || size.y <= 0) { |
| if (size != escher::vec2(0, 0)) { |
| error_reporter()->ERROR() |
| << "scenic::gfx::Layer::SetSize(): size must be positive"; |
| return false; |
| } |
| } |
| size_ = size; |
| return true; |
| } |
| |
| bool Layer::SetColor(const escher::vec4& color) { |
| color_ = color; |
| return true; |
| } |
| |
| bool Layer::Detach() { |
| if (layer_stack_) { |
| layer_stack_->RemoveLayer(this); |
| FXL_DCHECK(!layer_stack_); // removed by RemoveLayer(). |
| } |
| return true; |
| } |
| |
| void Layer::CollectScenes(std::set<Scene*>* scenes_out) { |
| if (renderer_ && renderer_->camera() && renderer_->camera()->scene()) { |
| scenes_out->insert(renderer_->camera()->scene().get()); |
| } |
| } |
| |
| bool Layer::IsDrawable() const { |
| if (size_ == escher::vec2(0, 0)) { |
| return false; |
| } |
| |
| // TODO(SCN-249): Layers can also have a material or image pipe. |
| return renderer_ && renderer_->camera() && renderer_->camera()->scene(); |
| } |
| |
| std::vector<Hit> Layer::HitTest(const escher::ray4& ray, |
| HitTester* hit_tester) const { |
| FXL_CHECK(hit_tester); |
| |
| Camera* camera = renderer()->camera(); |
| |
| if (width() == 0.f || height() == 0.f) { |
| return std::vector<Hit>(); |
| } |
| |
| // Normalize the origin of the ray with respect to the width and height of the |
| // layer before passing it to the camera. |
| escher::mat4 layer_normalization = |
| glm::scale(glm::vec3(1.f / width(), 1.f / height(), 1.f)); |
| |
| auto local_ray = layer_normalization * ray; |
| |
| // Transform the normalized ray by the camera's transformation. |
| std::pair<escher::ray4, escher::mat4> camera_projection_pair = |
| camera->ProjectRayIntoScene(local_ray, GetViewingVolume()); |
| |
| std::vector<Hit> hits = |
| hit_tester->HitTest(camera->scene().get(), camera_projection_pair.first); |
| |
| escher::mat4 inverse_layer_transform = |
| glm::inverse(camera_projection_pair.second * layer_normalization); |
| |
| // Take the camera's transformation into account; after this the hit's |
| // inverse_transform goes from the passed in ray's coordinate system to the |
| // hit nodes' coordinate system. |
| for (auto& hit : hits) { |
| hit.inverse_transform = |
| inverse_layer_transform * glm::inverse(hit.inverse_transform); |
| } |
| |
| return hits; |
| } |
| |
| escher::ViewingVolume Layer::GetViewingVolume() const { |
| // TODO(SCN-1276): Don't hardcode Z bounds in multiple locations. |
| constexpr float kTop = -1000; |
| constexpr float kBottom = 0; |
| return escher::ViewingVolume(size_.x, size_.y, kTop, kBottom); |
| } |
| |
| } // namespace gfx |
| } // namespace scenic_impl |