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;
51 for (
auto& cameraComponent : pool) {
53 IContext* gfxContext = context->device->getImmediateContext();
55 RenderTexture* sourceTexture = resources->getRenderTexture(cameraComponent.renderTexture);
56 RenderTexture* destTexture = resources->getRenderTexture(cameraComponent.resolveTarget);
58 assert(cameraComponent.renderTexture->description.width == cameraComponent.resolveTarget->description.width);
59 assert(cameraComponent.renderTexture->description.height == cameraComponent.resolveTarget->description.height);
61 if (cameraComponent.renderTexture->description.samples > 1) {
62 gfxContext->
resolveResource(sourceTexture->textureHandle, destTexture->textureHandle);
65 gfxContext->copyResource(destTexture->textureHandle, sourceTexture->textureHandle);
74 for (
auto & cameraComponent : pool) {
75 auto & cameraData = getData(&cameraComponent);
77 if (!std::isfinite(cameraComponent.nearPlaneLimit) ||
78 !std::isfinite(cameraComponent.farPlaneLimit) ||
79 (cameraComponent.farDistance <= cameraComponent.nearPlaneLimit))
81 LOG_ERROR(logger,
"Broken camera invariants, nearPlaneLimit=%f, setting to 1.0, farPlaneLimit=%f, setting to 100000.0",
82 cameraComponent.nearPlaneLimit,
83 cameraComponent.farPlaneLimit);
84 cameraComponent.nearPlaneLimit = 1.f;
85 cameraComponent.nearPlaneLimit = 100000.f;
89 if (cameraComponent.environment) {
90 cameraData.environment = cameraComponent.environment->getComponentHandle<
EnvironmentComponent>();
91 }
else if (
auto * env = context->environmentSystem->getGlobalEnvironment(); env) {
98 if (cameraComponent.supportsPicking() && &cameraComponent != mainCamera.resolve()) {
99 if (cameraData.rayPickId == -1) {
100 cameraData.rayPickId = nextRayPickId++;
104 auto transformComponent = cameraComponent.getContainer()->getComponent<
TransformComponent>();
106 if (!transformComponent) {
107 LOG_ERROR(logger,
"Camera %s missing transform component.", cameraComponent.getContainer()->getName().c_str());
111 cameraData.exposure = cameraComponent.exposure;
112 cameraData.layerMask = cameraComponent.layerMask;
113 cameraData.lightingMask = cameraComponent.lightingMask;
114 cameraData.viewportSize = cameraComponent.viewportSize;
115 cameraData.viewportOrigin = cameraComponent.viewportOrigin;
116 cameraData.viewMatrix = glm::inverse(context->transformSystem->getLocalToWorld(transformComponent));
117 cameraData.inverseViewMatrix = context->transformSystem->getLocalToWorld(transformComponent);
120 cameraData.clearColor = cameraComponent.clearColor;
121 cameraData.useClearColor =
true;
124 cameraData.useClearColor =
false;
126 if (cameraData.viewportSize.x == 0 &&
HandleIsValid(cameraComponent.renderTexture)) {
127 auto cameraTexture = cameraComponent.renderTexture;
129 cameraData.viewportSize = { cameraTexture->description.width, cameraTexture->description.height };
132 cameraData.discardThreshold = 4.f*(cameraComponent.discardThreshold) / (cameraData.viewportSize.x*cameraData.viewportSize.y);
133 cameraData.keepThreshold = cameraComponent.keepThreshold;
136 for (
auto & cameraComponent : pool) {
137 updateClippingPlanes(context, cameraComponent,
false);
138 updateProjection(context, cameraComponent);
147 for (
auto & cameraComponent : pool) {
148 if (updateClippingPlanes(context, cameraComponent,
true)) {
149 updateProjection(context, cameraComponent);
153 base::postUpdate(context);
158 auto & cameraData = getData(&cameraComponent);
160 float aspect = cameraData.viewportSize.y == 0 ? 1.0f : cameraData.viewportSize.x / cameraData.viewportSize.y;
163 const float height = cameraComponent.
orthoHeight / 2.0f;
165 cameraData.rawProjectionMatrix = glm::ortho(-height * aspect, height * aspect,
167 cameraData.nearDistance,
168 cameraData.farDistance);
170 cameraData.fieldOfView = cameraComponent.
fieldOfView;
171 cameraData.rawProjectionMatrix = glm::perspective(cameraComponent.
fieldOfView,
173 cameraData.nearDistance,
174 cameraData.farDistance);
177 if (glm::any(glm::notEqual(cameraComponent.
subsetMin, glm::vec2(0.f)) || glm::notEqual(cameraComponent.
subsetMax, glm::vec2(1.f)))) {
178 const auto& a = cameraComponent.
subsetMin;
179 const auto& b = cameraComponent.
subsetMax;
180 glm::vec2 s = glm::vec2(2.f) / (b - a);
181 if (std::isfinite(s.x) && std::isfinite(s.y)) {
182 glm::vec2 o = s * (glm::vec2(1) - 2.f * a) - glm::vec2(1);
183 cameraData.rawProjectionMatrix = glm::mat4(s.x, 0.f, 0.f, 0.f,
186 o.x, o.y, 0.f, 1.f) * cameraData.rawProjectionMatrix;
189 LOG_WARNING(logger,
"Invalid camera frustum subset [%f,%f]x[%f,%f], ignoring.", a.x, a.y, b.x, b.y);
195 static glm::mat4 flip(
201 cameraData.flipWindingOrder =
true;
202 cameraData.rawProjectionMatrix = flip * cameraData.rawProjectionMatrix;
205 cameraData.flipWindingOrder =
false;
208 cameraData.prevViewProjection = cameraData.viewProjection;
209 cameraData.rawViewProjection = cameraData.rawProjectionMatrix * cameraData.viewMatrix;
210 cameraData.rawViewCullMatrix = cameraData.rawViewProjection;
212 cameraData.viewProjection = cameraData.projectionMatrix * cameraData.viewMatrix;
213 cameraData.inverseViewProjectionMatrix = glm::inverse(cameraData.viewProjection);
214 cameraData.inverseProjectionMatrix = glm::inverse(cameraData.projectionMatrix);
217 cameraData.clientFlags = cameraComponent.
clientFlags;
218 cameraData.frustum = Geometry::calculateFrustum<Geometry::Frustum, glm::mat4>(cameraData.viewProjection);
223 CpuInstrumentationScope(SCOPE_SYSTEMS,
"CameraSystem::updateClippingPlanes");
225 bool tightEstimate = context->
variables->get(
"camera.tightBounds",
false);
226 auto & cameraData = getData(&cameraComponent);
228 cameraData.sceneBounds = Geometry::BoundingBox();
231 context->
bounds->getSceneBounds(context,
reinterpret_cast<float *
>(&cameraData.sceneBounds), cameraComponent.
layerMask & ~RenderLayers::Sky);
232 cameraData.sceneBounds = Bounds::getTransformedBounds(cameraData.sceneBounds, cameraData.viewMatrix);
234 float nearDist = std::numeric_limits<float>::max();
235 float farDist = -std::numeric_limits<float>::max();
237 if (tightEstimate && secondPass) {
238 CpuInstrumentationScope(SCOPE_SYSTEMS,
"CameraSystem::getCameraDepthBounds");
239 context->
bounds->getCameraDepthBounds(context, nearDist, farDist, cameraComponent);
240 }
else if (!isEmpty(cameraData.sceneBounds)) {
241 context->
bounds->getClipPlanes(context, nearDist, farDist, cameraComponent, cameraData.viewMatrix);
245 const float kMinRangeFactor = 0.0001f;
247 if(nearDist == std::numeric_limits<float>::max() && farDist == -std::numeric_limits<float>::max()){
253 farDist = glm::clamp(farDist, glm::max(nearDist * (1 + kMinRangeFactor), nearDist + 1), cameraComponent.
farPlaneLimit);
255 assert(nearDist < farDist);
257 const float calculationSlack = 0.001f;
259 const float newNearPlane = nearDist * (1.0f - calculationSlack);
260 const float newFarPlane = farDist * (1.0f + calculationSlack);
262 if (newNearPlane != cameraData.nearDistance || newFarPlane != cameraData.farDistance) {
263 cameraData.nearDistance = newNearPlane;
264 cameraData.farDistance = newFarPlane;
267 if (tightEstimate && secondPass){
268 if(cameraData.nearDepthBounds != newNearPlane || cameraData.farDepthBounds != newFarPlane){
269 context->
engine->setDirty();
270 cameraData.nearDepthBounds = newNearPlane;
271 cameraData.farDepthBounds = newFarPlane;
275 if(cameraData.nearClipPlanes != newNearPlane || cameraData.farClipPlanes != newFarPlane){
276 context->
engine->setDirty();
277 cameraData.nearClipPlanes = newNearPlane;
278 cameraData.farClipPlanes = newFarPlane;
284 if (cameraComponent.
nearDistance != cameraData.nearDistance || cameraComponent.
farDistance != cameraData.farDistance) {
286 cameraData.farDistance = cameraComponent.
farDistance;
288 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()
static const ResourceHandle_t NoHandle
Handle representing a default (or none if default not present) resource.
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.