// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #include "EnvironmentProbe.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Renderer/ProbesRenderer.h" #include "Engine/Renderer/ReflectionsPass.h" #include "Engine/Content/Content.h" #include "Engine/ContentExporters/AssetExporters.h" #include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Level/Scene/Scene.h" EnvironmentProbe::EnvironmentProbe(const SpawnParams& params) : Actor(params) , _radius(3000.0f) , _isUsingCustomProbe(false) , _probe(nullptr) { _sphere = BoundingSphere(Vector3::Zero, _radius); BoundingBox::FromSphere(_sphere, _box); } float EnvironmentProbe::GetRadius() const { return _radius; } void EnvironmentProbe::SetRadius(float value) { value = Math::Max(0.0f, value); if (Math::NearEqual(value, _radius)) return; _radius = value; UpdateBounds(); } float EnvironmentProbe::GetScaledRadius() const { return _radius * _transform.Scale.MaxValue(); } void EnvironmentProbe::SetupProbeData(const RenderContext& renderContext, ProbeData* data) const { const float radius = GetScaledRadius(); data->Data0 = Float4(GetPosition() - renderContext.View.Origin, 0); data->Data1 = Float4(radius, 1.0f / radius, Brightness, 0); } CubeTexture* EnvironmentProbe::GetCustomProbe() const { if (IsUsingCustomProbe()) return GetProbe(); return nullptr; } void EnvironmentProbe::SetCustomProbe(CubeTexture* probe) { // Check if value won't change if (probe == _probe) return; // Set _isUsingCustomProbe = probe != nullptr; _probe = probe; } void EnvironmentProbe::Bake(float timeout) { #if COMPILE_WITH_PROBES_BAKING ProbesRenderer::Bake(this, timeout); #else LOG(Warning, "Baking probes is not supported."); #endif } void EnvironmentProbe::SetProbeData(TextureData& data) { // Validate input data ASSERT(data.GetArraySize() == 6 && data.Height == ENV_PROBES_RESOLUTION && data.Width == ENV_PROBES_RESOLUTION); // Check if was using custom probe if (_isUsingCustomProbe) { // Set _isUsingCustomProbe = false; _probe = nullptr; } Guid id = Guid::New(); #if COMPILE_WITH_ASSETS_IMPORTER // Create asset file const String path = GetScene()->GetDataFolderPath() / TEXT("EnvProbes/") / GetID().ToString(Guid::FormatType::N) + ASSET_FILES_EXTENSION_WITH_DOT; AssetInfo info; if (FileSystem::FileExists(path) && Content::GetAssetInfo(path, info)) id = info.ID; if (AssetsImportingManager::Create(AssetsImportingManager::CreateCubeTextureTag, path, id, &data)) { LOG(Error, "Cannot import generated env probe!"); return; } #else // TODO: create or reuse virtual texture and use it LOG(Error, "Changing probes at runtime in game is not supported."); return; #endif // Check if has loaded probe and it has different ID if (_probe && _probe->GetID() != id) { const auto prevId = _probe->GetID(); _probe = nullptr; LOG(Warning, "New env probe cube texture has different ID={0} than old one={1}.", id, prevId); } // Link probe texture _probe = Content::LoadAsync(id); } void EnvironmentProbe::UpdateBounds() { _sphere = BoundingSphere(GetPosition(), GetScaledRadius()); BoundingBox::FromSphere(_sphere, _box); if (_sceneRenderingKey != -1) GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); } void EnvironmentProbe::Draw(RenderContext& renderContext) { if (Brightness > ZeroTolerance && (renderContext.View.Flags & ViewFlags::Reflections) != 0 && renderContext.View.Pass & DrawPass::GBuffer && HasProbeLoaded()) { renderContext.List->EnvironmentProbes.Add(this); } } #if USE_EDITOR #include "Engine/Debug/DebugDraw.h" void EnvironmentProbe::OnDebugDrawSelected() { // Draw influence range DEBUG_DRAW_WIRE_SPHERE(_sphere, Color::CornflowerBlue, 0, true); // Base Actor::OnDebugDrawSelected(); } #endif void EnvironmentProbe::OnLayerChanged() { if (_sceneRenderingKey != -1) GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); } void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj) { // Base Actor::Serialize(stream, otherObj); SERIALIZE_GET_OTHER_OBJ(EnvironmentProbe); SERIALIZE_MEMBER(Radius, _radius); SERIALIZE(Brightness); SERIALIZE(AutoUpdate); SERIALIZE(CaptureNearPlane); SERIALIZE_MEMBER(IsCustomProbe, _isUsingCustomProbe); SERIALIZE_MEMBER(ProbeID, _probe); } void EnvironmentProbe::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) { // Base Actor::Deserialize(stream, modifier); DESERIALIZE_MEMBER(Radius, _radius); DESERIALIZE(Brightness); DESERIALIZE(AutoUpdate); DESERIALIZE(CaptureNearPlane); DESERIALIZE_MEMBER(IsCustomProbe, _isUsingCustomProbe); DESERIALIZE_MEMBER(ProbeID, _probe); } bool EnvironmentProbe::HasContentLoaded() const { return _probe == nullptr || _probe->IsLoaded(); } bool EnvironmentProbe::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) { return CollisionsHelper::RayIntersectsSphere(ray, _sphere, distance, normal); } void EnvironmentProbe::OnEnable() { GetSceneRendering()->AddActor(this, _sceneRenderingKey); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); #endif // Base Actor::OnEnable(); } void EnvironmentProbe::OnDisable() { #if USE_EDITOR GetSceneRendering()->RemoveViewportIcon(this); #endif GetSceneRendering()->RemoveActor(this, _sceneRenderingKey); // Base Actor::OnDisable(); } void EnvironmentProbe::OnTransformChanged() { // Base Actor::OnTransformChanged(); UpdateBounds(); if (AutoUpdate && IsDuringPlay()) { Bake(1.0f); } }