You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/AtomLyIntegration/CommonFeatures/Code/Source/ImageBasedLights/ImageBasedLightComponentCon...

298 lines
13 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <ImageBasedLights/ImageBasedLightComponentController.h>
#include <AtomLyIntegration/CommonFeatures/ImageBasedLights/ImageBasedLightComponentConstants.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/Utils/Utils.h>
namespace AZ
{
namespace Render
{
void ImageBasedLightComponentController::Reflect(ReflectContext* context)
{
ImageBasedLightComponentConfig::Reflect(context);
if (SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context))
{
serializeContext->Class<ImageBasedLightComponentController>()
->Version(0)
->Field("Configuration", &ImageBasedLightComponentController::m_configuration)
;
}
if (BehaviorContext* behaviorContext = azrtti_cast<BehaviorContext*>(context))
{
behaviorContext->EBus<ImageBasedLightComponentRequestBus>("ImageBasedLightComponentRequestBus")
->Event("SetSpecularImageAssetId", &ImageBasedLightComponentRequestBus::Events::SetSpecularImageAssetId)
->Event("GetSpecularImageAssetId", &ImageBasedLightComponentRequestBus::Events::GetSpecularImageAssetId)
->Event("SetDiffuseImageAssetId", &ImageBasedLightComponentRequestBus::Events::SetDiffuseImageAssetId)
->Event("GetDiffuseImageAssetId", &ImageBasedLightComponentRequestBus::Events::GetDiffuseImageAssetId)
->Event("SetSpecularImageAssetPath", &ImageBasedLightComponentRequestBus::Events::SetSpecularImageAssetPath)
->Event("GetSpecularImageAssetPath", &ImageBasedLightComponentRequestBus::Events::GetSpecularImageAssetPath)
->Event("SetDiffuseImageAssetPath", &ImageBasedLightComponentRequestBus::Events::SetDiffuseImageAssetPath)
->Event("GetDiffuseImageAssetPath", &ImageBasedLightComponentRequestBus::Events::GetDiffuseImageAssetPath)
->VirtualProperty("SpecularImageAssetId", "GetSpecularImageAssetId", "SetSpecularImageAssetId")
->VirtualProperty("DiffuseImageAssetId", "GetDiffuseImageAssetId", "SetDiffuseImageAssetId")
->VirtualProperty("SpecularImageAssetPath", "GetSpecularImageAssetPath", "SetSpecularImageAssetPath")
->VirtualProperty("DiffuseImageAssetPath", "GetDiffuseImageAssetPath", "SetDiffuseImageAssetPath")
;
}
}
void ImageBasedLightComponentController::GetProvidedServices(ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("ImageBasedLightService", 0x80c48204));
}
void ImageBasedLightComponentController::GetIncompatibleServices(ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("ImageBasedLightService", 0x80c48204));
}
ImageBasedLightComponentController::ImageBasedLightComponentController(const ImageBasedLightComponentConfig& config)
: m_configuration(config)
{
}
void ImageBasedLightComponentController::Activate(EntityId entityId)
{
m_entityId = entityId;
m_featureProcessor = RPI::Scene::GetFeatureProcessorForEntity<ImageBasedLightFeatureProcessorInterface>(m_entityId);
AZ_Error("ImageBasedLightComponentController", m_featureProcessor, "Unable to find a ImageBasedLightFeatureProcessorInterface on this entity's scene.");
if (m_featureProcessor)
{
LoadImage(m_configuration.m_specularImageAsset);
LoadImage(m_configuration.m_diffuseImageAsset);
m_featureProcessor->SetExposure(m_configuration.m_exposure);
TransformInterface* transformInterface = TransformBus::FindFirstHandler(m_entityId);
AZ_Assert(transformInterface, "Unable to attach to a TransformBus handler. Entity transform will not affect IBL.");
const AZ::Transform& transform = transformInterface ? transformInterface->GetWorldTM() : Transform::Identity();
m_featureProcessor->SetOrientation(transform.GetRotation());
TransformNotificationBus::Handler::BusConnect(m_entityId);
ImageBasedLightComponentRequestBus::Handler::BusConnect(m_entityId);
transformInterface = nullptr;
}
}
void ImageBasedLightComponentController::Deactivate()
{
ImageBasedLightComponentRequestBus::Handler::BusDisconnect();
TransformNotificationBus::Handler::BusDisconnect();
ReleaseImages();
if (m_featureProcessor)
{
m_featureProcessor->Reset();
m_featureProcessor = nullptr;
}
m_entityId = EntityId(EntityId::InvalidEntityId);
}
void ImageBasedLightComponentController::SetConfiguration(const ImageBasedLightComponentConfig& config)
{
m_configuration = config;
}
const ImageBasedLightComponentConfig& ImageBasedLightComponentController::GetConfiguration() const
{
return m_configuration;
}
void ImageBasedLightComponentController::OnAssetReady(Data::Asset<Data::AssetData> asset)
{
UpdateWithAsset(asset);
}
void ImageBasedLightComponentController::OnAssetReloaded(Data::Asset<Data::AssetData> asset)
{
UpdateWithAsset(asset);
}
void ImageBasedLightComponentController::OnAssetError(Data::Asset<Data::AssetData> asset)
{
UpdateWithAsset(asset);
}
void ImageBasedLightComponentController::UpdateWithAsset(Data::Asset<Data::AssetData> updatedAsset)
{
if (m_configuration.m_specularImageAsset.GetId() == updatedAsset.GetId())
{
if (HandleAssetUpdate(updatedAsset, m_configuration.m_specularImageAsset))
{
m_featureProcessor->SetSpecularImage(m_configuration.m_specularImageAsset);
ImageBasedLightComponentNotificationBus::Event(m_entityId, &ImageBasedLightComponentNotifications::OnSpecularImageUpdated);
}
}
else if (m_configuration.m_diffuseImageAsset.GetId() == updatedAsset.GetId())
{
if (HandleAssetUpdate(updatedAsset, m_configuration.m_diffuseImageAsset))
{
m_featureProcessor->SetDiffuseImage(m_configuration.m_diffuseImageAsset);
ImageBasedLightComponentNotificationBus::Event(m_entityId, &ImageBasedLightComponentNotifications::OnDiffuseImageUpdated);
}
}
}
bool ImageBasedLightComponentController::HandleAssetUpdate(Data::Asset<Data::AssetData> updatedAsset, Data::Asset<RPI::StreamingImageAsset>& configAsset)
{
configAsset = updatedAsset;
if (updatedAsset.IsReady())
{
auto& descriptor = configAsset->GetImageDescriptor();
bool isCubemap = descriptor.m_isCubemap || descriptor.m_arraySize == 6;
if (isCubemap)
{
return true;
}
}
// If this asset didn't load or isn't a cubemap, release it.
configAsset.Release();
return false;
}
void ImageBasedLightComponentController::SetSpecularImageAsset(const Data::Asset<RPI::StreamingImageAsset>& imageAsset)
{
Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_specularImageAsset.GetId());
m_configuration.m_specularImageAsset = imageAsset;
if (imageAsset.GetId().IsValid())
{
LoadImage(m_configuration.m_specularImageAsset);
}
else
{
// Clear out current image asset
m_featureProcessor->SetSpecularImage(m_configuration.m_specularImageAsset);
}
}
void ImageBasedLightComponentController::SetDiffuseImageAsset(const Data::Asset<RPI::StreamingImageAsset>& imageAsset)
{
Data::AssetBus::MultiHandler::BusDisconnect(m_configuration.m_diffuseImageAsset.GetId());
m_configuration.m_diffuseImageAsset = imageAsset;
if (imageAsset.GetId().IsValid())
{
LoadImage(m_configuration.m_diffuseImageAsset);
}
else
{
// Clear out current image asset
m_featureProcessor->SetDiffuseImage(m_configuration.m_diffuseImageAsset);
}
}
Data::Asset<RPI::StreamingImageAsset> ImageBasedLightComponentController::GetSpecularImageAsset() const
{
return m_configuration.m_specularImageAsset;
}
Data::Asset<RPI::StreamingImageAsset> ImageBasedLightComponentController::GetDiffuseImageAsset() const
{
return m_configuration.m_diffuseImageAsset;
}
void ImageBasedLightComponentController::SetSpecularImageAssetId(const Data::AssetId imageAssetId)
{
SetSpecularImageAsset(GetAssetFromId<RPI::StreamingImageAsset>(imageAssetId, m_configuration.m_specularImageAsset.GetAutoLoadBehavior()));
}
void ImageBasedLightComponentController::SetDiffuseImageAssetId(const Data::AssetId imageAssetId)
{
SetDiffuseImageAsset(GetAssetFromId<RPI::StreamingImageAsset>(imageAssetId, m_configuration.m_diffuseImageAsset.GetAutoLoadBehavior()));
}
void ImageBasedLightComponentController::SetSpecularImageAssetPath(const AZStd::string path)
{
SetSpecularImageAsset(GetAssetFromPath<RPI::StreamingImageAsset>(path, m_configuration.m_specularImageAsset.GetAutoLoadBehavior()));
}
void ImageBasedLightComponentController::SetDiffuseImageAssetPath(const AZStd::string path)
{
SetDiffuseImageAsset(GetAssetFromPath<RPI::StreamingImageAsset>(path, m_configuration.m_diffuseImageAsset.GetAutoLoadBehavior()));
}
AZStd::string ImageBasedLightComponentController::GetSpecularImageAssetPath() const
{
AZStd::string assetPathString;
Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &Data::AssetCatalogRequests::GetAssetPathById, m_configuration.m_specularImageAsset.GetId());
return assetPathString;
}
AZStd::string ImageBasedLightComponentController::GetDiffuseImageAssetPath() const
{
AZStd::string assetPathString;
Data::AssetCatalogRequestBus::BroadcastResult(assetPathString, &Data::AssetCatalogRequests::GetAssetPathById, m_configuration.m_diffuseImageAsset.GetId());
return assetPathString;
}
Data::AssetId ImageBasedLightComponentController::GetSpecularImageAssetId() const
{
return m_configuration.m_specularImageAsset.GetId();
}
Data::AssetId ImageBasedLightComponentController::GetDiffuseImageAssetId() const
{
return m_configuration.m_diffuseImageAsset.GetId();
}
void ImageBasedLightComponentController::SetExposure(float exposure)
{
m_configuration.m_exposure = exposure;
m_featureProcessor->SetExposure(exposure);
}
float ImageBasedLightComponentController::GetExposure() const
{
return m_configuration.m_exposure;
}
void ImageBasedLightComponentController::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, const AZ::Transform& world)
{
const Quaternion& rotation = world.GetRotation();
m_featureProcessor->SetOrientation(rotation);
}
void ImageBasedLightComponentController::LoadImage(Data::Asset<RPI::StreamingImageAsset>& imageAsset)
{
Data::AssetBus::MultiHandler::BusDisconnect(imageAsset.GetId());
if (imageAsset.GetId().IsValid())
{
// If the asset is already loaded it'll call OnAssetReady() immediately on BusConnect().
Data::AssetBus::MultiHandler::BusConnect(imageAsset.GetId());
imageAsset.QueueLoad();
}
else
{
// Call update for invalid assets so current assets can be cleared if necessary.
UpdateWithAsset(imageAsset);
}
}
void ImageBasedLightComponentController::ReleaseImages()
{
Data::AssetBus::MultiHandler::BusDisconnect();
m_configuration.m_specularImageAsset.Release();
m_configuration.m_diffuseImageAsset.Release();
}
} // namespace Render
} // namespace AZ