#include "Rendering/SceneRenderer.h" #include "Components/CameraComponent.h" #include "Components/GameObject.h" #include "Scene/Scene.h" #include #include namespace XCEngine { namespace Rendering { namespace { bool CompareCameraRenderRequest(const CameraRenderRequest& lhs, const CameraRenderRequest& rhs) { if (lhs.cameraStackOrder != rhs.cameraStackOrder) { return lhs.cameraStackOrder < rhs.cameraStackOrder; } if (lhs.cameraDepth != rhs.cameraDepth) { return lhs.cameraDepth < rhs.cameraDepth; } return lhs.camera < rhs.camera; } bool IsUsableCamera(const Components::CameraComponent* camera) { return camera != nullptr && camera->IsEnabled() && camera->GetGameObject() != nullptr && camera->GetGameObject()->IsActiveInHierarchy(); } RenderClearFlags ResolveClearFlags( const Components::CameraComponent& camera, size_t renderedBaseCameraCount, size_t renderedRequestCount) { switch (camera.GetClearMode()) { case Components::CameraClearMode::ColorAndDepth: return RenderClearFlags::All; case Components::CameraClearMode::DepthOnly: return RenderClearFlags::Depth; case Components::CameraClearMode::None: return RenderClearFlags::None; case Components::CameraClearMode::Auto: default: if (camera.GetStackType() == Components::CameraStackType::Overlay) { return renderedRequestCount == 0 ? RenderClearFlags::All : RenderClearFlags::Depth; } return renderedBaseCameraCount == 0 ? RenderClearFlags::All : RenderClearFlags::Depth; } } Math::RectInt ResolveCameraRenderArea( const Components::CameraComponent& camera, const RenderSurface& surface) { const Math::Rect viewportRect = camera.GetViewportRect(); const float surfaceWidth = static_cast(surface.GetWidth()); const float surfaceHeight = static_cast(surface.GetHeight()); const int32_t left = static_cast(std::floor(viewportRect.x * surfaceWidth)); const int32_t top = static_cast(std::floor(viewportRect.y * surfaceHeight)); const int32_t right = static_cast(std::ceil((viewportRect.x + viewportRect.width) * surfaceWidth)); const int32_t bottom = static_cast(std::ceil((viewportRect.y + viewportRect.height) * surfaceHeight)); return Math::RectInt(left, top, right - left, bottom - top); } } // namespace SceneRenderer::SceneRenderer() = default; SceneRenderer::SceneRenderer(std::unique_ptr pipeline) : m_cameraRenderer(std::move(pipeline)) { } SceneRenderer::SceneRenderer(std::shared_ptr pipelineAsset) : m_cameraRenderer(std::move(pipelineAsset)) { } void SceneRenderer::SetPipeline(std::unique_ptr pipeline) { m_cameraRenderer.SetPipeline(std::move(pipeline)); } void SceneRenderer::SetPipelineAsset(std::shared_ptr pipelineAsset) { m_cameraRenderer.SetPipelineAsset(std::move(pipelineAsset)); } std::vector SceneRenderer::BuildRenderRequests( const Components::Scene& scene, Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) const { std::vector requests; std::vector cameras; if (IsUsableCamera(overrideCamera)) { cameras.push_back(overrideCamera); } else { cameras = scene.FindObjectsOfType(); cameras.erase( std::remove_if( cameras.begin(), cameras.end(), [](const Components::CameraComponent* camera) { return !IsUsableCamera(camera); }), cameras.end()); } std::stable_sort( cameras.begin(), cameras.end(), [](const Components::CameraComponent* lhs, const Components::CameraComponent* rhs) { if (lhs->GetStackType() != rhs->GetStackType()) { return static_cast(lhs->GetStackType()) < static_cast(rhs->GetStackType()); } if (lhs->GetDepth() != rhs->GetDepth()) { return lhs->GetDepth() < rhs->GetDepth(); } return lhs < rhs; }); size_t renderedBaseCameraCount = 0; for (Components::CameraComponent* camera : cameras) { CameraRenderRequest request; request.scene = &scene; request.camera = camera; request.context = context; request.surface = surface; request.surface.SetRenderArea(ResolveCameraRenderArea(*camera, surface)); request.cameraDepth = camera->GetDepth(); request.cameraStackOrder = static_cast(camera->GetStackType()); request.clearFlags = ResolveClearFlags(*camera, renderedBaseCameraCount, requests.size()); if (request.surface.GetRenderAreaWidth() > 0 && request.surface.GetRenderAreaHeight() > 0) { requests.push_back(request); if (camera->GetStackType() == Components::CameraStackType::Base) { ++renderedBaseCameraCount; } } } return requests; } bool SceneRenderer::Render(const CameraRenderRequest& request) { return m_cameraRenderer.Render(request); } bool SceneRenderer::Render(const std::vector& requests) { if (requests.empty()) { return false; } for (const CameraRenderRequest& request : requests) { if (!request.IsValid()) { return false; } } std::vector sortedRequests = requests; std::stable_sort( sortedRequests.begin(), sortedRequests.end(), CompareCameraRenderRequest); bool rendered = false; for (const CameraRenderRequest& request : sortedRequests) { if (!m_cameraRenderer.Render(request)) { return false; } rendered = true; } return rendered; } bool SceneRenderer::Render( const Components::Scene& scene, Components::CameraComponent* overrideCamera, const RenderContext& context, const RenderSurface& surface) { return Render(BuildRenderRequests(scene, overrideCamera, context, surface)); } } // namespace Rendering } // namespace XCEngine