#include "Components/VolumeRendererComponent.h" #include "Core/Asset/ResourceManager.h" namespace XCEngine { namespace Components { namespace { std::string ToStdString(const Containers::String& value) { return std::string(value.CStr()); } bool HasVirtualPathScheme(const std::string& path) { return path.find("://") != std::string::npos; } std::string EncodeAssetRef(const Resources::AssetRef& assetRef) { if (!assetRef.IsValid()) { return std::string(); } return ToStdString(assetRef.assetGuid.ToString()) + "," + std::to_string(assetRef.localID) + "," + std::to_string(static_cast(assetRef.resourceType)); } bool TryDecodeAssetRef(const std::string& value, Resources::AssetRef& outRef) { const size_t firstComma = value.find(','); const size_t secondComma = firstComma == std::string::npos ? std::string::npos : value.find(',', firstComma + 1); if (firstComma == std::string::npos || secondComma == std::string::npos) { return false; } const Containers::String guidText(value.substr(0, firstComma).c_str()); outRef.assetGuid = Resources::AssetGUID::ParseOrDefault(guidText); outRef.localID = static_cast(std::stoull(value.substr(firstComma + 1, secondComma - firstComma - 1))); outRef.resourceType = static_cast(std::stoi(value.substr(secondComma + 1))); return outRef.IsValid(); } std::string VolumeFieldPathFromHandle(const Resources::ResourceHandle& volumeField) { return volumeField.Get() != nullptr ? ToStdString(volumeField->GetPath()) : std::string(); } std::string MaterialPathFromHandle(const Resources::ResourceHandle& material) { return material.Get() != nullptr ? ToStdString(material->GetPath()) : std::string(); } } // namespace struct VolumeRendererComponent::PendingVolumeLoadState { Resources::LoadResult result; bool completed = false; }; struct VolumeRendererComponent::PendingMaterialLoadState { Resources::LoadResult result; bool completed = false; }; Resources::VolumeField* VolumeRendererComponent::GetVolumeField() const { const_cast(this)->EnsureDeferredAsyncVolumeLoadStarted(); const_cast(this)->ResolvePendingVolumeField(); return m_volumeField.Get(); } const Resources::ResourceHandle& VolumeRendererComponent::GetVolumeFieldHandle() const { const_cast(this)->EnsureDeferredAsyncVolumeLoadStarted(); const_cast(this)->ResolvePendingVolumeField(); return m_volumeField; } void VolumeRendererComponent::SetVolumeFieldPath(const std::string& volumeFieldPath) { m_pendingVolumeLoad.reset(); m_asyncVolumeLoadRequested = false; m_volumeFieldPath = volumeFieldPath; if (m_volumeFieldPath.empty()) { m_volumeField.Reset(); m_volumeFieldRef.Reset(); return; } m_volumeField = Resources::ResourceManager::Get().Load(m_volumeFieldPath.c_str()); if (!Resources::ResourceManager::Get().TryGetAssetRef( m_volumeFieldPath.c_str(), Resources::ResourceType::VolumeField, m_volumeFieldRef)) { m_volumeFieldRef.Reset(); } } void VolumeRendererComponent::SetVolumeField( const Resources::ResourceHandle& volumeField) { m_pendingVolumeLoad.reset(); m_asyncVolumeLoadRequested = false; m_volumeField = volumeField; m_volumeFieldPath = VolumeFieldPathFromHandle(volumeField); if (m_volumeFieldPath.empty() || !Resources::ResourceManager::Get().TryGetAssetRef( m_volumeFieldPath.c_str(), Resources::ResourceType::VolumeField, m_volumeFieldRef)) { m_volumeFieldRef.Reset(); } } void VolumeRendererComponent::SetVolumeField(Resources::VolumeField* volumeField) { SetVolumeField(Resources::ResourceHandle(volumeField)); } void VolumeRendererComponent::ClearVolumeField() { m_pendingVolumeLoad.reset(); m_asyncVolumeLoadRequested = false; m_volumeField.Reset(); m_volumeFieldPath.clear(); m_volumeFieldRef.Reset(); } Resources::Material* VolumeRendererComponent::GetMaterial() const { const_cast(this)->EnsureDeferredAsyncMaterialLoadStarted(); const_cast(this)->ResolvePendingMaterial(); return m_material.Get(); } const Resources::ResourceHandle& VolumeRendererComponent::GetMaterialHandle() const { const_cast(this)->EnsureDeferredAsyncMaterialLoadStarted(); const_cast(this)->ResolvePendingMaterial(); return m_material; } void VolumeRendererComponent::SetMaterialPath(const std::string& materialPath) { m_pendingMaterialLoad.reset(); m_asyncMaterialLoadRequested = false; m_materialPath = materialPath; if (m_materialPath.empty()) { m_material.Reset(); m_materialRef.Reset(); return; } m_material = Resources::ResourceManager::Get().Load(m_materialPath.c_str()); if (!Resources::ResourceManager::Get().TryGetAssetRef( m_materialPath.c_str(), Resources::ResourceType::Material, m_materialRef)) { m_materialRef.Reset(); } } void VolumeRendererComponent::SetMaterial( const Resources::ResourceHandle& material) { m_pendingMaterialLoad.reset(); m_asyncMaterialLoadRequested = false; m_material = material; m_materialPath = MaterialPathFromHandle(material); if (m_materialPath.empty() || !Resources::ResourceManager::Get().TryGetAssetRef( m_materialPath.c_str(), Resources::ResourceType::Material, m_materialRef)) { m_materialRef.Reset(); } } void VolumeRendererComponent::SetMaterial(Resources::Material* material) { SetMaterial(Resources::ResourceHandle(material)); } void VolumeRendererComponent::ClearMaterial() { m_pendingMaterialLoad.reset(); m_asyncMaterialLoadRequested = false; m_material.Reset(); m_materialPath.clear(); m_materialRef.Reset(); } void VolumeRendererComponent::Serialize(std::ostream& os) const { Resources::AssetRef volumeFieldRef = m_volumeFieldRef; if (!volumeFieldRef.IsValid() && !m_volumeFieldPath.empty() && !HasVirtualPathScheme(m_volumeFieldPath) && Resources::ResourceManager::Get().TryGetAssetRef( m_volumeFieldPath.c_str(), Resources::ResourceType::VolumeField, volumeFieldRef)) { } Resources::AssetRef materialRef = m_materialRef; if (!materialRef.IsValid() && !m_materialPath.empty() && !HasVirtualPathScheme(m_materialPath) && Resources::ResourceManager::Get().TryGetAssetRef( m_materialPath.c_str(), Resources::ResourceType::Material, materialRef)) { } os << "volumeRef=" << EncodeAssetRef(volumeFieldRef) << ";"; if (!volumeFieldRef.IsValid() && !m_volumeFieldPath.empty() && HasVirtualPathScheme(m_volumeFieldPath)) { os << "volumePath=" << m_volumeFieldPath << ";"; } os << "materialRef=" << EncodeAssetRef(materialRef) << ";"; if (!materialRef.IsValid() && !m_materialPath.empty() && HasVirtualPathScheme(m_materialPath)) { os << "materialPath=" << m_materialPath << ";"; } os << "castShadows=" << (m_castShadows ? 1 : 0) << ";"; os << "receiveShadows=" << (m_receiveShadows ? 1 : 0) << ";"; } void VolumeRendererComponent::Deserialize(std::istream& is) { ClearVolumeField(); ClearMaterial(); m_castShadows = true; m_receiveShadows = true; std::string token; std::string pendingVolumePath; Resources::AssetRef pendingVolumeRef; std::string pendingMaterialPath; Resources::AssetRef pendingMaterialRef; while (std::getline(is, token, ';')) { if (token.empty()) { continue; } const size_t eqPos = token.find('='); if (eqPos == std::string::npos) { continue; } const std::string key = token.substr(0, eqPos); const std::string value = token.substr(eqPos + 1); if (key == "volumePath") { pendingVolumePath = value; } else if (key == "volumeRef") { TryDecodeAssetRef(value, pendingVolumeRef); } else if (key == "materialPath") { pendingMaterialPath = value; } else if (key == "materialRef") { TryDecodeAssetRef(value, pendingMaterialRef); } else if (key == "castShadows") { m_castShadows = (std::stoi(value) != 0); } else if (key == "receiveShadows") { m_receiveShadows = (std::stoi(value) != 0); } } if (pendingVolumeRef.IsValid()) { bool restoredOrQueued = false; m_volumeFieldRef = pendingVolumeRef; if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) { Containers::String resolvedPath; if (Resources::ResourceManager::Get().TryResolveAssetPath(pendingVolumeRef, resolvedPath)) { m_volumeFieldPath = ToStdString(resolvedPath); restoredOrQueued = true; } } if (!restoredOrQueued) { m_volumeField = Resources::ResourceManager::Get().Load(pendingVolumeRef); m_volumeFieldPath = m_volumeField.Get() != nullptr ? VolumeFieldPathFromHandle(m_volumeField) : pendingVolumePath; } } else if (!pendingVolumePath.empty() && HasVirtualPathScheme(pendingVolumePath)) { if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) { m_volumeFieldPath = pendingVolumePath; } else { SetVolumeFieldPath(pendingVolumePath); } } if (pendingMaterialRef.IsValid()) { bool restoredOrQueued = false; m_materialRef = pendingMaterialRef; if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) { Containers::String resolvedPath; if (Resources::ResourceManager::Get().TryResolveAssetPath(pendingMaterialRef, resolvedPath)) { m_materialPath = ToStdString(resolvedPath); restoredOrQueued = true; } } if (!restoredOrQueued) { m_material = Resources::ResourceManager::Get().Load(pendingMaterialRef); m_materialPath = m_material.Get() != nullptr ? MaterialPathFromHandle(m_material) : pendingMaterialPath; } } else if (!pendingMaterialPath.empty() && HasVirtualPathScheme(pendingMaterialPath)) { if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) { m_materialPath = pendingMaterialPath; } else { SetMaterialPath(pendingMaterialPath); } } } void VolumeRendererComponent::BeginAsyncVolumeLoad(const std::string& volumeFieldPath) { if (volumeFieldPath.empty()) { m_pendingVolumeLoad.reset(); m_asyncVolumeLoadRequested = false; m_volumeField.Reset(); return; } m_asyncVolumeLoadRequested = true; m_volumeField.Reset(); m_pendingVolumeLoad = std::make_shared(); std::weak_ptr weakState = m_pendingVolumeLoad; Resources::ResourceManager::Get().LoadAsync( volumeFieldPath.c_str(), Resources::ResourceType::VolumeField, [weakState](Resources::LoadResult result) { if (std::shared_ptr state = weakState.lock()) { state->result = std::move(result); state->completed = true; } }); } void VolumeRendererComponent::EnsureDeferredAsyncVolumeLoadStarted() { if (m_asyncVolumeLoadRequested || m_volumeField.Get() != nullptr || m_volumeFieldPath.empty()) { return; } BeginAsyncVolumeLoad(m_volumeFieldPath); } void VolumeRendererComponent::ResolvePendingVolumeField() { if (!m_pendingVolumeLoad || !m_pendingVolumeLoad->completed) { return; } std::shared_ptr completedLoad = std::move(m_pendingVolumeLoad); m_pendingVolumeLoad.reset(); if (!completedLoad->result || completedLoad->result.resource == nullptr) { return; } m_volumeField = Resources::ResourceHandle( static_cast(completedLoad->result.resource)); if (m_volumeField.Get() == nullptr) { return; } m_volumeFieldPath = VolumeFieldPathFromHandle(m_volumeField); if (!Resources::ResourceManager::Get().TryGetAssetRef( m_volumeFieldPath.c_str(), Resources::ResourceType::VolumeField, m_volumeFieldRef)) { m_volumeFieldRef.Reset(); } } void VolumeRendererComponent::BeginAsyncMaterialLoad(const std::string& materialPath) { if (materialPath.empty()) { m_pendingMaterialLoad.reset(); m_asyncMaterialLoadRequested = false; m_material.Reset(); return; } m_asyncMaterialLoadRequested = true; m_material.Reset(); m_pendingMaterialLoad = std::make_shared(); std::weak_ptr weakState = m_pendingMaterialLoad; Resources::ResourceManager::Get().LoadAsync( materialPath.c_str(), Resources::ResourceType::Material, [weakState](Resources::LoadResult result) { if (std::shared_ptr state = weakState.lock()) { state->result = std::move(result); state->completed = true; } }); } void VolumeRendererComponent::EnsureDeferredAsyncMaterialLoadStarted() { if (m_asyncMaterialLoadRequested || m_material.Get() != nullptr || m_materialPath.empty()) { return; } BeginAsyncMaterialLoad(m_materialPath); } void VolumeRendererComponent::ResolvePendingMaterial() { if (!m_pendingMaterialLoad || !m_pendingMaterialLoad->completed) { return; } std::shared_ptr completedLoad = std::move(m_pendingMaterialLoad); m_pendingMaterialLoad.reset(); if (!completedLoad->result || completedLoad->result.resource == nullptr) { return; } m_material = Resources::ResourceHandle( static_cast(completedLoad->result.resource)); if (m_material.Get() == nullptr) { return; } m_materialPath = MaterialPathFromHandle(m_material); if (!Resources::ResourceManager::Get().TryGetAssetRef( m_materialPath.c_str(), Resources::ResourceType::Material, m_materialRef)) { m_materialRef.Reset(); } } } // namespace Components } // namespace XCEngine