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);
218 cameraData.viewportFromViewMatrix = glm::inverse(cameraData.viewFromViewportMatrix);
220 cameraData.clientFlags = cameraComponent.
clientFlags;
221 cameraData.frustum = Geometry::calculateFrustum<Geometry::Frustum, glm::mat4>(cameraData.viewProjection);
226 CpuInstrumentationScope(SCOPE_SYSTEMS,
"CameraSystem::updateClippingPlanes");
228 bool tightEstimate = context->
variables->get(
"camera.tightBounds",
false);
229 auto & cameraData = getData(&cameraComponent);
231 cameraData.sceneBounds = Geometry::BoundingBox();
234 context->
bounds->getSceneBounds(context,
reinterpret_cast<float *
>(&cameraData.sceneBounds), cameraComponent.
layerMask & ~RenderLayers::Sky);
235 cameraData.sceneBounds = Bounds::getTransformedBounds(cameraData.sceneBounds, cameraData.viewMatrix);
237 float nearDist = std::numeric_limits<float>::max();
238 float farDist = -std::numeric_limits<float>::max();
240 if (tightEstimate && secondPass) {
241 CpuInstrumentationScope(SCOPE_SYSTEMS,
"CameraSystem::getCameraDepthBounds");
242 context->
bounds->getCameraDepthBounds(context, nearDist, farDist, cameraComponent);
243 }
else if (!isEmpty(cameraData.sceneBounds)) {
244 context->
bounds->getClipPlanes(context, nearDist, farDist, cameraComponent, cameraData.viewMatrix);
248 const float kMinRangeFactor = 0.0001f;
250 if(nearDist == std::numeric_limits<float>::max() && farDist == -std::numeric_limits<float>::max()){
256 farDist = glm::clamp(farDist, glm::max(nearDist * (1 + kMinRangeFactor), nearDist + 1), cameraComponent.
farPlaneLimit);
258 assert(nearDist < farDist);
260 const float calculationSlack = 0.001f;
262 const float newNearPlane = nearDist * (1.0f - calculationSlack);
263 const float newFarPlane = farDist * (1.0f + calculationSlack);
265 if (newNearPlane != cameraData.nearDistance || newFarPlane != cameraData.farDistance) {
266 cameraData.nearDistance = newNearPlane;
267 cameraData.farDistance = newFarPlane;
270 if (tightEstimate && secondPass){
271 if(cameraData.nearDepthBounds != newNearPlane || cameraData.farDepthBounds != newFarPlane){
272 context->
engine->setDirty();
273 cameraData.nearDepthBounds = newNearPlane;
274 cameraData.farDepthBounds = newFarPlane;
278 if(cameraData.nearClipPlanes != newNearPlane || cameraData.farClipPlanes != newFarPlane){
279 context->
engine->setDirty();
280 cameraData.nearClipPlanes = newNearPlane;
281 cameraData.farClipPlanes = newFarPlane;
287 if (cameraComponent.
nearDistance != cameraData.nearDistance || cameraComponent.
farDistance != cameraData.farDistance) {
289 cameraData.farDistance = cameraComponent.
farDistance;
291 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.