#include "Components/CameraComponent.h" #include "Core/Asset/ResourceManager.h" #include "Resources/Material/Material.h" #include #include 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; } outRef.assetGuid = Resources::AssetGUID::ParseOrDefault(Containers::String(value.substr(0, firstComma).c_str())); 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(); } } // namespace void CameraComponent::SetFieldOfView(float value) { m_fieldOfView = std::clamp(value, 1.0f, 179.0f); } void CameraComponent::SetOrthographicSize(float value) { m_orthographicSize = std::max(0.001f, value); } void CameraComponent::SetNearClipPlane(float value) { m_nearClipPlane = std::max(0.001f, value); if (m_farClipPlane <= m_nearClipPlane) { m_farClipPlane = m_nearClipPlane + 0.001f; } } void CameraComponent::SetFarClipPlane(float value) { m_farClipPlane = std::max(m_nearClipPlane + 0.001f, value); } void CameraComponent::SetViewportRect(const Math::Rect& value) { const float x = std::clamp(value.x, 0.0f, 1.0f); const float y = std::clamp(value.y, 0.0f, 1.0f); const float width = std::clamp(value.width, 0.0f, 1.0f); const float height = std::clamp(value.height, 0.0f, 1.0f); const float right = std::min(1.0f, x + width); const float bottom = std::min(1.0f, y + height); m_viewportRect = Math::Rect(x, y, right - x, bottom - y); } void CameraComponent::SetSkyboxMaterialPath(const std::string& materialPath) { m_skyboxMaterialPath = materialPath; m_skyboxMaterialRef.Reset(); if (materialPath.empty()) { m_skyboxMaterial.Reset(); return; } m_skyboxMaterial = Resources::ResourceManager::Get().Load(materialPath.c_str()); if (!Resources::ResourceManager::Get().TryGetAssetRef( materialPath.c_str(), Resources::ResourceType::Material, m_skyboxMaterialRef)) { m_skyboxMaterialRef.Reset(); } } void CameraComponent::SetSkyboxMaterial(const Resources::ResourceHandle& material) { m_skyboxMaterial = material; m_skyboxMaterialPath = material.Get() != nullptr ? ToStdString(material->GetPath()) : std::string(); if (m_skyboxMaterialPath.empty() || !Resources::ResourceManager::Get().TryGetAssetRef( m_skyboxMaterialPath.c_str(), Resources::ResourceType::Material, m_skyboxMaterialRef)) { m_skyboxMaterialRef.Reset(); } } void CameraComponent::SetSkyboxMaterial(Resources::Material* material) { SetSkyboxMaterial(Resources::ResourceHandle(material)); } void CameraComponent::Serialize(std::ostream& os) const { Resources::AssetRef serializedSkyboxMaterialRef = m_skyboxMaterialRef; std::string serializedSkyboxMaterialPath = m_skyboxMaterialPath; if (serializedSkyboxMaterialPath.empty() && m_skyboxMaterial.Get() != nullptr) { serializedSkyboxMaterialPath = ToStdString(m_skyboxMaterial->GetPath()); } if (!serializedSkyboxMaterialRef.IsValid() && !serializedSkyboxMaterialPath.empty() && !HasVirtualPathScheme(serializedSkyboxMaterialPath) && Resources::ResourceManager::Get().TryGetAssetRef( serializedSkyboxMaterialPath.c_str(), Resources::ResourceType::Material, serializedSkyboxMaterialRef)) { } if (serializedSkyboxMaterialRef.IsValid() || !HasVirtualPathScheme(serializedSkyboxMaterialPath)) { serializedSkyboxMaterialPath.clear(); } os << "projection=" << static_cast(m_projectionType) << ";"; os << "fov=" << m_fieldOfView << ";"; os << "orthoSize=" << m_orthographicSize << ";"; os << "near=" << m_nearClipPlane << ";"; os << "far=" << m_farClipPlane << ";"; os << "depth=" << m_depth << ";"; os << "primary=" << (m_primary ? 1 : 0) << ";"; os << "clearMode=" << static_cast(m_clearMode) << ";"; os << "stackType=" << static_cast(m_stackType) << ";"; os << "cullingMask=" << m_cullingMask << ";"; os << "viewportRect=" << m_viewportRect.x << "," << m_viewportRect.y << "," << m_viewportRect.width << "," << m_viewportRect.height << ";"; os << "clearColor=" << m_clearColor.r << "," << m_clearColor.g << "," << m_clearColor.b << "," << m_clearColor.a << ";"; os << "skyboxEnabled=" << (m_skyboxEnabled ? 1 : 0) << ";"; os << "skyboxMaterialPath=" << serializedSkyboxMaterialPath << ";"; os << "skyboxMaterialRef=" << EncodeAssetRef(serializedSkyboxMaterialRef) << ";"; os << "skyboxTopColor=" << m_skyboxTopColor.r << "," << m_skyboxTopColor.g << "," << m_skyboxTopColor.b << "," << m_skyboxTopColor.a << ";"; os << "skyboxHorizonColor=" << m_skyboxHorizonColor.r << "," << m_skyboxHorizonColor.g << "," << m_skyboxHorizonColor.b << "," << m_skyboxHorizonColor.a << ";"; os << "skyboxBottomColor=" << m_skyboxBottomColor.r << "," << m_skyboxBottomColor.g << "," << m_skyboxBottomColor.b << "," << m_skyboxBottomColor.a << ";"; } void CameraComponent::Deserialize(std::istream& is) { m_skyboxMaterial.Reset(); m_skyboxMaterialPath.clear(); m_skyboxMaterialRef.Reset(); std::string token; std::string pendingSkyboxMaterialPath; Resources::AssetRef pendingSkyboxMaterialRef; 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); std::string value = token.substr(eqPos + 1); if (key == "projection") { m_projectionType = static_cast(std::stoi(value)); } else if (key == "fov") { SetFieldOfView(std::stof(value)); } else if (key == "orthoSize") { SetOrthographicSize(std::stof(value)); } else if (key == "near") { SetNearClipPlane(std::stof(value)); } else if (key == "far") { SetFarClipPlane(std::stof(value)); } else if (key == "depth") { m_depth = std::stof(value); } else if (key == "primary") { m_primary = (std::stoi(value) != 0); } else if (key == "clearMode") { m_clearMode = static_cast(std::stoi(value)); } else if (key == "stackType") { m_stackType = static_cast(std::stoi(value)); } else if (key == "cullingMask") { m_cullingMask = static_cast(std::stoul(value)); } else if (key == "viewportRect") { std::replace(value.begin(), value.end(), ',', ' '); std::istringstream ss(value); Math::Rect viewportRect; ss >> viewportRect.x >> viewportRect.y >> viewportRect.width >> viewportRect.height; SetViewportRect(viewportRect); } else if (key == "clearColor") { std::replace(value.begin(), value.end(), ',', ' '); std::istringstream ss(value); ss >> m_clearColor.r >> m_clearColor.g >> m_clearColor.b >> m_clearColor.a; } else if (key == "skyboxEnabled") { m_skyboxEnabled = (std::stoi(value) != 0); } else if (key == "skyboxMaterialPath") { pendingSkyboxMaterialPath = value; } else if (key == "skyboxMaterialRef") { TryDecodeAssetRef(value, pendingSkyboxMaterialRef); } else if (key == "skyboxTopColor") { std::replace(value.begin(), value.end(), ',', ' '); std::istringstream ss(value); ss >> m_skyboxTopColor.r >> m_skyboxTopColor.g >> m_skyboxTopColor.b >> m_skyboxTopColor.a; } else if (key == "skyboxHorizonColor") { std::replace(value.begin(), value.end(), ',', ' '); std::istringstream ss(value); ss >> m_skyboxHorizonColor.r >> m_skyboxHorizonColor.g >> m_skyboxHorizonColor.b >> m_skyboxHorizonColor.a; } else if (key == "skyboxBottomColor") { std::replace(value.begin(), value.end(), ',', ' '); std::istringstream ss(value); ss >> m_skyboxBottomColor.r >> m_skyboxBottomColor.g >> m_skyboxBottomColor.b >> m_skyboxBottomColor.a; } } if (pendingSkyboxMaterialRef.IsValid()) { m_skyboxMaterialRef = pendingSkyboxMaterialRef; m_skyboxMaterial = Resources::ResourceManager::Get().Load(pendingSkyboxMaterialRef); if (m_skyboxMaterial.Get() != nullptr) { m_skyboxMaterialPath = ToStdString(m_skyboxMaterial->GetPath()); } else { Containers::String resolvedPath; if (Resources::ResourceManager::Get().TryResolveAssetPath(pendingSkyboxMaterialRef, resolvedPath)) { SetSkyboxMaterialPath(ToStdString(resolvedPath)); m_skyboxMaterialRef = pendingSkyboxMaterialRef; } } } if (m_skyboxMaterial.Get() == nullptr && !pendingSkyboxMaterialPath.empty()) { SetSkyboxMaterialPath(pendingSkyboxMaterialPath); } if (m_skyboxMaterial.Get() == nullptr && pendingSkyboxMaterialRef.IsValid()) { m_skyboxMaterialRef = pendingSkyboxMaterialRef; } } } // namespace Components } // namespace XCEngine