#include "Camera.hpp"
#include "Debug.hpp"
#include "GlState.hpp"
#include "Transform.hpp"
#include "UniformBuffer.hpp"
#include <wren/camera.h>
#include <cmath>
namespace wren {
static primitive::Aabb computeAabb(const glm::vec3 &position, float nearDistance) {
const glm::vec3 nearVector(nearDistance);
return primitive::Aabb(position - nearVector, position + nearVector);
}
void Camera::setDirection(const glm::vec3 &direction) {
assert(glm::length(direction) > glm::epsilon<float>());
const glm::vec3 orig = orientation() * -gVec3UnitZ;
const glm::vec3 d = glm::normalize(direction);
glm::quat rotation = glm::rotation(orig, d);
applyRotation(rotation);
}
const primitive::Aabb &Camera::aabb() {
if (mAabbDirty) {
mNearAabb = computeAabb(TransformNode::position(), mNear);
mAabbDirty = false;
}
return mNearAabb;
}
void Camera::updateUniforms() const {
update();
glstate::uniformBuffer(WR_GLSL_LAYOUT_UNIFORM_BUFFER_CAMERA_TRANSFORMS)->writeValue(&mMatrices);
}
bool Camera::isAabbVisible(const primitive::Aabb &aabb) const {
return mFrustum.isInside(aabb);
}
bool Camera::isBoundingSphereVisible(const primitive::Sphere &boundingSphere) const {
return mFrustum.isInside(boundingSphere);
}
void Camera::updateFromParent() {
if (mParent->isMatrixDirty()) {
TransformNode::updateFromParent();
mIsViewDirty = true;
}
}
void Camera::update() const {
const bool updateFrustum = mIsViewDirty || mIsProjectionDirty;
updateView();
updateProjection();
if (updateFrustum) {
const glm::dmat4 dpView = mMatrices.mView;
mFrustum.recomputeFromMatrix(mDpProjectionMatrix * dpView);
}
}
Camera::Camera() :
mIsViewDirty(true),
mIsProjectionDirty(true),
mProjectionMode(WR_CAMERA_PROJECTION_MODE_PERSPECTIVE),
mAspectRatio(1.0f),
mNear(0.05f),
mFar(100.0f),
mFovy(glm::quarter_pi<float>()),
mHalfHeight(0.5f),
mFlipY(false),
mAabbDirty(false) {
mNearAabb = computeAabb(TransformNode::position(), mNear);
}
void Camera::updateView() const {
if (!mIsViewDirty)
return;
mUp = orientation() * gVec3UnitY;
mForward = orientation() * -gVec3UnitZ;
mRight = glm::cross(mForward, mUp);
mMatrices.mView = glm::lookAt(position(), position() + mForward, mUp);
mIsViewDirty = false;
}
static glm::mat4 infinitePerspective(float fovy, float aspectRatio, float near) {
const float tanHalfFovy = tanf(fovy / 2.0f);
glm::mat4 projection = glm::mat4(0.0f);
projection[0][0] = 1.0f / (aspectRatio * tanHalfFovy);
projection[1][1] = 1.0f / tanHalfFovy;
projection[2][2] = -1.0f;
projection[2][3] = -1.0f;
projection[3][2] = -near;
return projection;
}
void Camera::updateProjection() const {
if (!mIsProjectionDirty)
return;
if (mProjectionMode == WR_CAMERA_PROJECTION_MODE_PERSPECTIVE) {
mDpProjectionMatrix =
glm::perspective(static_cast<double>(mFovy), static_cast<double>(mAspectRatio), static_cast<double>(mNear),
static_cast<double>(mFar)); mMatrices.mProjection = mDpProjectionMatrix;
mMatrices.mInfiniteProjection = infinitePerspective(mFovy, mAspectRatio, mNear); } else {
mMatrices.mProjection =
glm::ortho(-mHalfHeight * mAspectRatio, mHalfHeight * mAspectRatio, -mHalfHeight, mHalfHeight, mNear, mFar);
mMatrices.mInfiniteProjection = mMatrices.mProjection;
}
if (mFlipY) {
mMatrices.mProjection = glm::scale(mMatrices.mProjection, glm::vec3(1.0f, -1.0f, 1.0f));
mMatrices.mInfiniteProjection = glm::scale(mMatrices.mInfiniteProjection, glm::vec3(1.0f, -1.0f, 1.0f));
}
mIsProjectionDirty = false;
}
}
WrCamera *wr_camera_new() {
return reinterpret_cast<WrCamera *>(wren::Camera::createCamera());
}
void wr_camera_set_projection_mode(WrCamera *camera, WrCameraProjectionMode mode) {
reinterpret_cast<wren::Camera *>(camera)->setProjectionMode(mode);
}
void wr_camera_set_aspect_ratio(WrCamera *camera, float ratio) {
reinterpret_cast<wren::Camera *>(camera)->setAspectRatio(ratio);
}
void wr_camera_set_near(WrCamera *camera, float near_distance) {
reinterpret_cast<wren::Camera *>(camera)->setNear(near_distance);
}
void wr_camera_set_far(WrCamera *camera, float far_distance) {
reinterpret_cast<wren::Camera *>(camera)->setFar(far_distance);
}
void wr_camera_set_fovy(WrCamera *camera, float fovy) {
reinterpret_cast<wren::Camera *>(camera)->setFovy(fovy);
}
void wr_camera_set_height(WrCamera *camera, float height) {
reinterpret_cast<wren::Camera *>(camera)->setHeight(height);
}
void wr_camera_set_position(WrCamera *camera, float *position) {
reinterpret_cast<wren::Camera *>(camera)->setPosition(glm::make_vec3(position));
}
void wr_camera_set_orientation(WrCamera *camera, float *angle_axis) {
reinterpret_cast<wren::Camera *>(camera)->setOrientation(glm::angleAxis(angle_axis[0], glm::make_vec3(&angle_axis[1])));
}
void wr_camera_set_flip_y(WrCamera *camera, bool flip_y) {
return reinterpret_cast<wren::Camera *>(camera)->setFlipY(flip_y);
}
void wr_camera_apply_yaw(WrCamera *camera, float angle) {
reinterpret_cast<wren::Camera *>(camera)->applyYaw(angle);
}
void wr_camera_apply_pitch(WrCamera *camera, float angle) {
reinterpret_cast<wren::Camera *>(camera)->applyPitch(angle);
}
void wr_camera_apply_roll(WrCamera *camera, float angle) {
reinterpret_cast<wren::Camera *>(camera)->applyRoll(angle);
}
float wr_camera_get_near(WrCamera *camera) {
return reinterpret_cast<wren::Camera *>(camera)->nearDistance();
}
float wr_camera_get_far(WrCamera *camera) {
return reinterpret_cast<wren::Camera *>(camera)->farDistance();
}
float wr_camera_get_fovy(WrCamera *camera) {
return reinterpret_cast<wren::Camera *>(camera)->fovy();
}
float wr_camera_get_height(WrCamera *camera) {
return reinterpret_cast<wren::Camera *>(camera)->height();
}