#include "Components/GaussianSplatRendererComponent.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 GaussianSplatPathFromHandle(const Resources::ResourceHandle& gaussianSplat) { return gaussianSplat.Get() != nullptr ? ToStdString(gaussianSplat->GetPath()) : std::string(); } std::string MaterialPathFromHandle(const Resources::ResourceHandle& material) { return material.Get() != nullptr ? ToStdString(material->GetPath()) : std::string(); } } // namespace struct GaussianSplatRendererComponent::PendingGaussianSplatLoadState { Resources::LoadResult result; bool completed = false; }; struct GaussianSplatRendererComponent::PendingMaterialLoadState { Resources::LoadResult result; bool completed = false; }; Resources::GaussianSplat* GaussianSplatRendererComponent::GetGaussianSplat() const { const_cast(this)->EnsureDeferredAsyncGaussianSplatLoadStarted(); const_cast(this)->ResolvePendingGaussianSplat(); return m_gaussianSplat.Get(); } const Resources::ResourceHandle& GaussianSplatRendererComponent::GetGaussianSplatHandle() const { const_cast(this)->EnsureDeferredAsyncGaussianSplatLoadStarted(); const_cast(this)->ResolvePendingGaussianSplat(); return m_gaussianSplat; } void GaussianSplatRendererComponent::SetGaussianSplatPath(const std::string& gaussianSplatPath) { m_pendingGaussianSplatLoad.reset(); m_asyncGaussianSplatLoadRequested = false; m_gaussianSplatPath = gaussianSplatPath; if (m_gaussianSplatPath.empty()) { m_gaussianSplat.Reset(); m_gaussianSplatRef.Reset(); return; } m_gaussianSplat = Resources::ResourceManager::Get().Load(m_gaussianSplatPath.c_str()); if (!Resources::ResourceManager::Get().TryGetAssetRef( m_gaussianSplatPath.c_str(), Resources::ResourceType::GaussianSplat, m_gaussianSplatRef)) { m_gaussianSplatRef.Reset(); } } void GaussianSplatRendererComponent::SetGaussianSplat( const Resources::ResourceHandle& gaussianSplat) { m_pendingGaussianSplatLoad.reset(); m_asyncGaussianSplatLoadRequested = false; m_gaussianSplat = gaussianSplat; m_gaussianSplatPath = GaussianSplatPathFromHandle(gaussianSplat); if (m_gaussianSplatPath.empty() || !Resources::ResourceManager::Get().TryGetAssetRef( m_gaussianSplatPath.c_str(), Resources::ResourceType::GaussianSplat, m_gaussianSplatRef)) { m_gaussianSplatRef.Reset(); } } void GaussianSplatRendererComponent::SetGaussianSplat(Resources::GaussianSplat* gaussianSplat) { SetGaussianSplat(Resources::ResourceHandle(gaussianSplat)); } void GaussianSplatRendererComponent::ClearGaussianSplat() { m_pendingGaussianSplatLoad.reset(); m_asyncGaussianSplatLoadRequested = false; m_gaussianSplat.Reset(); m_gaussianSplatPath.clear(); m_gaussianSplatRef.Reset(); } Resources::Material* GaussianSplatRendererComponent::GetMaterial() const { const_cast(this)->EnsureDeferredAsyncMaterialLoadStarted(); const_cast(this)->ResolvePendingMaterial(); return m_material.Get(); } const Resources::ResourceHandle& GaussianSplatRendererComponent::GetMaterialHandle() const { const_cast(this)->EnsureDeferredAsyncMaterialLoadStarted(); const_cast(this)->ResolvePendingMaterial(); return m_material; } void GaussianSplatRendererComponent::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 GaussianSplatRendererComponent::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 GaussianSplatRendererComponent::SetMaterial(Resources::Material* material) { SetMaterial(Resources::ResourceHandle(material)); } void GaussianSplatRendererComponent::ClearMaterial() { m_pendingMaterialLoad.reset(); m_asyncMaterialLoadRequested = false; m_material.Reset(); m_materialPath.clear(); m_materialRef.Reset(); } void GaussianSplatRendererComponent::Serialize(std::ostream& os) const { Resources::AssetRef gaussianSplatRef = m_gaussianSplatRef; if (!gaussianSplatRef.IsValid() && !m_gaussianSplatPath.empty() && !HasVirtualPathScheme(m_gaussianSplatPath) && Resources::ResourceManager::Get().TryGetAssetRef( m_gaussianSplatPath.c_str(), Resources::ResourceType::GaussianSplat, gaussianSplatRef)) { } 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 << "gaussianSplatRef=" << EncodeAssetRef(gaussianSplatRef) << ";"; if (!gaussianSplatRef.IsValid() && !m_gaussianSplatPath.empty() && HasVirtualPathScheme(m_gaussianSplatPath)) { os << "gaussianSplatPath=" << m_gaussianSplatPath << ";"; } 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 GaussianSplatRendererComponent::Deserialize(std::istream& is) { ClearGaussianSplat(); ClearMaterial(); m_castShadows = true; m_receiveShadows = true; std::string token; std::string pendingGaussianSplatPath; Resources::AssetRef pendingGaussianSplatRef; 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 == "gaussianSplatPath") { pendingGaussianSplatPath = value; } else if (key == "gaussianSplatRef") { TryDecodeAssetRef(value, pendingGaussianSplatRef); } 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 (pendingGaussianSplatRef.IsValid()) { bool restoredOrQueued = false; m_gaussianSplatRef = pendingGaussianSplatRef; if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) { Containers::String resolvedPath; if (Resources::ResourceManager::Get().TryResolveAssetPath(pendingGaussianSplatRef, resolvedPath)) { m_gaussianSplatPath = ToStdString(resolvedPath); restoredOrQueued = true; } } if (!restoredOrQueued) { m_gaussianSplat = Resources::ResourceManager::Get().Load(pendingGaussianSplatRef); m_gaussianSplatPath = m_gaussianSplat.Get() != nullptr ? GaussianSplatPathFromHandle(m_gaussianSplat) : pendingGaussianSplatPath; } } else if (!pendingGaussianSplatPath.empty() && HasVirtualPathScheme(pendingGaussianSplatPath)) { if (Resources::ResourceManager::Get().IsDeferredSceneLoadEnabled()) { m_gaussianSplatPath = pendingGaussianSplatPath; } else { SetGaussianSplatPath(pendingGaussianSplatPath); } } 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 GaussianSplatRendererComponent::BeginAsyncGaussianSplatLoad(const std::string& gaussianSplatPath) { if (gaussianSplatPath.empty()) { m_pendingGaussianSplatLoad.reset(); m_asyncGaussianSplatLoadRequested = false; m_gaussianSplat.Reset(); return; } m_asyncGaussianSplatLoadRequested = true; m_gaussianSplat.Reset(); m_pendingGaussianSplatLoad = std::make_shared(); std::weak_ptr weakState = m_pendingGaussianSplatLoad; Resources::ResourceManager::Get().LoadAsync( gaussianSplatPath.c_str(), Resources::ResourceType::GaussianSplat, [weakState](Resources::LoadResult result) { if (std::shared_ptr state = weakState.lock()) { state->result = std::move(result); state->completed = true; } }); } void GaussianSplatRendererComponent::EnsureDeferredAsyncGaussianSplatLoadStarted() { if (m_asyncGaussianSplatLoadRequested || m_gaussianSplat.Get() != nullptr || m_gaussianSplatPath.empty()) { return; } BeginAsyncGaussianSplatLoad(m_gaussianSplatPath); } void GaussianSplatRendererComponent::ResolvePendingGaussianSplat() { if (!m_pendingGaussianSplatLoad || !m_pendingGaussianSplatLoad->completed) { return; } std::shared_ptr completedLoad = std::move(m_pendingGaussianSplatLoad); m_pendingGaussianSplatLoad.reset(); if (!completedLoad->result || completedLoad->result.resource == nullptr) { return; } m_gaussianSplat = Resources::ResourceHandle( static_cast(completedLoad->result.resource)); if (m_gaussianSplat.Get() == nullptr) { return; } m_gaussianSplatPath = GaussianSplatPathFromHandle(m_gaussianSplat); if (!Resources::ResourceManager::Get().TryGetAssetRef( m_gaussianSplatPath.c_str(), Resources::ResourceType::GaussianSplat, m_gaussianSplatRef)) { m_gaussianSplatRef.Reset(); } } void GaussianSplatRendererComponent::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 GaussianSplatRendererComponent::EnsureDeferredAsyncMaterialLoadStarted() { if (m_asyncMaterialLoadRequested || m_material.Get() != nullptr || m_materialPath.empty()) { return; } BeginAsyncMaterialLoad(m_materialPath); } void GaussianSplatRendererComponent::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