Files
FGL-Engine/src/engine/camera/Camera.cpp
2024-10-04 05:58:00 -04:00

416 lines
13 KiB
C++

//
// Created by kj16609 on 11/28/23.
//
#include "Camera.hpp"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/string_cast.hpp>
#include <tracy/Tracy.hpp>
#include "CameraDescriptor.hpp"
#include "CameraInfo.hpp"
#include "CameraRenderer.hpp"
#include "CameraSwapchain.hpp"
namespace fgl::engine
{
inline static std::unique_ptr< CameraRenderer > camera_renderer;
Matrix< MatrixType::WorldToScreen > Camera::getProjectionViewMatrix() const
{
assert( projection_matrix != constants::MAT4_IDENTITY );
return projection_matrix * view_matrix;
}
void Camera::setOrthographicProjection( float left, float right, float top, float bottom, float near, float far )
{
projection_matrix = Matrix< MatrixType::CameraToScreen >( glm::ortho( left, right, bottom, top, near, far ) );
//TODO: Figure out frustum culling for orthographic projection. (If we even wanna use it)
}
FGL_FLATTEN_HOT void Camera::
setPerspectiveProjection( const float fovy, const float aspect, const float near, const float far )
{
projection_matrix = Matrix< MatrixType::CameraToScreen >( glm::perspective( fovy, aspect, near, far ) );
base_frustum = createFrustum( aspect, fovy, near, far );
}
Coordinate< CoordinateSpace::World > Camera::getPosition() const
{
//Should maybe store the inverse view matrix
return WorldCoordinate( inverse_view_matrix[ 3 ] );
}
void Camera::updateInfo( const FrameIndex frame_index )
{
ZoneScoped;
CameraInfo current_camera_info { .projection = getProjectionMatrix(),
.view = getViewMatrix(),
.inverse_view = getInverseViewMatrix() };
m_camera_frame_info[ frame_index ] = current_camera_info;
}
descriptors::DescriptorSet& Camera::getDescriptor( const FrameIndex index )
{
assert( index < m_camera_info_descriptors.size() );
return m_camera_info_descriptors[ index ];
}
void Camera::setFOV( const float fov_y )
{
m_fov_y = fov_y;
setPerspectiveProjection( m_fov_y, aspectRatio(), constants::NEAR_PLANE, constants::FAR_PLANE );
}
void Camera::pass( FrameInfo& frame_info )
{
ZoneScopedN( "Camera::pass" );
if ( m_cold && m_swapchain )
{
//TODO: Make some way to destroy the swapchain in a deffered manner.
m_old_swapchain = m_swapchain;
m_swapchain = nullptr;
m_active = false;
}
if ( !m_active ) return;
assert( frame_info.camera == nullptr );
frame_info.camera = this;
if ( m_swapchain->getExtent() != m_target_extent )
{
remakeSwapchain( m_target_extent );
}
updateInfo( frame_info.frame_idx );
camera_renderer->pass( frame_info, *m_swapchain );
frame_info.camera = nullptr;
}
vk::raii::RenderPass& Camera::getRenderpass()
{
return camera_renderer->getRenderpass();
}
CameraSwapchain& Camera::getSwapchain() const
{
return *m_swapchain;
}
void Camera::setViewport( const vk::raii::CommandBuffer& command_buffer )
{
vk::Viewport viewport {};
viewport.x = 0.0f;
viewport.y = 0.0f;
const auto& [ width, height ] = m_swapchain->getExtent();
viewport.width = static_cast< float >( width );
viewport.height = static_cast< float >( height );
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
const std::vector< vk::Viewport > viewports { viewport };
command_buffer.setViewport( 0, viewports );
}
void Camera::setScissor( const vk::raii::CommandBuffer& command_buffer )
{
const vk::Rect2D scissor { { 0, 0 }, m_swapchain->getExtent() };
const std::vector< vk::Rect2D > scissors { scissor };
command_buffer.setScissor( 0, scissors );
}
void Camera::remakeSwapchain( vk::Extent2D extent )
{
this->setPerspectiveProjection( m_fov_y, aspectRatio(), constants::NEAR_PLANE, constants::FAR_PLANE );
m_old_swapchain = m_swapchain;
m_swapchain = std::make_shared< CameraSwapchain >( camera_renderer->getRenderpass(), extent );
}
void Camera::setName( const std::string_view str )
{
name = str;
}
float Camera::aspectRatio() const
{
return m_swapchain->getAspectRatio();
}
void Camera::
copyOutput( const vk::raii::CommandBuffer& command_buffer, const FrameIndex frame_index, Image& target )
{
assert( m_swapchain->getExtent() == target.getExtent() );
Image& source { this->getSwapchain().getOutput( frame_index ) };
vk::ImageSubresourceRange range {};
range.aspectMask = vk::ImageAspectFlagBits::eColor;
range.baseMipLevel = 0;
range.levelCount = 1;
range.baseArrayLayer = 0;
range.layerCount = 1;
vk::ImageMemoryBarrier barrier_to_target {};
barrier_to_target.oldLayout = vk::ImageLayout::eUndefined;
barrier_to_target.newLayout = vk::ImageLayout::eTransferDstOptimal;
barrier_to_target.image = target.getVkImage();
barrier_to_target.subresourceRange = range;
barrier_to_target.srcAccessMask = vk::AccessFlagBits::eMemoryWrite;
barrier_to_target.dstAccessMask =
vk::AccessFlagBits::eTransferWrite | vk::AccessFlagBits::eColorAttachmentWrite;
barrier_to_target.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier_to_target.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
command_buffer.pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eTransfer | vk::PipelineStageFlagBits::eColorAttachmentOutput,
{},
{},
{},
{ barrier_to_target } );
vk::ImageMemoryBarrier barrier_from_source {};
barrier_from_source.oldLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier_from_source.newLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier_from_source.image = source.getVkImage();
barrier_from_source.subresourceRange = range;
barrier_from_source.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
barrier_from_source.dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite;
barrier_from_source.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier_from_source.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
command_buffer.pipelineBarrier(
vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eTransfer,
{},
{},
{},
{ barrier_from_source } );
vk::ImageCopy region {};
region.extent = vk::Extent3D( m_swapchain->getExtent(), 1 );
region.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
region.srcSubresource.layerCount = 1;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcOffset = vk::Offset3D( 0, 0, 0 );
region.dstOffset = region.srcOffset;
region.dstSubresource = region.srcSubresource;
command_buffer.copyImage(
source.getVkImage(),
vk::ImageLayout::eTransferSrcOptimal,
target.getVkImage(),
vk::ImageLayout::eTransferDstOptimal,
{ region } );
vk::ImageMemoryBarrier barrier_from_target {};
barrier_from_target.oldLayout = barrier_to_target.newLayout;
barrier_from_target.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier_from_target.image = target.getVkImage();
barrier_from_target.subresourceRange = range;
barrier_from_target.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier_from_target.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eInputAttachmentRead
| vk::AccessFlagBits::eColorAttachmentRead;
barrier_from_target.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier_from_target.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
command_buffer.pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader | vk::PipelineStageFlagBits::eColorAttachmentOutput,
{},
{},
{},
{ barrier_from_target } );
vk::ImageMemoryBarrier barrier_to_source {};
barrier_to_source.oldLayout = barrier_from_source.newLayout;
barrier_to_source.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier_to_source.image = source.getVkImage();
barrier_to_source.subresourceRange = range;
barrier_to_source.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier_to_source.dstAccessMask = vk::AccessFlagBits::eShaderRead;
barrier_to_source.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier_to_source.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
command_buffer.pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader,
{},
{},
{},
{ barrier_to_source } );
}
void Camera::updateMatrix()
{
const auto& [ pos, scale, rotation ] = m_transform;
const auto rotation_matrix { rotation.mat() };
const glm::vec3 forward { rotation_matrix * glm::vec4( constants::WORLD_FORWARD, 0.0f ) };
const glm::vec3 camera_up { rotation_matrix * glm::vec4( -constants::WORLD_Z, 0.0f ) };
const WorldCoordinate center_pos { pos + forward };
view_matrix = Matrix< MatrixType::WorldToCamera >( glm::lookAt( pos.vec(), center_pos.vec(), camera_up ) );
inverse_view_matrix = glm::inverse( view_matrix );
updateFrustum();
}
FGL_FLATTEN_HOT void Camera::setView( const WorldCoordinate pos, const Rotation& rotation, const ViewMode mode )
{
switch ( mode )
{
case ViewMode::TaitBryan:
{
m_transform.translation = pos;
m_transform.rotation = rotation;
updateMatrix();
break;
}
case ViewMode::Euler:
[[fallthrough]];
{
//TODO: Implement
//view_matrix = glm::lookAt(position, position + );
}
default:
throw std::runtime_error( "Unimplemented view mode" );
}
updateFrustum();
}
void Camera::updateFrustum()
{
last_frustum_pos = getPosition();
const Matrix< MatrixType::ModelToWorld > translation_matrix { frustumTranslationMatrix() };
frustum = translation_matrix * base_frustum;
}
const std::string& Camera::getName() const
{
return name;
}
void Camera::initCameraRenderer()
{
assert( !camera_renderer );
camera_renderer = std::make_unique< CameraRenderer >();
}
Camera::Camera( const vk::Extent2D extent, memory::Buffer& buffer ) :
m_transform(),
m_target_extent( extent ),
m_camera_frame_info( buffer, SwapChain::MAX_FRAMES_IN_FLIGHT ),
m_swapchain( std::make_shared< CameraSwapchain >( camera_renderer->getRenderpass(), m_target_extent ) ),
name()
{
this->setPerspectiveProjection( m_fov_y, aspectRatio(), constants::NEAR_PLANE, constants::FAR_PLANE );
this->setView( WorldCoordinate( constants::CENTER ), Rotation( 0.0f, 0.0f, 0.0f ) );
for ( std::uint8_t i = 0; i < SwapChain::MAX_FRAMES_IN_FLIGHT; ++i )
{
descriptors::DescriptorSet set { CameraDescriptorSet::createLayout() };
set.setMaxIDX( 0 );
set.bindUniformBuffer( 0, m_camera_frame_info[ i ] );
set.update();
m_camera_info_descriptors.emplace_back( std::move( set ) );
}
}
void Camera::setExtent( const vk::Extent2D extent )
{
m_target_extent = extent;
}
FrustumBase createFrustum( const float aspect, const float fov_y, const float near, const float far )
{
const Plane< CoordinateSpace::Model > near_plane { ModelCoordinate( constants::WORLD_FORWARD * near ),
NormalVector( constants::WORLD_FORWARD ) };
const Plane< CoordinateSpace::Model > far_plane { ModelCoordinate( constants::WORLD_FORWARD * far ),
NormalVector( -constants::WORLD_FORWARD ) };
const float half_height { far * glm::tan( fov_y / 2.0f ) };
const float half_width { half_height * aspect };
const ModelCoordinate far_forward { constants::WORLD_FORWARD * far };
const ModelCoordinate right_half { constants::WORLD_RIGHT * half_width };
const Vector right_forward { ( far_forward + right_half ).vec() };
const Vector left_forward { ( far_forward - right_half ).vec() };
const Plane< CoordinateSpace::Model > right_plane {
ModelCoordinate( constants::WORLD_CENTER ),
NormalVector( glm::cross( right_forward.vec(), constants::WORLD_Z_NEG ) )
};
const Plane< CoordinateSpace::Model > left_plane {
ModelCoordinate( constants::WORLD_CENTER ),
NormalVector( glm::cross( left_forward.vec(), constants::WORLD_Z ) )
};
const ModelCoordinate top_half { constants::WORLD_Z * half_height };
const Vector top_forward { ( far_forward + top_half ).vec() };
const Vector bottom_forward { ( far_forward - top_half ).vec() };
const Plane< CoordinateSpace::Model > top_plane {
ModelCoordinate( constants::WORLD_CENTER ),
NormalVector( glm::cross( top_forward.vec(), constants::WORLD_RIGHT ) )
};
const Plane< CoordinateSpace::Model > bottom_plane {
ModelCoordinate( constants::WORLD_CENTER ),
NormalVector( glm::cross( bottom_forward.vec(), -constants::WORLD_RIGHT ) )
};
return { near_plane,
far_plane,
top_plane,
bottom_plane,
right_plane,
left_plane,
Coordinate< CoordinateSpace::Model >( constants::WORLD_CENTER ) };
}
Matrix< MatrixType::ModelToWorld > Camera::frustumTranslationMatrix() const
{
return m_transform.mat();
}
WorldCoordinate Camera::getFrustumPosition() const
{
return last_frustum_pos;
}
Camera::~Camera()
{}
CameraIDX Camera::getIDX() const
{
return camera_idx;
}
} // namespace fgl::engine