#include "Rendering/RenderSceneExtractor.h" #include "Components/CameraComponent.h" #include "Components/GameObject.h" #include "Components/LightComponent.h" #include "Rendering/RenderSceneUtility.h" #include "Scene/Scene.h" #include namespace XCEngine { namespace Rendering { namespace { bool IsUsableCamera(const Components::CameraComponent* camera) { return camera != nullptr && camera->IsEnabled() && camera->GetGameObject() != nullptr && camera->GetGameObject()->IsActiveInHierarchy(); } RenderLightType ToRenderLightType(Components::LightType lightType) { switch (lightType) { case Components::LightType::Directional: return RenderLightType::Directional; case Components::LightType::Point: return RenderLightType::Point; case Components::LightType::Spot: return RenderLightType::Spot; } return RenderLightType::Point; } RenderDirectionalLightData BuildDirectionalLightData( const Components::LightComponent& light) { RenderDirectionalLightData lightData; lightData.enabled = true; lightData.castsShadows = light.GetCastsShadows(); lightData.intensity = light.GetIntensity(); lightData.color = light.GetColor(); lightData.direction = BuildRenderLightDirection(light); return lightData; } RenderAdditionalLightData BuildAdditionalLightData( const Components::LightComponent& light) { RenderAdditionalLightData lightData; lightData.type = ToRenderLightType(light.GetLightType()); lightData.enabled = true; lightData.castsShadows = light.GetCastsShadows(); lightData.color = light.GetColor(); lightData.intensity = light.GetIntensity(); switch (light.GetLightType()) { case Components::LightType::Directional: lightData.direction = BuildRenderLightDirection(light); break; case Components::LightType::Point: lightData.position = light.transform().GetPosition(); lightData.range = light.GetRange(); break; case Components::LightType::Spot: lightData.position = light.transform().GetPosition(); lightData.direction = BuildRenderLightDirection(light); lightData.range = light.GetRange(); lightData.spotAngle = light.GetSpotAngle(); break; } return lightData; } float ComputeAdditionalLightInfluence( const Components::LightComponent& light, const Math::Vector3& cameraPosition) { if (light.GetIntensity() <= Math::EPSILON) { return 0.0f; } if (light.GetLightType() == Components::LightType::Directional) { return light.GetIntensity(); } const float range = std::max(light.GetRange(), 0.001f); const float rangeSq = range * range; const Math::Vector3 lightPosition = light.transform().GetPosition(); const float distanceSq = (lightPosition - cameraPosition).SqrMagnitude(); if (distanceSq >= rangeSq) { return 0.0f; } return light.GetIntensity() / std::max(distanceSq, Math::EPSILON); } uint32_t GetAdditionalLightGroupRank(const Components::LightComponent& light) { return light.GetLightType() == Components::LightType::Directional ? 0u : 1u; } uint32_t GetAdditionalLightTypeRank(const Components::LightComponent& light) { switch (light.GetLightType()) { case Components::LightType::Directional: return 0u; case Components::LightType::Point: return 1u; case Components::LightType::Spot: return 2u; } return 3u; } bool CompareVisibleItems(const VisibleRenderItem& lhs, const VisibleRenderItem& rhs) { return CompareVisibleRenderItemsStable(lhs, rhs); } } // namespace RenderSceneData RenderSceneExtractor::Extract( const Components::Scene& scene, Components::CameraComponent* overrideCamera, uint32_t viewportWidth, uint32_t viewportHeight) const { RenderSceneData sceneData; sceneData.camera = SelectCamera(scene, overrideCamera); if (sceneData.camera == nullptr) { return sceneData; } sceneData.cameraData = BuildRenderCameraData(*sceneData.camera, viewportWidth, viewportHeight); const Math::Vector3 cameraPosition = sceneData.cameraData.worldPosition; const uint32_t cullingMask = sceneData.camera->GetCullingMask(); const std::vector rootGameObjects = scene.GetRootGameObjects(); for (Components::GameObject* rootGameObject : rootGameObjects) { ExtractVisibleItems(rootGameObject, cameraPosition, cullingMask, sceneData.visibleItems); } std::stable_sort( sceneData.visibleItems.begin(), sceneData.visibleItems.end(), CompareVisibleItems); ExtractLighting(scene, cameraPosition, cullingMask, sceneData.lighting); return sceneData; } RenderSceneData RenderSceneExtractor::ExtractForCamera( const Components::Scene& scene, Components::CameraComponent& camera, uint32_t viewportWidth, uint32_t viewportHeight) const { RenderSceneData sceneData; if (!IsUsableCamera(&camera)) { return sceneData; } sceneData.camera = &camera; sceneData.cameraData = BuildRenderCameraData(camera, viewportWidth, viewportHeight); const Math::Vector3 cameraPosition = sceneData.cameraData.worldPosition; const uint32_t cullingMask = camera.GetCullingMask(); const std::vector rootGameObjects = scene.GetRootGameObjects(); for (Components::GameObject* rootGameObject : rootGameObjects) { ExtractVisibleItems(rootGameObject, cameraPosition, cullingMask, sceneData.visibleItems); } std::stable_sort( sceneData.visibleItems.begin(), sceneData.visibleItems.end(), CompareVisibleItems); ExtractLighting(scene, cameraPosition, cullingMask, sceneData.lighting); return sceneData; } Components::CameraComponent* RenderSceneExtractor::SelectCamera( const Components::Scene& scene, Components::CameraComponent* overrideCamera) const { if (IsUsableCamera(overrideCamera)) { return overrideCamera; } const std::vector cameras = scene.FindObjectsOfType(); Components::CameraComponent* primaryCamera = nullptr; for (Components::CameraComponent* camera : cameras) { if (!IsUsableCamera(camera)) { continue; } if (camera->IsPrimary()) { if (primaryCamera == nullptr || camera->GetDepth() > primaryCamera->GetDepth()) { primaryCamera = camera; } } } if (primaryCamera != nullptr) { return primaryCamera; } for (Components::CameraComponent* camera : cameras) { if (IsUsableCamera(camera)) { return camera; } } return nullptr; } void RenderSceneExtractor::ExtractLighting( const Components::Scene& scene, const Math::Vector3& cameraPosition, uint32_t cullingMask, RenderLightingData& lightingData) const { lightingData = {}; const std::vector lights = scene.FindObjectsOfType(); Components::LightComponent* mainDirectionalLight = FindMainDirectionalLight(scene, cullingMask); if (mainDirectionalLight != nullptr) { lightingData.mainDirectionalLight = BuildDirectionalLightData(*mainDirectionalLight); } struct AdditionalLightCandidate { RenderAdditionalLightData data = {}; float influence = 0.0f; uint32_t groupRank = 0u; uint32_t typeRank = 0u; Components::GameObject::ID gameObjectId = Components::GameObject::INVALID_ID; }; std::vector candidates; candidates.reserve(lights.size()); for (Components::LightComponent* light : lights) { if (light == nullptr || !IsLightVisibleForCullingMask(*light, cullingMask) || light == mainDirectionalLight) { continue; } AdditionalLightCandidate candidate; candidate.data = BuildAdditionalLightData(*light); candidate.influence = ComputeAdditionalLightInfluence(*light, cameraPosition); candidate.groupRank = GetAdditionalLightGroupRank(*light); candidate.typeRank = GetAdditionalLightTypeRank(*light); candidate.gameObjectId = light->GetGameObject() != nullptr ? light->GetGameObject()->GetID() : Components::GameObject::INVALID_ID; candidates.push_back(candidate); } std::sort( candidates.begin(), candidates.end(), [](const AdditionalLightCandidate& lhs, const AdditionalLightCandidate& rhs) { if (lhs.groupRank != rhs.groupRank) { return lhs.groupRank < rhs.groupRank; } if (lhs.influence != rhs.influence) { return lhs.influence > rhs.influence; } if (lhs.typeRank != rhs.typeRank) { return lhs.typeRank < rhs.typeRank; } return lhs.gameObjectId < rhs.gameObjectId; }); const uint32_t additionalLightCount = static_cast( std::min(candidates.size(), static_cast(RenderLightingData::kMaxAdditionalLightCount))); lightingData.additionalLightCount = additionalLightCount; for (uint32_t index = 0; index < additionalLightCount; ++index) { lightingData.additionalLights[index] = candidates[index].data; } } void RenderSceneExtractor::ExtractVisibleItems( Components::GameObject* gameObject, const Math::Vector3& cameraPosition, uint32_t cullingMask, std::vector& visibleItems) const { if (gameObject == nullptr || !gameObject->IsActiveInHierarchy()) { return; } const uint32_t gameObjectLayerMask = 1u << gameObject->GetLayer(); const bool isVisibleInCameraMask = (cullingMask & gameObjectLayerMask) != 0; if (isVisibleInCameraMask) { AppendRenderItemsForGameObject(*gameObject, cameraPosition, visibleItems); } for (Components::GameObject* child : gameObject->GetChildren()) { ExtractVisibleItems(child, cameraPosition, cullingMask, visibleItems); } } } // namespace Rendering } // namespace XCEngine