blob: 797322b0e96d4244a976d176bf03cb2f8249bd25 [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 "src/ui/scenic/lib/gfx/resources/camera.h"
#include "src/ui/lib/escher/util/type_utils.h"
#include "src/ui/scenic/lib/gfx/util/unwrap.h"
namespace scenic_impl {
namespace gfx {
const ResourceTypeInfo Camera::kTypeInfo = {ResourceType::kCamera, "Camera"};
Camera::Camera(Session* session, SessionId session_id, ResourceId id, ScenePtr scene)
: Resource(session, session_id, id, Camera::kTypeInfo), scene_(std::move(scene)) {}
Camera::Camera(Session* session, SessionId session_id, ResourceId id, ScenePtr scene,
const ResourceTypeInfo& type_info)
: Resource(session, session_id, id, type_info), scene_(std::move(scene)) {}
void Camera::SetTransform(const glm::vec3& eye_position, const glm::vec3& eye_look_at,
const glm::vec3& eye_up) {
eye_position_ = eye_position;
eye_look_at_ = eye_look_at;
eye_up_ = eye_up;
}
void Camera::SetProjection(const float fovy) { fovy_ = fovy; }
void Camera::SetClipSpaceTransform(const glm::vec2& translation, float scale) {
if (translation == glm::vec2() && scale == 1) {
has_clip_space_transform_ = false;
} else {
has_clip_space_transform_ = true;
// clang-format off
clip_space_transform_ = {
scale, 0, 0, 0,
0, scale, 0, 0,
0, 0, 1, 0,
translation.x, translation.y, 0, 1
};
// clang-format on
}
}
void Camera::SetPoseBuffer(fxl::RefPtr<Buffer> buffer, uint32_t num_entries, zx::time base_time,
zx::duration time_interval) {
pose_buffer_ = buffer;
num_entries_ = num_entries;
base_time_ = base_time;
time_interval_ = time_interval;
}
escher::Camera Camera::GetEscherCamera(const escher::ViewingVolume& volume) const {
escher::Camera camera(glm::mat4(1), glm::mat4(1));
if (fovy_ == 0.f) {
camera = escher::Camera::NewOrtho(volume,
has_clip_space_transform_ ? &clip_space_transform_ : nullptr);
} else {
camera = escher::Camera::NewPerspective(
volume, glm::lookAt(eye_position_, eye_look_at_, eye_up_), fovy_,
has_clip_space_transform_ ? &clip_space_transform_ : nullptr);
}
return camera;
}
escher::hmd::PoseBuffer Camera::GetEscherPoseBuffer() const {
return pose_buffer_ ? escher::hmd::PoseBuffer(pose_buffer_->escher_buffer(), num_entries_,
base_time_.get(), time_interval_.get())
: escher::hmd::PoseBuffer(); // NOTE: has operator bool
}
std::pair<escher::ray4, escher::mat4> Camera::ProjectRay(
const escher::ray4& ray, const escher::ViewingVolume& viewing_volume) const {
auto camera = GetEscherCamera(viewing_volume);
// This screen transform shifts the x, y from [-1, 1] to [0, 1]. Therefore,
// when we invert it below, it takes the input coords into Vulkan normalized
// device coordinates.
auto scale = glm::scale(glm::vec3(0.5f, 0.5f, -1.f));
auto translate = glm::translate(glm::vec3(1.f, 1.f, 0.f));
auto device_transform = scale * translate;
// This operation can be thought of as constructing the ray originating at the
// camera's position, that passes through a point on the near plane.
//
// First the constructed near/mid points get passed through the inverse of the
// device transform. After that the projection gets "undone," and finally the
// camera transform is taken into account.
//
// For more information about world, view, and projection matrices, and how
// they can be used for ray picking, see:
// http://www.codinglabs.net/article_world_view_projection_matrix.aspx
// https://stackoverflow.com/questions/2093096/implementing-ray-picking
auto inverse_vp = glm::inverse(device_transform * camera.projection() * camera.transform());
return {inverse_vp * ray, inverse_vp};
}
} // namespace gfx
} // namespace scenic_impl