1#include "CameraSystem.h"
3#include "Components/Core/TransformComponent.h"
5#include "Renderer/CullingManager.h"
6#include "Renderer/IRenderer.h"
7#include "Renderer/RenderTexture.h"
9#include "Resources/Texture.h"
11#include "Systems/Core/TransformSystem.h"
12#include "Systems/Core/EnvironmentSystem.h"
14#include "Scene/GetBounds.h"
16#include "Platform/Instrumentation.h"
21#include "Rendering/IContext.h"
23#include "Foundation/ComponentModel/Entity.h"
24#include "Foundation/ComponentModel/Component.h"
25#include "Foundation/Geometry/BoundingBox.hpp"
26#include "Foundation/Logging/Logger.h"
46 data.camera = component;
53 for (
auto& cameraComponent : pool) {
54 RenderTexture* sourceTexture = resources->getRenderTexture(cameraComponent.renderTexture);
55 RenderTexture* destTexture = resources->getRenderTexture(cameraComponent.resolveTarget);
57 if (sourceTexture && destTexture) {
58 IContext* gfxContext = context->device->getImmediateContext();
60 assert(cameraComponent.renderTexture->description.width == cameraComponent.resolveTarget->description.width);
61 assert(cameraComponent.renderTexture->description.height == cameraComponent.resolveTarget->description.height);
63 if (cameraComponent.renderTexture->description.samples > 1) {
64 gfxContext->
resolveResource(sourceTexture->textureHandle, destTexture->textureHandle);
67 gfxContext->copyResource(destTexture->textureHandle, sourceTexture->textureHandle);
76 for (
auto & cameraComponent : pool) {
77 auto & cameraData = getData(&cameraComponent);
79 if (!std::isfinite(cameraComponent.nearPlaneLimit) ||
80 !std::isfinite(cameraComponent.farPlaneLimit) ||
81 (cameraComponent.farDistance <= cameraComponent.nearPlaneLimit))
83 LOG_ERROR(logger,
"Broken camera invariants, nearPlaneLimit=%f, setting to 1.0, farPlaneLimit=%f, setting to 100000.0",
84 cameraComponent.nearPlaneLimit,
85 cameraComponent.farPlaneLimit);
86 cameraComponent.nearPlaneLimit = 1.f;
87 cameraComponent.nearPlaneLimit = 100000.f;
91 if (cameraComponent.environment) {
92 cameraData.environment = cameraComponent.environment->getComponentHandle<
EnvironmentComponent>();
93 }
else if (
auto * env = context->environmentSystem->getGlobalEnvironment(); env) {
100 if (cameraComponent.supportsPicking() && &cameraComponent != mainCamera.resolve()) {
101 if (cameraData.rayPickId == -1) {
102 cameraData.rayPickId = nextRayPickId++;
106 auto transformComponent = cameraComponent.getContainer()->getComponent<
TransformComponent>();
108 if (!transformComponent) {
109 LOG_ERROR(logger,
"Camera %s missing transform component.", cameraComponent.getContainer()->getName().c_str());
113 cameraData.exposure = cameraComponent.exposure;
114 cameraData.layerMask = cameraComponent.layerMask;
115 cameraData.lightingMask = cameraComponent.lightingMask;
116 cameraData.viewportSize = cameraComponent.viewportSize;
117 cameraData.viewportOrigin = cameraComponent.viewportOrigin;
118 cameraData.viewMatrix = glm::inverse(context->transformSystem->getLocalToWorld(transformComponent));
119 cameraData.inverseViewMatrix = context->transformSystem->getLocalToWorld(transformComponent);
122 cameraData.clearColor = cameraComponent.clearColor;
123 cameraData.useClearColor =
true;
126 cameraData.useClearColor =
false;
128 if (cameraData.viewportSize.x == 0 &&
HandleIsValid(cameraComponent.renderTexture)) {
129 auto cameraTexture = cameraComponent.renderTexture;
131 cameraData.viewportSize = { cameraTexture->description.width, cameraTexture->description.height };
134 cameraData.discardThreshold = 4.f*(cameraComponent.discardThreshold) / (cameraData.viewportSize.x*cameraData.viewportSize.y);
135 cameraData.keepThreshold = cameraComponent.keepThreshold;
138 for (
auto & cameraComponent : pool) {
139 updateClippingPlanes(context, cameraComponent,
false);
140 updateProjection(context, cameraComponent);
149 for (
auto & cameraComponent : pool) {
150 if (updateClippingPlanes(context, cameraComponent,
true)) {
151 updateProjection(context, cameraComponent);
155 base::postUpdate(context);
160 auto & cameraData = getData(&cameraComponent);
162 float aspect = cameraData.viewportSize.y == 0 ? 1.0f : cameraData.viewportSize.x / cameraData.viewportSize.y;
165 const float height = cameraComponent.
orthoHeight / 2.0f;
167 cameraData.rawProjectionMatrix = glm::ortho(-height * aspect, height * aspect,
169 cameraData.nearDistance,
170 cameraData.farDistance);
172 cameraData.fieldOfView = cameraComponent.
fieldOfView;
173 cameraData.rawProjectionMatrix = glm::perspective(cameraComponent.
fieldOfView,
175 cameraData.nearDistance,
176 cameraData.farDistance);
179 if (glm::any(glm::notEqual(cameraComponent.
subsetMin, glm::vec2(0.f)) || glm::notEqual(cameraComponent.
subsetMax, glm::vec2(1.f)))) {
180 const auto& a = cameraComponent.
subsetMin;
181 const auto& b = cameraComponent.
subsetMax;
182 glm::vec2 s = glm::vec2(2.f) / (b - a);
183 if (std::isfinite(s.x) && std::isfinite(s.y)) {
184 glm::vec2 o = s * (glm::vec2(1) - 2.f * a) - glm::vec2(1);
185 cameraData.rawProjectionMatrix = glm::mat4(s.x, 0.f, 0.f, 0.f,
188 o.x, o.y, 0.f, 1.f) * cameraData.rawProjectionMatrix;
191 LOG_WARNING(logger,
"Invalid camera frustum subset [%f,%f]x[%f,%f], ignoring.", a.x, a.y, b.x, b.y);
197 static glm::mat4 flip(
203 cameraData.flipWindingOrder =
true;
204 cameraData.rawProjectionMatrix = flip * cameraData.rawProjectionMatrix;
207 cameraData.flipWindingOrder =
false;
210 cameraData.prevViewProjection = cameraData.viewProjection;
211 cameraData.rawViewProjection = cameraData.rawProjectionMatrix * cameraData.viewMatrix;
212 cameraData.rawViewCullMatrix = cameraData.rawViewProjection;
214 cameraData.viewProjection = cameraData.projectionMatrix * cameraData.viewMatrix;
215 cameraData.inverseViewProjectionMatrix = glm::inverse(cameraData.viewProjection);
216 cameraData.inverseProjectionMatrix = glm::inverse(cameraData.projectionMatrix);
219 cameraData.clientFlags = cameraComponent.
clientFlags;
220 cameraData.frustum = Geometry::calculateFrustum<Geometry::Frustum, glm::mat4>(cameraData.viewProjection);
225 CpuInstrumentationScope(SCOPE_SYSTEMS,
"CameraSystem::updateClippingPlanes");
227 bool tightEstimate = context->
variables->get(
"camera.tightBounds",
false);
228 auto & cameraData = getData(&cameraComponent);
230 cameraData.sceneBounds = Geometry::BoundingBox();
233 context->
bounds->getSceneBounds(context,
reinterpret_cast<float *
>(&cameraData.sceneBounds), cameraComponent.
layerMask & ~RenderLayers::Sky);
234 cameraData.sceneBounds = Bounds::getTransformedBounds(cameraData.sceneBounds, cameraData.viewMatrix);
236 float nearDist = std::numeric_limits<float>::max();
237 float farDist = -std::numeric_limits<float>::max();
239 if (tightEstimate && secondPass) {
240 CpuInstrumentationScope(SCOPE_SYSTEMS,
"CameraSystem::getCameraDepthBounds");
241 context->
bounds->getCameraDepthBounds(context, nearDist, farDist, cameraComponent);
242 }
else if (!isEmpty(cameraData.sceneBounds)) {
243 context->
bounds->getClipPlanes(context, nearDist, farDist, cameraComponent, cameraData.viewMatrix);
247 const float kMinRangeFactor = 0.0001f;
249 if(nearDist == std::numeric_limits<float>::max() && farDist == -std::numeric_limits<float>::max()){
255 farDist = glm::clamp(farDist, glm::max(nearDist * (1 + kMinRangeFactor), nearDist + 1), cameraComponent.
farPlaneLimit);
257 assert(nearDist < farDist);
259 const float calculationSlack = 0.001f;
261 const float newNearPlane = nearDist * (1.0f - calculationSlack);
262 const float newFarPlane = farDist * (1.0f + calculationSlack);
264 if (newNearPlane != cameraData.nearDistance || newFarPlane != cameraData.farDistance) {
265 cameraData.nearDistance = newNearPlane;
266 cameraData.farDistance = newFarPlane;
269 if (tightEstimate && secondPass){
270 if(cameraData.nearDepthBounds != newNearPlane || cameraData.farDepthBounds != newFarPlane){
271 context->
engine->setDirty();
272 cameraData.nearDepthBounds = newNearPlane;
273 cameraData.farDepthBounds = newFarPlane;
277 if(cameraData.nearClipPlanes != newNearPlane || cameraData.farClipPlanes != newFarPlane){
278 context->
engine->setDirty();
279 cameraData.nearClipPlanes = newNearPlane;
280 cameraData.farClipPlanes = newFarPlane;
286 if (cameraComponent.
nearDistance != cameraData.nearDistance || cameraComponent.
farDistance != cameraData.farDistance) {
288 cameraData.farDistance = cameraComponent.
farDistance;
290 context->
engine->setDirty();
ComponentHandle getComponentHandle() const
CameraFlags flags
Camera behavior flags.
float fieldOfView
Vertical field of view, given in radians.
uint32_t clientFlags
Application defined, camera specific flags passed to shaders.
float farPlaneLimit
Largest value allowed to adjust far plane to.
bool enableClippingPlaneAdjustment
If automatic adjustment of the clipping planes should be performed to fit as much of the scene as pos...
glm::vec2 subsetMin
Subset of the frustum to view (for only rendering a subset of the view covered by the viewport),...
ProjectionMode projectionMode
The projection mode to use for the camera.
RenderLayers layerMask
Layer mask used to determine which RenderComponent instances should be visible in this cameras viewpo...
float nearPlaneLimit
Smallest value allowed to adjust near plane to.
glm::vec2 subsetMax
Subset of the frustum to view (for only rendering a subset of the view covered by the viewport),...
float orthoHeight
Height of the viewport in scene units when using orthographic projection.
void postRender(const Context *context)
Called by the renderer after all rendering is complete.
ComponentHandle createComponent() override
bool updateClippingPlanes(Context *context, const CameraComponent &cameraComponent, bool secondPass)
Update near/far clipping plane calculations.
void updateProjection(Context *context, const CameraComponent &cameraComponent)
Update the projection matrix and derived matrices of a camera.
virtual ComponentHandle createComponent()
Create a new component instance.
void postUpdate()
Perform post update logic in the system.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class CullingManager > cullingManager
CullingManager instance.
class IRenderer * renderer
Renderer.
std::unique_ptr< class Bounds > bounds
Bounds service instance.
std::unique_ptr< class Variables > variables
Variables service instance.
std::unique_ptr< class Engine > engine
Engine instance.
virtual glm::mat4 getViewFromViewportMatrix(const glm::mat4 inverseProjectionMatrix)=0
Get an adjusted inverse projection matrix mainly used in post processing.
virtual glm::mat4 getProjectionMatrix(const glm::mat4 projectionMatrix)=0
Get an adjusted projection matrix used to render.
virtual IRenderResources * getResources()=0
Get the render resources interface.
Contains render resources used by the renderer.
Log implementation class.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
@ OverrideClearColor
Provide a clear colour for this camera to use.
@ Orthographic
Orthographic projection.
Contains geometry calculations and generation.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
constexpr size_t fnv1a(uint8_t data, size_t hashValue) noexcept
Hashes a single byte using the fnv1a algorithm.
Handle to a Component instance.
static ComponentHandle Empty()
Returns an empty, invalid handle. Will evaluate to false if tested against using operator bool().
COGSCORE_DLL_API void updateHash()
Represents a graphics device context which can receive rendering commands.
virtual void resolveResource(TextureHandle source, TextureHandle destination)=0
Resolves the given source resource target into the given destination texture.