Files
FlaxEngine/Source/Engine/Level/Actors/EnvironmentProbe.cpp

256 lines
6.3 KiB
C++

// 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"
#include "Engine/Graphics/Graphics.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();
}
bool EnvironmentProbe::HasProbe() const
{
return _probe != nullptr;
}
bool EnvironmentProbe::HasProbeLoaded() const
{
return _probe != nullptr && _probe->IsLoaded();
}
CubeTexture* EnvironmentProbe::GetProbe() const
{
return _probe;
}
bool EnvironmentProbe::IsUsingCustomProbe() const
{
return _isUsingCustomProbe;
}
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)
{
// Remove custom probe (if used)
if (_isUsingCustomProbe)
{
_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<CubeTexture>(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(CubemapResolution);
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(CubemapResolution);
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);
}
}