blob: 0b67bd9010677c2b1b513b4e9f31462f2d623b9c [file] [log] [blame]
// 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 "garnet/public/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(MZ-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(MZ-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(MZ-194): Define these properties somewhere better (perhaps Scene?)
// instead of hardcoding them here.
constexpr float kTop = 1000;
constexpr float kBottom = 0;
return escher::ViewingVolume(size_.x, size_.y, kTop, kBottom);
}
} // namespace gfx
} // namespace scenic_impl