Skip to content

Commit

Permalink
feat(rendering): add generic camera component to hold the projection …
Browse files Browse the repository at this point in the history
…matrix of a specific camera
  • Loading branch information
mkuritsu committed Sep 27, 2024
1 parent d69f034 commit 3e28551
Show file tree
Hide file tree
Showing 15 changed files with 140 additions and 90 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Binary Serializer and Deserializer (#1306, **@RiscadoA**).
- Type Client and Type Server (#1302, **@RiscadoA**).
- Deadzone for input axis (#844, **@kuukitenshi**)
- Generic Camera component to hold projection matrix (#1331, **@mkuritsu**)

### Fixed

Expand Down
1 change: 1 addition & 0 deletions engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ set(CUBOS_ENGINE_SOURCE
"src/render/camera/plugin.cpp"
"src/render/camera/perspective_camera.cpp"
"src/render/camera/draws_to.cpp"
"src/render/camera/camera.cpp"
"src/render/voxels/plugin.cpp"
"src/render/voxels/load.cpp"
"src/render/voxels/grid.cpp"
Expand Down
28 changes: 28 additions & 0 deletions engine/include/cubos/engine/render/camera/camera.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/// @file
/// @brief Component @ref cubos::engine::Camera
/// @ingroup render-camera-plugin

#pragma once

#include <glm/mat4x4.hpp>

#include <cubos/core/reflection/reflect.hpp>

#include <cubos/engine/api.hpp>

namespace cubos::engine
{
/// @brief Generic component to hold the projection matrix of a specific camera (either perspective or orthogonal).
/// @note Added automatically once a specific camera is added (e.g @ref cubos::engine::PerspectiveCamera).
/// @ingroup render-camera-plugin
struct CUBOS_ENGINE_API Camera
{
CUBOS_REFLECT;

/// @brief Whether the camera is drawing to a target.
bool active{true};

/// @brief Projection matrix of the camera.
glm::mat4 projection{};
};
} // namespace cubos::engine
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ namespace cubos::engine
{
CUBOS_REFLECT;

/// @brief Whether the camera is drawing to a target.
bool active = true;

/// @brief Vertical field of view in degrees.
float fovY = 60.0F;

Expand Down
16 changes: 4 additions & 12 deletions engine/src/gizmos/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

#include <cubos/engine/gizmos/plugin.hpp>
#include <cubos/engine/gizmos/target.hpp>
#include <cubos/engine/render/camera/camera.hpp>
#include <cubos/engine/render/camera/draws_to.hpp>
#include <cubos/engine/render/camera/perspective_camera.hpp>
#include <cubos/engine/render/camera/plugin.hpp>
#include <cubos/engine/render/picker/picker.hpp>
#include <cubos/engine/render/picker/plugin.hpp>
Expand Down Expand Up @@ -106,7 +106,7 @@ void cubos::engine::gizmosPlugin(Cubos& cubos)
.tagged(drawToRenderPickerTag)
.call([](Gizmos& gizmos, GizmosRenderer& gizmosRenderer, const DeltaTime& deltaTime,
Query<Entity, RenderTarget&, RenderPicker&, GizmosTarget&> targets,
Query<const LocalToWorld&, const PerspectiveCamera&, const DrawsTo&> cameras) {
Query<const LocalToWorld&, const Camera&, const DrawsTo&> cameras) {
auto& rd = *gizmosRenderer.renderDevice;
auto orthoVP =
glm::translate(glm::mat4(1.0F), glm::vec3(-1.0F, -1.0F, 0.0F)) * glm::ortho(-0.5F, 0.5F, -0.5F, 0.5F);
Expand Down Expand Up @@ -156,11 +156,7 @@ void cubos::engine::gizmosPlugin(Cubos& cubos)
static_cast<int>(drawsTo.viewportSize.y * static_cast<float>(target.size.y)));

// Prepare camera projection (TODO: fetch this matrix from a Camera component).
auto worldVP = glm::perspective(glm::radians(camera.fovY),
(drawsTo.viewportSize.x * static_cast<float>(target.size.x)) /
(drawsTo.viewportSize.y * static_cast<float>(target.size.y)),
camera.zNear, camera.zFar) *
glm::inverse(localToWorld.mat);
auto worldVP = camera.projection * glm::inverse(localToWorld.mat);

// Draw world-space gizmos.
rd.setDepthStencilState(gizmosRenderer.doDepthCheckStencilState);
Expand Down Expand Up @@ -209,11 +205,7 @@ void cubos::engine::gizmosPlugin(Cubos& cubos)
static_cast<int>(drawsTo.viewportSize.y * static_cast<float>(target.size.y)));

// Prepare camera projection (TODO: fetch this matrix from a Camera component).
auto worldVP = glm::perspective(glm::radians(camera.fovY),
(drawsTo.viewportSize.x * static_cast<float>(target.size.x)) /
(drawsTo.viewportSize.y * static_cast<float>(target.size.y)),
camera.zNear, camera.zFar) *
glm::inverse(localToWorld.mat);
auto worldVP = camera.projection * glm::inverse(localToWorld.mat);

// Draw world-space gizmos.
rd.setDepthStencilState(gizmosRenderer.doDepthCheckStencilState);
Expand Down
13 changes: 13 additions & 0 deletions engine/src/render/camera/camera.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <cubos/core/ecs/reflection.hpp>
#include <cubos/core/reflection/external/glm.hpp>
#include <cubos/core/reflection/external/primitives.hpp>

#include <cubos/engine/render/camera/camera.hpp>

CUBOS_REFLECT_IMPL(cubos::engine::Camera)
{
return core::ecs::TypeBuilder<Camera>("cubos::engine::Camera")
.withField("active", &Camera::active)
.withField("projection", &Camera::projection)
.build();
}
1 change: 1 addition & 0 deletions engine/src/render/camera/draws_to.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
CUBOS_REFLECT_IMPL(cubos::engine::DrawsTo)
{
return core::ecs::TypeBuilder<DrawsTo>("cubos::engine::DrawsTo")
.tree()
.withField("viewportOffset", &DrawsTo::viewportOffset)
.withField("viewportSize", &DrawsTo::viewportSize)
.build();
Expand Down
1 change: 0 additions & 1 deletion engine/src/render/camera/perspective_camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
CUBOS_REFLECT_IMPL(cubos::engine::PerspectiveCamera)
{
return core::ecs::TypeBuilder<PerspectiveCamera>("cubos::engine::PerspectiveCamera")
.withField("active", &PerspectiveCamera::active)
.withField("fovY", &PerspectiveCamera::fovY)
.withField("zNear", &PerspectiveCamera::zNear)
.withField("zFar", &PerspectiveCamera::zFar)
Expand Down
34 changes: 34 additions & 0 deletions engine/src/render/camera/plugin.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,44 @@
#include <glm/gtc/matrix_transform.hpp>

#include <cubos/engine/render/camera/camera.hpp>
#include <cubos/engine/render/camera/draws_to.hpp>
#include <cubos/engine/render/camera/perspective_camera.hpp>
#include <cubos/engine/render/camera/plugin.hpp>
#include <cubos/engine/render/target/plugin.hpp>
#include <cubos/engine/render/target/target.hpp>

using namespace cubos::engine;

void cubos::engine::cameraPlugin(Cubos& cubos)
{
cubos.depends(renderTargetPlugin);

cubos.component<PerspectiveCamera>();
cubos.component<Camera>();

cubos.relation<DrawsTo>();

cubos.observer("add Camera on add PerspectiveCamera")
.onAdd<PerspectiveCamera>()
.without<Camera>()
.call([](Commands cmds, Query<Entity> query) {
for (auto [ent] : query)
{
cmds.add(ent, Camera{});
}
});

cubos.system("update Camera projection by PerspectiveCamera")
.call([](Query<Camera&, const PerspectiveCamera&, const DrawsTo&, const RenderTarget&> query) {
for (auto [camera, perspective, drawsTo, target] : query)
{
float aspect = (static_cast<float>(target.size.x) * drawsTo.viewportSize.x) /
(static_cast<float>(target.size.y) * drawsTo.viewportSize.y);
if (aspect > 0)
{
camera.projection =
glm::perspective(glm::radians(perspective.fovY), aspect, perspective.zNear, perspective.zFar);
}
}
});
}
12 changes: 4 additions & 8 deletions engine/src/render/deferred_shading/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#include <cubos/core/io/window.hpp>

#include <cubos/engine/assets/plugin.hpp>
#include <cubos/engine/render/camera/camera.hpp>
#include <cubos/engine/render/camera/draws_to.hpp>
#include <cubos/engine/render/camera/perspective_camera.hpp>
#include <cubos/engine/render/camera/plugin.hpp>
#include <cubos/engine/render/deferred_shading/deferred_shading.hpp>
#include <cubos/engine/render/deferred_shading/plugin.hpp>
Expand Down Expand Up @@ -177,13 +177,13 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos)
Query<const LocalToWorld&, const PointLight&> pointLights,
Query<const LocalToWorld&, const SpotLight&, Opt<const SpotShadowCaster&>> spotLights,
Query<Entity, const HDR&, const GBuffer&, const SSAO&, DeferredShading&> targets,
Query<const LocalToWorld&, const PerspectiveCamera&, const DrawsTo&> perspectiveCameras) {
Query<const LocalToWorld&, const Camera&, const DrawsTo&> cameras) {
auto& rd = window->renderDevice();

for (auto [targetEnt, hdr, gBuffer, ssao, deferredShading] : targets)
{
// Find the cameras that draw to the GBuffer.
for (auto [localToWorld, camera, drawsTo] : perspectiveCameras.pin(1, targetEnt))
for (auto [localToWorld, camera, drawsTo] : cameras.pin(1, targetEnt))
{
if (!camera.active)
{
Expand Down Expand Up @@ -213,11 +213,7 @@ void cubos::engine::deferredShadingPlugin(Cubos& cubos)
// Fill the PerScene constant buffer.
PerScene perScene{};
perScene.inverseView = localToWorld.mat;
perScene.inverseProjection =
glm::inverse(glm::perspective(glm::radians(camera.fovY),
(float(gBuffer.size.x) * drawsTo.viewportSize.x) /
(float(gBuffer.size.y) * drawsTo.viewportSize.y),
camera.zNear, camera.zFar));
perScene.inverseProjection = glm::inverse(camera.projection);

perScene.ambientLight = glm::vec4(environment.ambient, 1.0F);
perScene.skyGradient[0] = glm::vec4(environment.skyGradient[0], 1.0F);
Expand Down
13 changes: 5 additions & 8 deletions engine/src/render/g_buffer_rasterizer/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#include <cubos/core/reflection/external/uuid.hpp>

#include <cubos/engine/assets/plugin.hpp>
#include <cubos/engine/render/camera/camera.hpp>
#include <cubos/engine/render/camera/draws_to.hpp>
#include <cubos/engine/render/camera/perspective_camera.hpp>
#include <cubos/engine/render/camera/plugin.hpp>
#include <cubos/engine/render/depth/depth.hpp>
#include <cubos/engine/render/depth/plugin.hpp>
Expand Down Expand Up @@ -142,10 +142,10 @@ void cubos::engine::gBufferRasterizerPlugin(Cubos& cubos)

cubos.system("rasterize to GBuffer")
.tagged(rasterizeToGBufferTag)
.with<PerspectiveCamera>()
.with<Camera>()
.related<DrawsTo>()
.call([](State& state, const Window& window, const RenderMeshPool& pool, const RenderPalette& palette,
Assets& assets, Query<const LocalToWorld&, PerspectiveCamera&, const DrawsTo&> perspectiveCameras,
Assets& assets, Query<const LocalToWorld&, Camera&, const DrawsTo&> cameras,
Query<Entity, GBufferRasterizer&, GBuffer&, RenderDepth&, RenderPicker&> targets,
Query<Entity, const LocalToWorld&, const RenderMesh&, const RenderVoxelGrid&> meshes) {
auto& rd = window->renderDevice();
Expand Down Expand Up @@ -233,7 +233,7 @@ void cubos::engine::gBufferRasterizerPlugin(Cubos& cubos)
}

// Find the active cameras for this target.
for (auto [cameraLocalToWorld, camera, drawsTo] : perspectiveCameras.pin(1, ent))
for (auto [cameraLocalToWorld, camera, drawsTo] : cameras.pin(1, ent))
{
// Skip inactive cameras.
if (!camera.active)
Expand All @@ -243,10 +243,7 @@ void cubos::engine::gBufferRasterizerPlugin(Cubos& cubos)

// Send the PerScene data to the GPU.
auto view = glm::inverse(cameraLocalToWorld.mat);
auto proj = glm::perspective(glm::radians(camera.fovY),
(float(gBuffer.size.x) * drawsTo.viewportSize.x) /
(float(gBuffer.size.y) * drawsTo.viewportSize.y),
camera.zNear, camera.zFar);
auto proj = camera.projection;
PerScene perScene{.viewProj = proj * view};
state.perSceneCB->fill(&perScene, sizeof(perScene));

Expand Down
66 changes: 32 additions & 34 deletions engine/src/render/split_screen/plugin.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <glm/vec2.hpp>

#include <cubos/engine/render/camera/camera.hpp>
#include <cubos/engine/render/camera/draws_to.hpp>
#include <cubos/engine/render/camera/perspective_camera.hpp>
#include <cubos/engine/render/camera/plugin.hpp>
#include <cubos/engine/render/split_screen/plugin.hpp>
#include <cubos/engine/render/split_screen/split_screen.hpp>
Expand Down Expand Up @@ -62,48 +62,46 @@ void cubos::engine::splitScreenPlugin(Cubos& cubos)

cubos.system("split screen for each DrawsTo relation")
.tagged(splitScreenTag)
.call(
[](Query<Entity, const RenderTarget&> targets,
Query<const LocalToWorld&, const PerspectiveCamera&, DrawsTo&, const SplitScreen&> perspectiveCameras) {
for (auto [targetEnt, target] : targets)
.call([](Query<Entity, const RenderTarget&> targets,
Query<const LocalToWorld&, const Camera&, DrawsTo&, const SplitScreen&> cameras) {
for (auto [targetEnt, target] : targets)
{
int cameraCount = 0;

// Find the cameras that draw to the target.
for (auto [localToWorld, camera, drawsTo, splitScreen] : cameras.pin(1, targetEnt))
{
int cameraCount = 0;
// Ignore unused argument warnings
(void)splitScreen;

// Find the cameras that draw to the target.
for (auto [localToWorld, camera, drawsTo, splitScreen] : perspectiveCameras.pin(1, targetEnt))
if (!camera.active)
{
// Ignore unused argument warnings
(void)splitScreen;
continue;
}

if (!camera.active)
{
continue;
}
cameraCount += 1;
}

cameraCount += 1;
}
std::vector<glm::ivec2> positions(static_cast<unsigned long>(cameraCount));
std::vector<glm::ivec2> sizes(static_cast<unsigned long>(cameraCount));

std::vector<glm::ivec2> positions(static_cast<unsigned long>(cameraCount));
std::vector<glm::ivec2> sizes(static_cast<unsigned long>(cameraCount));
setViewportCameras({0, 0}, target.size, cameraCount, positions.begin(), sizes.begin());

setViewportCameras({0, 0}, target.size, cameraCount, positions.begin(), sizes.begin());
unsigned long i = 0;
for (auto [localToWorld, camera, drawsTo, splitScreen] : cameras.pin(1, targetEnt))
{
// Ignore unused argument warnings
(void)splitScreen;

unsigned long i = 0;
for (auto [localToWorld, camera, drawsTo, splitScreen] : perspectiveCameras.pin(1, targetEnt))
if (!camera.active)
{
// Ignore unused argument warnings
(void)splitScreen;

if (!camera.active)
{
continue;
}

drawsTo.viewportOffset =
static_cast<glm::vec2>(positions[i]) / static_cast<glm::vec2>(target.size);
drawsTo.viewportSize = static_cast<glm::vec2>(sizes[i]) / static_cast<glm::vec2>(target.size);
i++;
continue;
}

drawsTo.viewportOffset = static_cast<glm::vec2>(positions[i]) / static_cast<glm::vec2>(target.size);
drawsTo.viewportSize = static_cast<glm::vec2>(sizes[i]) / static_cast<glm::vec2>(target.size);
i++;
}
});
}
});
}
11 changes: 4 additions & 7 deletions engine/src/render/ssao/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#include <cubos/core/io/window.hpp>

#include <cubos/engine/assets/plugin.hpp>
#include <cubos/engine/render/camera/camera.hpp>
#include <cubos/engine/render/camera/draws_to.hpp>
#include <cubos/engine/render/camera/perspective_camera.hpp>
#include <cubos/engine/render/camera/plugin.hpp>
#include <cubos/engine/render/g_buffer/g_buffer.hpp>
#include <cubos/engine/render/g_buffer/plugin.hpp>
Expand Down Expand Up @@ -146,7 +146,7 @@ void cubos::engine::ssaoPlugin(Cubos& cubos)
cubos.system("apply SSAO to the GBuffer and output to the SSAO texture")
.tagged(drawToSSAOTag)
.call([](State& state, const Window& window, Query<Entity, const GBuffer&, SSAO&> targets,
Query<const LocalToWorld&, const PerspectiveCamera&, const DrawsTo&> perspectiveCameras) {
Query<const LocalToWorld&, const Camera&, const DrawsTo&> cameras) {
auto& rd = window->renderDevice();

for (auto [targetEnt, gBuffer, ssao] : targets)
Expand Down Expand Up @@ -188,7 +188,7 @@ void cubos::engine::ssaoPlugin(Cubos& cubos)
}

// Find the cameras that draw to the SSAO target.
for (auto [localToWorld, camera, drawsTo] : perspectiveCameras.pin(1, targetEnt))
for (auto [localToWorld, camera, drawsTo] : cameras.pin(1, targetEnt))
{
if (!camera.active)
{
Expand Down Expand Up @@ -225,10 +225,7 @@ void cubos::engine::ssaoPlugin(Cubos& cubos)
// Fill the PerScene constant buffer.
PerScene perScene{};
perScene.view = glm::inverse(localToWorld.mat);
perScene.projection = glm::perspective(glm::radians(camera.fovY),
(float(gBuffer.size.x) * drawsTo.viewportSize.x) /
(float(gBuffer.size.y) * drawsTo.viewportSize.y),
camera.zNear, camera.zFar);
perScene.projection = camera.projection;
for (std::size_t i = 0; i < state.kernel.size(); ++i)
{
perScene.samples[i] = glm::vec4(state.kernel[i], 0.0F);
Expand Down
Loading

0 comments on commit 3e28551

Please sign in to comment.