refactor: advance runtime and pipeline architecture split
This commit is contained in:
File diff suppressed because it is too large
Load Diff
211
Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h
Normal file
211
Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h
Normal file
@@ -0,0 +1,211 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/AssetRef.h>
|
||||
#include <XCEngine/Core/Asset/ProjectAssetTypes.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/Resources/UI/UIDocumentTypes.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class Mesh;
|
||||
class Material;
|
||||
namespace AssetDatabaseInternal {
|
||||
class AssetDatabaseImportRunner;
|
||||
class AssetDatabaseMaintenanceRunner;
|
||||
class SourceAssetDB;
|
||||
class ArtifactDB;
|
||||
}
|
||||
|
||||
class AssetDatabase {
|
||||
public:
|
||||
AssetDatabase();
|
||||
~AssetDatabase();
|
||||
|
||||
struct MaintenanceStats {
|
||||
Core::uint32 importedAssetCount = 0;
|
||||
Core::uint32 removedArtifactCount = 0;
|
||||
};
|
||||
|
||||
struct ArtifactDependencyRecord {
|
||||
Containers::String path;
|
||||
Containers::String hash;
|
||||
Core::uint64 fileSize = 0;
|
||||
Core::uint64 writeTime = 0;
|
||||
};
|
||||
|
||||
struct SourceAssetRecord {
|
||||
AssetGUID guid;
|
||||
Containers::String relativePath;
|
||||
Containers::String metaPath;
|
||||
bool isFolder = false;
|
||||
Containers::String importerName;
|
||||
Core::uint32 importerVersion = 0;
|
||||
Containers::String metaHash;
|
||||
Containers::String sourceHash;
|
||||
Core::uint64 sourceFileSize = 0;
|
||||
Core::uint64 sourceWriteTime = 0;
|
||||
Containers::String lastKnownArtifactKey;
|
||||
};
|
||||
|
||||
struct ArtifactRecord {
|
||||
Containers::String artifactKey;
|
||||
AssetGUID assetGuid;
|
||||
Containers::String importerName;
|
||||
Core::uint32 importerVersion = 0;
|
||||
ResourceType resourceType = ResourceType::Unknown;
|
||||
ArtifactStorageKind storageKind = ArtifactStorageKind::Unknown;
|
||||
Containers::String artifactDirectory;
|
||||
Containers::String mainArtifactPath;
|
||||
Containers::String mainEntryName;
|
||||
Containers::String sourceHash;
|
||||
Containers::String metaHash;
|
||||
Core::uint64 sourceFileSize = 0;
|
||||
Core::uint64 sourceWriteTime = 0;
|
||||
LocalID mainLocalID = kMainAssetLocalID;
|
||||
std::vector<ArtifactDependencyRecord> dependencies;
|
||||
};
|
||||
|
||||
struct ResolvedAsset {
|
||||
bool exists = false;
|
||||
bool artifactReady = false;
|
||||
bool imported = false;
|
||||
Containers::String absolutePath;
|
||||
Containers::String relativePath;
|
||||
AssetGUID assetGuid;
|
||||
ResourceType resourceType = ResourceType::Unknown;
|
||||
Containers::String artifactMainPath;
|
||||
Containers::String artifactMainEntryPath;
|
||||
Containers::String artifactDirectory;
|
||||
ArtifactStorageKind artifactStorageKind = ArtifactStorageKind::Unknown;
|
||||
Containers::String mainEntryName;
|
||||
LocalID mainLocalID = kMainAssetLocalID;
|
||||
};
|
||||
|
||||
void Initialize(const Containers::String& projectRoot);
|
||||
void Shutdown();
|
||||
MaintenanceStats Refresh();
|
||||
|
||||
bool ResolvePath(const Containers::String& requestPath,
|
||||
Containers::String& outAbsolutePath,
|
||||
Containers::String& outRelativePath) const;
|
||||
bool TryGetAssetGuid(const Containers::String& requestPath, AssetGUID& outGuid) const;
|
||||
bool TryGetImportableResourceType(const Containers::String& requestPath, ResourceType& outType) const;
|
||||
bool TryGetAssetRef(const Containers::String& requestPath, ResourceType resourceType, AssetRef& outRef) const;
|
||||
bool TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath);
|
||||
bool ReimportAsset(const Containers::String& requestPath,
|
||||
ResolvedAsset& outAsset,
|
||||
MaintenanceStats* outStats = nullptr);
|
||||
bool ReimportAllAssets(MaintenanceStats* outStats = nullptr);
|
||||
bool EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
ResolvedAsset& outAsset);
|
||||
bool TryGetPrimaryAssetPath(const AssetGUID& guid, Containers::String& outRelativePath) const;
|
||||
void BuildLookupSnapshot(std::unordered_map<std::string, AssetGUID>& outPathToGuid,
|
||||
std::unordered_map<AssetGUID, Containers::String>& outGuidToPath) const;
|
||||
|
||||
const Containers::String& GetProjectRoot() const { return m_projectRoot; }
|
||||
const Containers::String& GetAssetsRoot() const { return m_assetsRoot; }
|
||||
const Containers::String& GetLibraryRoot() const { return m_libraryRoot; }
|
||||
const Containers::String& GetLastErrorMessage() const { return m_lastErrorMessage; }
|
||||
|
||||
private:
|
||||
friend class AssetDatabaseInternal::AssetDatabaseImportRunner;
|
||||
friend class AssetDatabaseInternal::AssetDatabaseMaintenanceRunner;
|
||||
|
||||
static constexpr Core::uint32 kBaseImporterVersion = 7;
|
||||
|
||||
void EnsureProjectLayout();
|
||||
void LoadSourceAssetDB();
|
||||
void SaveSourceAssetDB() const;
|
||||
void LoadArtifactDB();
|
||||
void SaveArtifactDB() const;
|
||||
|
||||
bool EnsureMetaForPath(const std::filesystem::path& sourcePath,
|
||||
bool isFolder,
|
||||
SourceAssetRecord& outRecord);
|
||||
bool ReadMetaFile(const std::filesystem::path& metaPath,
|
||||
SourceAssetRecord& inOutRecord) const;
|
||||
void WriteMetaFile(const std::filesystem::path& metaPath,
|
||||
const SourceAssetRecord& record) const;
|
||||
|
||||
Containers::String NormalizeRelativePath(const std::filesystem::path& sourcePath) const;
|
||||
static Containers::String NormalizePathString(const std::filesystem::path& path);
|
||||
static Containers::String NormalizePathString(const Containers::String& path);
|
||||
static Containers::String MakeKey(const Containers::String& path);
|
||||
static Containers::String GetImporterNameForPath(const Containers::String& relativePath, bool isFolder);
|
||||
static Core::uint32 GetCurrentImporterVersion(const Containers::String& importerName);
|
||||
static ResourceType GetPrimaryResourceTypeForImporter(const Containers::String& importerName);
|
||||
|
||||
bool ShouldReimport(const SourceAssetRecord& sourceRecord,
|
||||
const ArtifactRecord* artifactRecord) const;
|
||||
bool ImportAsset(const SourceAssetRecord& sourceRecord,
|
||||
ArtifactRecord& outRecord);
|
||||
bool ImportTextureAsset(const SourceAssetRecord& sourceRecord,
|
||||
ArtifactRecord& outRecord);
|
||||
bool ImportMaterialAsset(const SourceAssetRecord& sourceRecord,
|
||||
ArtifactRecord& outRecord);
|
||||
bool ImportModelAsset(const SourceAssetRecord& sourceRecord,
|
||||
ArtifactRecord& outRecord);
|
||||
bool ImportShaderAsset(const SourceAssetRecord& sourceRecord,
|
||||
ArtifactRecord& outRecord);
|
||||
bool ImportGaussianSplatAsset(const SourceAssetRecord& sourceRecord,
|
||||
ArtifactRecord& outRecord);
|
||||
bool ImportVolumeFieldAsset(const SourceAssetRecord& sourceRecord,
|
||||
ArtifactRecord& outRecord);
|
||||
bool ImportUIDocumentAsset(const SourceAssetRecord& sourceRecord,
|
||||
UIDocumentKind kind,
|
||||
const char* artifactFileName,
|
||||
ResourceType resourceType,
|
||||
ArtifactRecord& outRecord);
|
||||
|
||||
Containers::String BuildArtifactKey(
|
||||
const SourceAssetRecord& sourceRecord,
|
||||
const std::vector<ArtifactDependencyRecord>& dependencies = {}) const;
|
||||
Containers::String BuildArtifactDirectory(const Containers::String& artifactKey) const;
|
||||
Containers::String BuildArtifactFilePath(const Containers::String& artifactKey,
|
||||
const char* extension) const;
|
||||
static Containers::String ReadWholeFileText(const std::filesystem::path& path);
|
||||
static Containers::String ComputeFileHash(const std::filesystem::path& path);
|
||||
static Core::uint64 GetFileSizeValue(const std::filesystem::path& path);
|
||||
static Core::uint64 GetFileWriteTimeValue(const std::filesystem::path& path);
|
||||
Containers::String NormalizeDependencyPath(const std::filesystem::path& path) const;
|
||||
std::filesystem::path ResolveDependencyPath(const Containers::String& path) const;
|
||||
bool CaptureDependencyRecord(const std::filesystem::path& path,
|
||||
ArtifactDependencyRecord& outRecord) const;
|
||||
bool AreDependenciesCurrent(const std::vector<ArtifactDependencyRecord>& dependencies) const;
|
||||
bool CollectModelDependencies(const SourceAssetRecord& sourceRecord,
|
||||
const std::vector<Containers::String>& importedTexturePaths,
|
||||
std::vector<ArtifactDependencyRecord>& outDependencies) const;
|
||||
bool CollectMaterialDependencies(const Material& material,
|
||||
std::vector<ArtifactDependencyRecord>& outDependencies) const;
|
||||
bool CollectShaderDependencies(const SourceAssetRecord& sourceRecord,
|
||||
std::vector<ArtifactDependencyRecord>& outDependencies,
|
||||
Containers::String* outError = nullptr) const;
|
||||
void ClearLastErrorMessage();
|
||||
void SetLastErrorMessage(const Containers::String& message);
|
||||
|
||||
AssetDatabaseInternal::SourceAssetDB& GetSourceAssetDB();
|
||||
const AssetDatabaseInternal::SourceAssetDB& GetSourceAssetDB() const;
|
||||
AssetDatabaseInternal::ArtifactDB& GetArtifactDB();
|
||||
const AssetDatabaseInternal::ArtifactDB& GetArtifactDB() const;
|
||||
|
||||
Containers::String m_projectRoot;
|
||||
Containers::String m_assetsRoot;
|
||||
Containers::String m_libraryRoot;
|
||||
Containers::String m_sourceDbPath;
|
||||
Containers::String m_artifactDbPath;
|
||||
Containers::String m_lastErrorMessage;
|
||||
|
||||
std::unique_ptr<AssetDatabaseInternal::SourceAssetDB> m_sourceAssetDB;
|
||||
std::unique_ptr<AssetDatabaseInternal::ArtifactDB> m_artifactDB;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
393
Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.cpp
Normal file
393
Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
#include "Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.h"
|
||||
|
||||
#include "Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.h"
|
||||
#include "Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h"
|
||||
#include "Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h"
|
||||
|
||||
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
AssetDatabaseImportRunner::AssetDatabaseImportRunner(AssetDatabase& assetDatabase)
|
||||
: m_assetDatabase(assetDatabase) {
|
||||
}
|
||||
|
||||
bool AssetDatabaseImportRunner::ReimportAsset(const Containers::String& requestPath,
|
||||
AssetDatabase::ResolvedAsset& outAsset,
|
||||
AssetDatabase::MaintenanceStats* outStats) {
|
||||
outAsset = AssetDatabase::ResolvedAsset();
|
||||
m_assetDatabase.ClearLastErrorMessage();
|
||||
if (outStats != nullptr) {
|
||||
*outStats = AssetDatabase::MaintenanceStats();
|
||||
}
|
||||
|
||||
Containers::String absolutePath;
|
||||
Containers::String relativePath;
|
||||
if (!m_assetDatabase.ResolvePath(requestPath, absolutePath, relativePath) || relativePath.Empty()) {
|
||||
m_assetDatabase.SetLastErrorMessage(Containers::String("Unable to resolve asset path: ") + requestPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
const fs::path absoluteFsPath(absolutePath.CStr());
|
||||
if (!fs::exists(absoluteFsPath) || fs::is_directory(absoluteFsPath)) {
|
||||
m_assetDatabase.SetLastErrorMessage(Containers::String("Asset source file does not exist: ") + absolutePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetDatabase::SourceAssetRecord sourceRecord;
|
||||
if (!m_assetDatabase.EnsureMetaForPath(absoluteFsPath, false, sourceRecord)) {
|
||||
m_assetDatabase.SetLastErrorMessage(Containers::String("Failed to prepare asset metadata: ") + absolutePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ResourceType primaryType =
|
||||
AssetDatabase::GetPrimaryResourceTypeForImporter(sourceRecord.importerName);
|
||||
if (primaryType == ResourceType::Unknown) {
|
||||
m_assetDatabase.SetLastErrorMessage(Containers::String("Asset type is not importable: ") + requestPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetDatabase::ArtifactRecord rebuiltRecord;
|
||||
if (!m_assetDatabase.ImportAsset(sourceRecord, rebuiltRecord)) {
|
||||
if (m_assetDatabase.m_lastErrorMessage.Empty()) {
|
||||
m_assetDatabase.SetLastErrorMessage(Containers::String("Failed to import asset: ") + requestPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CommitArtifactRecord(sourceRecord, rebuiltRecord, true);
|
||||
|
||||
AssetDatabase::MaintenanceStats localStats;
|
||||
localStats.importedAssetCount = 1;
|
||||
localStats.removedArtifactCount =
|
||||
AssetDatabaseMaintenanceRunner(m_assetDatabase).CleanupOrphanedArtifacts();
|
||||
if (outStats != nullptr) {
|
||||
*outStats = localStats;
|
||||
}
|
||||
|
||||
PopulateResolvedAssetResult(m_assetDatabase.m_projectRoot, sourceRecord, rebuiltRecord, true, outAsset);
|
||||
m_assetDatabase.ClearLastErrorMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<AssetDatabase::SourceAssetRecord> AssetDatabaseImportRunner::CollectImportableRecords() const {
|
||||
std::vector<AssetDatabase::SourceAssetRecord> importableRecords;
|
||||
const auto& sourceRecordsByGuid = m_assetDatabase.GetSourceAssetDB().GetRecordsByGuid();
|
||||
importableRecords.reserve(sourceRecordsByGuid.size());
|
||||
|
||||
for (const auto& [guid, record] : sourceRecordsByGuid) {
|
||||
(void)guid;
|
||||
if (record.isFolder) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ResourceType primaryType =
|
||||
AssetDatabase::GetPrimaryResourceTypeForImporter(record.importerName);
|
||||
if (primaryType == ResourceType::Unknown) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fs::path sourcePath =
|
||||
fs::path(m_assetDatabase.m_projectRoot.CStr()) / record.relativePath.CStr();
|
||||
if (!fs::exists(sourcePath) || fs::is_directory(sourcePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
importableRecords.push_back(record);
|
||||
}
|
||||
|
||||
std::sort(importableRecords.begin(), importableRecords.end(),
|
||||
[](const AssetDatabase::SourceAssetRecord& lhs,
|
||||
const AssetDatabase::SourceAssetRecord& rhs) {
|
||||
return ToStdString(lhs.relativePath) < ToStdString(rhs.relativePath);
|
||||
});
|
||||
return importableRecords;
|
||||
}
|
||||
|
||||
bool AssetDatabaseImportRunner::ReimportAllAssets(AssetDatabase::MaintenanceStats* outStats) {
|
||||
m_assetDatabase.ClearLastErrorMessage();
|
||||
if (outStats != nullptr) {
|
||||
*outStats = AssetDatabase::MaintenanceStats();
|
||||
}
|
||||
|
||||
const std::vector<AssetDatabase::SourceAssetRecord> importableRecords = CollectImportableRecords();
|
||||
|
||||
bool allSucceeded = true;
|
||||
AssetDatabase::MaintenanceStats localStats;
|
||||
for (const AssetDatabase::SourceAssetRecord& record : importableRecords) {
|
||||
AssetDatabase::ArtifactRecord rebuiltRecord;
|
||||
if (!m_assetDatabase.ImportAsset(record, rebuiltRecord)) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] ReimportAllAssets failed path=") + record.relativePath);
|
||||
allSucceeded = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
CommitArtifactRecord(record, rebuiltRecord, false);
|
||||
++localStats.importedAssetCount;
|
||||
}
|
||||
|
||||
localStats.removedArtifactCount =
|
||||
AssetDatabaseMaintenanceRunner(m_assetDatabase).CleanupOrphanedArtifacts();
|
||||
if (outStats != nullptr) {
|
||||
*outStats = localStats;
|
||||
}
|
||||
return allSucceeded;
|
||||
}
|
||||
|
||||
bool AssetDatabaseImportRunner::EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
AssetDatabase::ResolvedAsset& outAsset) {
|
||||
outAsset = AssetDatabase::ResolvedAsset();
|
||||
m_assetDatabase.ClearLastErrorMessage();
|
||||
|
||||
Containers::String absolutePath;
|
||||
Containers::String relativePath;
|
||||
if (!m_assetDatabase.ResolvePath(requestPath, absolutePath, relativePath) || relativePath.Empty()) {
|
||||
m_assetDatabase.SetLastErrorMessage(Containers::String("Unable to resolve asset path: ") + requestPath);
|
||||
if (ShouldTraceAssetPath(requestPath)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] EnsureArtifact unresolved path=") + requestPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const fs::path absoluteFsPath(absolutePath.CStr());
|
||||
if (!fs::exists(absoluteFsPath)) {
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Asset source file does not exist: ") + absolutePath);
|
||||
if (ShouldTraceAssetPath(requestPath)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] EnsureArtifact missing source path=") +
|
||||
requestPath +
|
||||
" absolute=" +
|
||||
absolutePath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetDatabase::SourceAssetRecord sourceRecord;
|
||||
if (!m_assetDatabase.EnsureMetaForPath(
|
||||
absoluteFsPath,
|
||||
fs::is_directory(absoluteFsPath),
|
||||
sourceRecord)) {
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Failed to prepare asset metadata: ") + absolutePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ShouldTraceAssetPath(requestPath)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] EnsureArtifact source path=") +
|
||||
requestPath +
|
||||
" guid=" +
|
||||
sourceRecord.guid.ToString() +
|
||||
" importer=" +
|
||||
sourceRecord.importerName +
|
||||
" relative=" +
|
||||
sourceRecord.relativePath);
|
||||
}
|
||||
|
||||
const ResourceType primaryType =
|
||||
AssetDatabase::GetPrimaryResourceTypeForImporter(sourceRecord.importerName);
|
||||
if (primaryType == ResourceType::Unknown || primaryType != requestedType) {
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Asset type mismatch for ") +
|
||||
requestPath +
|
||||
": requested " +
|
||||
GetResourceTypeName(requestedType) +
|
||||
", importer produces " +
|
||||
GetResourceTypeName(primaryType));
|
||||
if (ShouldTraceAssetPath(requestPath)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] EnsureArtifact type-mismatch path=") +
|
||||
requestPath +
|
||||
" requested=" +
|
||||
GetResourceTypeName(requestedType) +
|
||||
" importerType=" +
|
||||
GetResourceTypeName(primaryType));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const AssetDatabase::ArtifactRecord* artifactRecord =
|
||||
m_assetDatabase.GetArtifactDB().FindByGuid(sourceRecord.guid);
|
||||
if (m_assetDatabase.ShouldReimport(sourceRecord, artifactRecord)) {
|
||||
if (ShouldTraceAssetPath(requestPath)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] EnsureArtifact reimport path=") + requestPath);
|
||||
}
|
||||
|
||||
AssetDatabase::ArtifactRecord rebuiltRecord;
|
||||
if (!m_assetDatabase.ImportAsset(sourceRecord, rebuiltRecord)) {
|
||||
if (m_assetDatabase.m_lastErrorMessage.Empty()) {
|
||||
m_assetDatabase.SetLastErrorMessage(Containers::String("Failed to import asset: ") + requestPath);
|
||||
}
|
||||
if (ShouldTraceAssetPath(requestPath)) {
|
||||
Debug::Logger::Get().Error(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] EnsureArtifact reimport failed path=") + requestPath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CommitArtifactRecord(sourceRecord, rebuiltRecord, true);
|
||||
artifactRecord = m_assetDatabase.GetArtifactDB().FindByGuid(sourceRecord.guid);
|
||||
outAsset.imported = true;
|
||||
}
|
||||
|
||||
if (artifactRecord == nullptr) {
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Imported asset did not produce an artifact: ") + requestPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
outAsset.exists = true;
|
||||
PopulateResolvedAssetResult(
|
||||
m_assetDatabase.m_projectRoot,
|
||||
sourceRecord,
|
||||
*artifactRecord,
|
||||
outAsset.imported,
|
||||
outAsset);
|
||||
m_assetDatabase.ClearLastErrorMessage();
|
||||
|
||||
if (ShouldTraceAssetPath(requestPath)) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] EnsureArtifact ready path=") +
|
||||
requestPath +
|
||||
" artifactKey=" +
|
||||
artifactRecord->artifactKey +
|
||||
" artifact=" +
|
||||
outAsset.artifactMainPath);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AssetDatabaseImportRunner::TryResolveAssetPath(const AssetRef& assetRef,
|
||||
Containers::String& outPath) {
|
||||
outPath.Clear();
|
||||
m_assetDatabase.ClearLastErrorMessage();
|
||||
|
||||
if (!assetRef.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (assetRef.localID == kMainAssetLocalID) {
|
||||
return m_assetDatabase.TryGetPrimaryAssetPath(assetRef.assetGuid, outPath);
|
||||
}
|
||||
|
||||
const AssetDatabase::SourceAssetRecord* sourceRecord =
|
||||
m_assetDatabase.GetSourceAssetDB().FindByGuid(assetRef.assetGuid);
|
||||
if (sourceRecord == nullptr) {
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Unknown asset GUID for sub-asset path resolution: ") +
|
||||
assetRef.assetGuid.ToString());
|
||||
return false;
|
||||
}
|
||||
|
||||
const ResourceType primaryType =
|
||||
AssetDatabase::GetPrimaryResourceTypeForImporter(sourceRecord->importerName);
|
||||
if (primaryType == ResourceType::Unknown) {
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Asset does not have an importable primary type: ") +
|
||||
sourceRecord->relativePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto resolveFromArtifactRecord =
|
||||
[&](const AssetDatabase::ArtifactRecord& artifactRecord) -> bool {
|
||||
const Containers::String absoluteMainArtifactPath = NormalizeArtifactPathString(
|
||||
fs::path(m_assetDatabase.m_projectRoot.CStr()) /
|
||||
artifactRecord.mainArtifactPath.CStr());
|
||||
|
||||
ArtifactContainerReader reader;
|
||||
Containers::String containerError;
|
||||
if (reader.Open(absoluteMainArtifactPath, &containerError)) {
|
||||
const ArtifactContainerEntryView* entry =
|
||||
reader.FindEntry(assetRef.resourceType, assetRef.localID);
|
||||
if (entry != nullptr) {
|
||||
outPath = BuildArtifactContainerEntryPath(absoluteMainArtifactPath, entry->name);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const fs::path manifestPath =
|
||||
fs::path(m_assetDatabase.m_projectRoot.CStr()) /
|
||||
artifactRecord.artifactDirectory.CStr() /
|
||||
kModelSubAssetManifestFileName;
|
||||
|
||||
Containers::String artifactPath;
|
||||
if (!TryResolveModelSubAssetArtifactPath(manifestPath, assetRef, artifactPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outPath = NormalizeArtifactPathString(
|
||||
fs::path(m_assetDatabase.m_projectRoot.CStr()) / artifactPath.CStr());
|
||||
return true;
|
||||
};
|
||||
|
||||
const AssetDatabase::ArtifactRecord* artifactRecord =
|
||||
m_assetDatabase.GetArtifactDB().FindByGuid(assetRef.assetGuid);
|
||||
if (artifactRecord != nullptr &&
|
||||
!m_assetDatabase.ShouldReimport(*sourceRecord, artifactRecord) &&
|
||||
resolveFromArtifactRecord(*artifactRecord)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
AssetDatabase::ResolvedAsset resolvedAsset;
|
||||
if (!m_assetDatabase.EnsureArtifact(sourceRecord->relativePath, primaryType, resolvedAsset)) {
|
||||
if (m_assetDatabase.m_lastErrorMessage.Empty()) {
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Failed to import asset while resolving sub-asset path: ") +
|
||||
sourceRecord->relativePath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
artifactRecord = m_assetDatabase.GetArtifactDB().FindByGuid(assetRef.assetGuid);
|
||||
if (artifactRecord != nullptr && resolveFromArtifactRecord(*artifactRecord)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
m_assetDatabase.SetLastErrorMessage(
|
||||
Containers::String("Sub-asset localID was not found in artifact manifest: ") +
|
||||
sourceRecord->relativePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
void AssetDatabaseImportRunner::CommitArtifactRecord(
|
||||
const AssetDatabase::SourceAssetRecord& sourceRecord,
|
||||
const AssetDatabase::ArtifactRecord& artifactRecord,
|
||||
bool cleanupOrphans) {
|
||||
m_assetDatabase.GetArtifactDB().Upsert(sourceRecord.guid, artifactRecord);
|
||||
m_assetDatabase.GetSourceAssetDB().SetLastKnownArtifactKey(
|
||||
ToStdString(AssetDatabase::MakeKey(sourceRecord.relativePath)),
|
||||
sourceRecord.guid,
|
||||
artifactRecord.artifactKey);
|
||||
m_assetDatabase.SaveArtifactDB();
|
||||
m_assetDatabase.SaveSourceAssetDB();
|
||||
if (cleanupOrphans) {
|
||||
AssetDatabaseMaintenanceRunner(m_assetDatabase).CleanupOrphanedArtifacts();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
33
Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.h
Normal file
33
Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
class AssetDatabaseImportRunner {
|
||||
public:
|
||||
explicit AssetDatabaseImportRunner(AssetDatabase& assetDatabase);
|
||||
|
||||
bool ReimportAsset(const Containers::String& requestPath,
|
||||
AssetDatabase::ResolvedAsset& outAsset,
|
||||
AssetDatabase::MaintenanceStats* outStats);
|
||||
bool ReimportAllAssets(AssetDatabase::MaintenanceStats* outStats);
|
||||
bool EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
AssetDatabase::ResolvedAsset& outAsset);
|
||||
bool TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath);
|
||||
|
||||
private:
|
||||
void CommitArtifactRecord(const AssetDatabase::SourceAssetRecord& sourceRecord,
|
||||
const AssetDatabase::ArtifactRecord& artifactRecord,
|
||||
bool cleanupOrphans);
|
||||
std::vector<AssetDatabase::SourceAssetRecord> CollectImportableRecords() const;
|
||||
|
||||
AssetDatabase& m_assetDatabase;
|
||||
};
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -0,0 +1,215 @@
|
||||
#include "Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.h"
|
||||
|
||||
#include "Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h"
|
||||
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string ToStdString(const Containers::String& value) {
|
||||
return std::string(value.CStr());
|
||||
}
|
||||
|
||||
std::string ToLowerCopy(std::string text) {
|
||||
std::transform(text.begin(), text.end(), text.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
std::vector<fs::path> CollectBuiltinShaderAssetPaths() {
|
||||
std::vector<fs::path> paths;
|
||||
paths.reserve(14u);
|
||||
|
||||
Containers::String resolvedPath;
|
||||
const Containers::String builtinShaderPaths[] = {
|
||||
GetBuiltinForwardLitShaderPath(),
|
||||
GetBuiltinUnlitShaderPath(),
|
||||
GetBuiltinDepthOnlyShaderPath(),
|
||||
GetBuiltinShadowCasterShaderPath(),
|
||||
#if XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT
|
||||
GetBuiltinObjectIdShaderPath(),
|
||||
#endif
|
||||
GetBuiltinSkyboxShaderPath(),
|
||||
GetBuiltinGaussianSplatShaderPath(),
|
||||
GetBuiltinGaussianSplatUtilitiesShaderPath(),
|
||||
GetBuiltinVolumetricShaderPath(),
|
||||
GetBuiltinColorScalePostProcessShaderPath(),
|
||||
GetBuiltinFinalColorShaderPath()
|
||||
};
|
||||
|
||||
for (const Containers::String& builtinPath : builtinShaderPaths) {
|
||||
if (TryResolveBuiltinShaderAssetPath(builtinPath, resolvedPath) && !resolvedPath.Empty()) {
|
||||
paths.push_back(fs::path(resolvedPath.CStr()));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AssetDatabaseMaintenanceRunner::AssetDatabaseMaintenanceRunner(AssetDatabase& assetDatabase)
|
||||
: m_assetDatabase(assetDatabase) {
|
||||
}
|
||||
|
||||
AssetDatabase::MaintenanceStats AssetDatabaseMaintenanceRunner::Refresh() {
|
||||
AssetDatabase::MaintenanceStats stats;
|
||||
std::unordered_map<std::string, bool> seenPaths;
|
||||
const fs::path assetsRootPath(m_assetDatabase.m_assetsRoot.CStr());
|
||||
if (fs::exists(assetsRootPath)) {
|
||||
ScanAssetPath(assetsRootPath, seenPaths);
|
||||
}
|
||||
for (const fs::path& builtinShaderPath : CollectBuiltinShaderAssetPaths()) {
|
||||
ScanAssetPath(builtinShaderPath, seenPaths);
|
||||
}
|
||||
RemoveMissingRecords(seenPaths);
|
||||
stats.removedArtifactCount = CleanupOrphanedArtifacts();
|
||||
m_assetDatabase.SaveSourceAssetDB();
|
||||
m_assetDatabase.SaveArtifactDB();
|
||||
return stats;
|
||||
}
|
||||
|
||||
void AssetDatabaseMaintenanceRunner::ScanAssetPath(
|
||||
const fs::path& path,
|
||||
std::unordered_map<std::string, bool>& seenPaths) {
|
||||
if (!fs::exists(path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.has_extension() &&
|
||||
ToLowerCopy(path.extension().string()) == ".meta") {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isFolder = fs::is_directory(path);
|
||||
AssetDatabase::SourceAssetRecord record;
|
||||
if (m_assetDatabase.EnsureMetaForPath(path, isFolder, record)) {
|
||||
seenPaths[ToStdString(AssetDatabase::MakeKey(record.relativePath))] = true;
|
||||
}
|
||||
|
||||
if (!isFolder) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(path)) {
|
||||
ScanAssetPath(entry.path(), seenPaths);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetDatabaseMaintenanceRunner::RemoveMissingRecords(
|
||||
const std::unordered_map<std::string, bool>& seenPaths) {
|
||||
std::vector<std::string> missingPathKeys;
|
||||
for (const auto& [pathKey, record] : m_assetDatabase.GetSourceAssetDB().GetRecordsByPathKey()) {
|
||||
(void)record;
|
||||
if (seenPaths.find(pathKey) == seenPaths.end()) {
|
||||
missingPathKeys.push_back(pathKey);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& pathKey : missingPathKeys) {
|
||||
const AssetDatabase::SourceAssetRecord* record =
|
||||
m_assetDatabase.GetSourceAssetDB().FindByPathKey(pathKey);
|
||||
if (record == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_assetDatabase.GetArtifactDB().EraseByGuid(record->guid);
|
||||
m_assetDatabase.GetSourceAssetDB().EraseByPathKey(pathKey);
|
||||
}
|
||||
}
|
||||
|
||||
Core::uint32 AssetDatabaseMaintenanceRunner::CleanupOrphanedArtifacts() const {
|
||||
std::error_code ec;
|
||||
const fs::path artifactsRoot = fs::path(m_assetDatabase.m_libraryRoot.CStr()) / "Artifacts";
|
||||
if (!fs::exists(artifactsRoot, ec) || !fs::is_directory(artifactsRoot, ec)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> retainedArtifactPathKeys;
|
||||
const auto& artifactRecordsByGuid = m_assetDatabase.GetArtifactDB().GetRecordsByGuid();
|
||||
retainedArtifactPathKeys.reserve(artifactRecordsByGuid.size() * 2u);
|
||||
for (const auto& [guid, record] : artifactRecordsByGuid) {
|
||||
(void)guid;
|
||||
if (!record.artifactDirectory.Empty()) {
|
||||
retainedArtifactPathKeys.insert(
|
||||
ToStdString(AssetDatabase::MakeKey(record.artifactDirectory)));
|
||||
}
|
||||
if (!record.mainArtifactPath.Empty()) {
|
||||
retainedArtifactPathKeys.insert(
|
||||
ToStdString(AssetDatabase::MakeKey(record.mainArtifactPath)));
|
||||
}
|
||||
}
|
||||
|
||||
Core::uint32 removedArtifactCount = 0;
|
||||
for (const auto& shardEntry : fs::directory_iterator(artifactsRoot, ec)) {
|
||||
if (ec) {
|
||||
ec.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
const fs::path shardPath = shardEntry.path();
|
||||
if (!shardEntry.is_directory()) {
|
||||
fs::remove_all(shardPath, ec);
|
||||
if (!ec) {
|
||||
++removedArtifactCount;
|
||||
}
|
||||
ec.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& artifactEntry : fs::directory_iterator(shardPath, ec)) {
|
||||
if (ec) {
|
||||
ec.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
const Containers::String relativeArtifactPath =
|
||||
m_assetDatabase.NormalizeRelativePath(artifactEntry.path());
|
||||
const std::string artifactPathKey =
|
||||
ToStdString(AssetDatabase::MakeKey(relativeArtifactPath));
|
||||
if (!relativeArtifactPath.Empty() &&
|
||||
retainedArtifactPathKeys.find(artifactPathKey) != retainedArtifactPathKeys.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::remove_all(artifactEntry.path(), ec);
|
||||
if (!ec) {
|
||||
++removedArtifactCount;
|
||||
}
|
||||
ec.clear();
|
||||
}
|
||||
|
||||
std::error_code shardEc;
|
||||
if (fs::is_empty(shardPath, shardEc)) {
|
||||
fs::remove(shardPath, shardEc);
|
||||
}
|
||||
}
|
||||
|
||||
if (removedArtifactCount > 0) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[AssetDatabase] Removed orphan artifact entries count=") +
|
||||
Containers::String(std::to_string(removedArtifactCount).c_str()));
|
||||
}
|
||||
|
||||
return removedArtifactCount;
|
||||
}
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
class AssetDatabaseMaintenanceRunner {
|
||||
public:
|
||||
explicit AssetDatabaseMaintenanceRunner(AssetDatabase& assetDatabase);
|
||||
|
||||
AssetDatabase::MaintenanceStats Refresh();
|
||||
Core::uint32 CleanupOrphanedArtifacts() const;
|
||||
|
||||
private:
|
||||
void ScanAssetPath(const std::filesystem::path& path,
|
||||
std::unordered_map<std::string, bool>& seenPaths);
|
||||
void RemoveMissingRecords(const std::unordered_map<std::string, bool>& seenPaths);
|
||||
|
||||
AssetDatabase& m_assetDatabase;
|
||||
};
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
287
Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.cpp
Normal file
287
Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
#include "Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h"
|
||||
|
||||
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::string ToStdString(const Containers::String& value) {
|
||||
return std::string(value.CStr());
|
||||
}
|
||||
|
||||
Containers::String ToContainersString(const std::string& value) {
|
||||
return Containers::String(value.c_str());
|
||||
}
|
||||
|
||||
bool HasVirtualPathScheme(const Containers::String& value) {
|
||||
return ToStdString(value).find("://") != std::string::npos;
|
||||
}
|
||||
|
||||
bool ShouldTraceAssetPath(const Containers::String& path) {
|
||||
const std::string text = ToStdString(path);
|
||||
return text.rfind("builtin://", 0) == 0 ||
|
||||
text.find("backpack") != std::string::npos ||
|
||||
text.find("New Material.mat") != std::string::npos;
|
||||
}
|
||||
|
||||
Containers::String NormalizeArtifactPathString(const fs::path& path) {
|
||||
if (path.empty()) {
|
||||
return Containers::String();
|
||||
}
|
||||
|
||||
return ToContainersString(path.lexically_normal().generic_string());
|
||||
}
|
||||
|
||||
Containers::String NormalizeArtifactPathString(const Containers::String& path) {
|
||||
if (path.Empty()) {
|
||||
return Containers::String();
|
||||
}
|
||||
|
||||
return NormalizeArtifactPathString(fs::path(path.CStr()));
|
||||
}
|
||||
|
||||
ArtifactStorageKind InferArtifactStorageKind(
|
||||
const Containers::String& projectRoot,
|
||||
const AssetDatabase::ArtifactRecord& record) {
|
||||
if (!record.mainArtifactPath.Empty()) {
|
||||
const Containers::String absoluteMainArtifactPath =
|
||||
NormalizeArtifactPathString(fs::path(projectRoot.CStr()) / record.mainArtifactPath.CStr());
|
||||
if (IsArtifactContainerFile(absoluteMainArtifactPath)) {
|
||||
return ArtifactStorageKind::SingleFileContainer;
|
||||
}
|
||||
}
|
||||
|
||||
return record.artifactDirectory.Empty()
|
||||
? ArtifactStorageKind::Unknown
|
||||
: ArtifactStorageKind::LegacyDirectory;
|
||||
}
|
||||
|
||||
Containers::String BuildArtifactMainEntryLoadPath(
|
||||
const Containers::String& artifactMainPath,
|
||||
ArtifactStorageKind storageKind,
|
||||
const Containers::String& mainEntryName) {
|
||||
if (storageKind == ArtifactStorageKind::SingleFileContainer &&
|
||||
!artifactMainPath.Empty() &&
|
||||
!mainEntryName.Empty()) {
|
||||
return BuildArtifactContainerEntryPath(artifactMainPath, mainEntryName);
|
||||
}
|
||||
|
||||
return artifactMainPath;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
std::string EscapeField(const std::string& value) {
|
||||
std::string escaped;
|
||||
escaped.reserve(value.size());
|
||||
for (const char ch : value) {
|
||||
if (ch == '\\' || ch == '\t' || ch == '\n' || ch == '\r') {
|
||||
escaped.push_back('\\');
|
||||
switch (ch) {
|
||||
case '\t':
|
||||
escaped.push_back('t');
|
||||
break;
|
||||
case '\n':
|
||||
escaped.push_back('n');
|
||||
break;
|
||||
case '\r':
|
||||
escaped.push_back('r');
|
||||
break;
|
||||
default:
|
||||
escaped.push_back(ch);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
escaped.push_back(ch);
|
||||
}
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
std::string UnescapeField(const std::string& value) {
|
||||
std::string result;
|
||||
result.reserve(value.size());
|
||||
for (size_t index = 0; index < value.size(); ++index) {
|
||||
if (value[index] == '\\' && index + 1 < value.size()) {
|
||||
++index;
|
||||
switch (value[index]) {
|
||||
case 't':
|
||||
result.push_back('\t');
|
||||
break;
|
||||
case 'n':
|
||||
result.push_back('\n');
|
||||
break;
|
||||
case 'r':
|
||||
result.push_back('\r');
|
||||
break;
|
||||
default:
|
||||
result.push_back(value[index]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
result.push_back(value[index]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitFields(const std::string& line) {
|
||||
std::vector<std::string> fields;
|
||||
std::string current;
|
||||
bool escaping = false;
|
||||
|
||||
for (const char ch : line) {
|
||||
if (escaping) {
|
||||
current.push_back('\\');
|
||||
current.push_back(ch);
|
||||
escaping = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\\') {
|
||||
escaping = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\t') {
|
||||
fields.push_back(UnescapeField(current));
|
||||
current.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
current.push_back(ch);
|
||||
}
|
||||
|
||||
if (escaping) {
|
||||
current.push_back('\\');
|
||||
}
|
||||
|
||||
fields.push_back(UnescapeField(current));
|
||||
return fields;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WriteModelSubAssetManifest(
|
||||
const std::filesystem::path& manifestPath,
|
||||
const std::vector<ModelSubAssetManifestEntry>& entries) {
|
||||
std::ofstream output(manifestPath, std::ios::out | std::ios::trunc);
|
||||
if (!output.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
output << "# localID\tresourceType\tartifactPath\n";
|
||||
for (const ModelSubAssetManifestEntry& entry : entries) {
|
||||
if (entry.localID == kInvalidLocalID ||
|
||||
entry.resourceType == ResourceType::Unknown ||
|
||||
entry.artifactPath.Empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
output << entry.localID << '\t'
|
||||
<< static_cast<Core::uint32>(entry.resourceType) << '\t'
|
||||
<< EscapeField(ToStdString(entry.artifactPath)) << '\n';
|
||||
}
|
||||
|
||||
return static_cast<bool>(output);
|
||||
}
|
||||
|
||||
bool TryReadModelSubAssetManifest(
|
||||
const std::filesystem::path& manifestPath,
|
||||
std::vector<ModelSubAssetManifestEntry>& outEntries) {
|
||||
outEntries.clear();
|
||||
|
||||
std::ifstream input(manifestPath);
|
||||
if (!input.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(input, line)) {
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> fields = SplitFields(line);
|
||||
if (fields.size() < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ModelSubAssetManifestEntry entry;
|
||||
entry.localID = static_cast<LocalID>(std::stoull(fields[0]));
|
||||
entry.resourceType = static_cast<ResourceType>(std::stoul(fields[1]));
|
||||
entry.artifactPath = ToContainersString(fields[2]);
|
||||
if (entry.localID == kInvalidLocalID ||
|
||||
entry.resourceType == ResourceType::Unknown ||
|
||||
entry.artifactPath.Empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outEntries.push_back(entry);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TryResolveModelSubAssetArtifactPath(
|
||||
const std::filesystem::path& manifestPath,
|
||||
const AssetRef& assetRef,
|
||||
Containers::String& outArtifactPath) {
|
||||
std::vector<ModelSubAssetManifestEntry> entries;
|
||||
if (!TryReadModelSubAssetManifest(manifestPath, entries)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const ModelSubAssetManifestEntry& entry : entries) {
|
||||
if (entry.localID != assetRef.localID || entry.resourceType != assetRef.resourceType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outArtifactPath = entry.artifactPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PopulateResolvedAssetResult(
|
||||
const Containers::String& projectRoot,
|
||||
const AssetDatabase::SourceAssetRecord& sourceRecord,
|
||||
const AssetDatabase::ArtifactRecord& artifactRecord,
|
||||
bool imported,
|
||||
AssetDatabase::ResolvedAsset& outAsset) {
|
||||
outAsset = AssetDatabase::ResolvedAsset();
|
||||
outAsset.exists = true;
|
||||
outAsset.artifactReady = true;
|
||||
outAsset.imported = imported;
|
||||
outAsset.absolutePath =
|
||||
NormalizeArtifactPathString(fs::path(projectRoot.CStr()) / sourceRecord.relativePath.CStr());
|
||||
outAsset.relativePath = sourceRecord.relativePath;
|
||||
outAsset.assetGuid = sourceRecord.guid;
|
||||
outAsset.resourceType = artifactRecord.resourceType;
|
||||
outAsset.artifactStorageKind = artifactRecord.storageKind;
|
||||
outAsset.mainEntryName = artifactRecord.mainEntryName;
|
||||
if (!artifactRecord.artifactDirectory.Empty()) {
|
||||
outAsset.artifactDirectory = NormalizeArtifactPathString(
|
||||
fs::path(projectRoot.CStr()) / artifactRecord.artifactDirectory.CStr());
|
||||
}
|
||||
outAsset.artifactMainPath = NormalizeArtifactPathString(
|
||||
fs::path(projectRoot.CStr()) / artifactRecord.mainArtifactPath.CStr());
|
||||
outAsset.artifactMainEntryPath = BuildArtifactMainEntryLoadPath(
|
||||
outAsset.artifactMainPath,
|
||||
artifactRecord.storageKind,
|
||||
artifactRecord.mainEntryName);
|
||||
outAsset.mainLocalID = artifactRecord.mainLocalID;
|
||||
}
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
56
Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h
Normal file
56
Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
std::string ToStdString(const Containers::String& value);
|
||||
Containers::String ToContainersString(const std::string& value);
|
||||
bool HasVirtualPathScheme(const Containers::String& value);
|
||||
bool ShouldTraceAssetPath(const Containers::String& path);
|
||||
|
||||
Containers::String NormalizeArtifactPathString(const std::filesystem::path& path);
|
||||
Containers::String NormalizeArtifactPathString(const Containers::String& path);
|
||||
|
||||
ArtifactStorageKind InferArtifactStorageKind(
|
||||
const Containers::String& projectRoot,
|
||||
const AssetDatabase::ArtifactRecord& record);
|
||||
Containers::String BuildArtifactMainEntryLoadPath(
|
||||
const Containers::String& artifactMainPath,
|
||||
ArtifactStorageKind storageKind,
|
||||
const Containers::String& mainEntryName);
|
||||
|
||||
inline constexpr const char* kModelSubAssetManifestFileName = "subassets.tsv";
|
||||
|
||||
struct ModelSubAssetManifestEntry {
|
||||
LocalID localID = kInvalidLocalID;
|
||||
ResourceType resourceType = ResourceType::Unknown;
|
||||
Containers::String artifactPath;
|
||||
};
|
||||
|
||||
bool WriteModelSubAssetManifest(
|
||||
const std::filesystem::path& manifestPath,
|
||||
const std::vector<ModelSubAssetManifestEntry>& entries);
|
||||
bool TryReadModelSubAssetManifest(
|
||||
const std::filesystem::path& manifestPath,
|
||||
std::vector<ModelSubAssetManifestEntry>& outEntries);
|
||||
bool TryResolveModelSubAssetArtifactPath(
|
||||
const std::filesystem::path& manifestPath,
|
||||
const AssetRef& assetRef,
|
||||
Containers::String& outArtifactPath);
|
||||
void PopulateResolvedAssetResult(
|
||||
const Containers::String& projectRoot,
|
||||
const AssetDatabase::SourceAssetRecord& sourceRecord,
|
||||
const AssetDatabase::ArtifactRecord& artifactRecord,
|
||||
bool imported,
|
||||
AssetDatabase::ResolvedAsset& outAsset);
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
435
Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.cpp
Normal file
435
Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.cpp
Normal file
@@ -0,0 +1,435 @@
|
||||
#include "Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h"
|
||||
|
||||
#include "Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h"
|
||||
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
using AssetDatabaseInternal::NormalizeArtifactPathString;
|
||||
using AssetDatabaseInternal::ToContainersString;
|
||||
using AssetDatabaseInternal::ToStdString;
|
||||
|
||||
namespace {
|
||||
|
||||
using SourceAssetRecord = AssetDatabase::SourceAssetRecord;
|
||||
using ArtifactRecord = AssetDatabase::ArtifactRecord;
|
||||
using ArtifactDependencyRecord = AssetDatabase::ArtifactDependencyRecord;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
Containers::String NormalizePathString(const fs::path& path) {
|
||||
return ToContainersString(path.lexically_normal().generic_string());
|
||||
}
|
||||
|
||||
Containers::String MakeKey(const Containers::String& path) {
|
||||
std::string key = ToStdString(NormalizePathString(fs::path(path.CStr())));
|
||||
std::transform(key.begin(), key.end(), key.begin(), [](unsigned char ch) {
|
||||
return static_cast<char>(std::tolower(ch));
|
||||
});
|
||||
return ToContainersString(key);
|
||||
}
|
||||
|
||||
std::string EscapeField(const std::string& value) {
|
||||
std::string escaped;
|
||||
escaped.reserve(value.size());
|
||||
for (const char ch : value) {
|
||||
if (ch == '\\' || ch == '\t' || ch == '\n' || ch == '\r') {
|
||||
escaped.push_back('\\');
|
||||
switch (ch) {
|
||||
case '\t':
|
||||
escaped.push_back('t');
|
||||
break;
|
||||
case '\n':
|
||||
escaped.push_back('n');
|
||||
break;
|
||||
case '\r':
|
||||
escaped.push_back('r');
|
||||
break;
|
||||
default:
|
||||
escaped.push_back(ch);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
escaped.push_back(ch);
|
||||
}
|
||||
}
|
||||
return escaped;
|
||||
}
|
||||
|
||||
std::string UnescapeField(const std::string& value) {
|
||||
std::string result;
|
||||
result.reserve(value.size());
|
||||
for (size_t index = 0; index < value.size(); ++index) {
|
||||
if (value[index] == '\\' && index + 1 < value.size()) {
|
||||
++index;
|
||||
switch (value[index]) {
|
||||
case 't':
|
||||
result.push_back('\t');
|
||||
break;
|
||||
case 'n':
|
||||
result.push_back('\n');
|
||||
break;
|
||||
case 'r':
|
||||
result.push_back('\r');
|
||||
break;
|
||||
default:
|
||||
result.push_back(value[index]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
result.push_back(value[index]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitFields(const std::string& line) {
|
||||
std::vector<std::string> fields;
|
||||
std::string current;
|
||||
bool escaping = false;
|
||||
|
||||
for (const char ch : line) {
|
||||
if (escaping) {
|
||||
current.push_back('\\');
|
||||
current.push_back(ch);
|
||||
escaping = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\\') {
|
||||
escaping = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == '\t') {
|
||||
fields.push_back(UnescapeField(current));
|
||||
current.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
current.push_back(ch);
|
||||
}
|
||||
|
||||
if (escaping) {
|
||||
current.push_back('\\');
|
||||
}
|
||||
|
||||
fields.push_back(UnescapeField(current));
|
||||
return fields;
|
||||
}
|
||||
|
||||
constexpr Core::uint32 kArtifactDbSchemaVersion = 2;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
void SourceAssetDB::Clear() {
|
||||
m_recordsByPathKey.clear();
|
||||
m_recordsByGuid.clear();
|
||||
}
|
||||
|
||||
bool SourceAssetDB::Load(const Containers::String& sourceDbPath) {
|
||||
Clear();
|
||||
|
||||
std::ifstream input(sourceDbPath.CStr());
|
||||
if (!input.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(input, line)) {
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> fields = SplitFields(line);
|
||||
if (fields.size() < 10) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SourceAssetRecord record;
|
||||
record.guid = AssetGUID::ParseOrDefault(ToContainersString(fields[0]));
|
||||
record.relativePath = ToContainersString(fields[1]);
|
||||
record.metaPath = ToContainersString(fields[2]);
|
||||
record.isFolder = (fields[3] == "1");
|
||||
record.importerName = ToContainersString(fields[4]);
|
||||
record.importerVersion = static_cast<Core::uint32>(std::stoul(fields[5]));
|
||||
record.metaHash = ToContainersString(fields[6]);
|
||||
record.sourceHash = ToContainersString(fields[7]);
|
||||
record.sourceFileSize = static_cast<Core::uint64>(std::stoull(fields[8]));
|
||||
record.sourceWriteTime = static_cast<Core::uint64>(std::stoull(fields[9]));
|
||||
if (fields.size() > 10) {
|
||||
record.lastKnownArtifactKey = ToContainersString(fields[10]);
|
||||
}
|
||||
|
||||
if (!record.guid.IsValid() || record.relativePath.Empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string pathKey = ToStdString(MakeKey(record.relativePath));
|
||||
m_recordsByGuid[record.guid] = record;
|
||||
m_recordsByPathKey[pathKey] = record;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SourceAssetDB::Save(const Containers::String& sourceDbPath) const {
|
||||
std::ofstream output(sourceDbPath.CStr(), std::ios::out | std::ios::trunc);
|
||||
if (!output.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
output << "# guid\trelativePath\tmetaPath\tisFolder\timporter\timporterVersion\tmetaHash\tsourceHash\tsize\twriteTime\tartifactKey\n";
|
||||
for (const auto& [guid, record] : m_recordsByGuid) {
|
||||
(void)guid;
|
||||
output << EscapeField(ToStdString(record.guid.ToString())) << '\t'
|
||||
<< EscapeField(ToStdString(record.relativePath)) << '\t'
|
||||
<< EscapeField(ToStdString(record.metaPath)) << '\t'
|
||||
<< (record.isFolder ? "1" : "0") << '\t'
|
||||
<< EscapeField(ToStdString(record.importerName)) << '\t'
|
||||
<< record.importerVersion << '\t'
|
||||
<< EscapeField(ToStdString(record.metaHash)) << '\t'
|
||||
<< EscapeField(ToStdString(record.sourceHash)) << '\t'
|
||||
<< record.sourceFileSize << '\t'
|
||||
<< record.sourceWriteTime << '\t'
|
||||
<< EscapeField(ToStdString(record.lastKnownArtifactKey)) << '\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SourceAssetRecord* SourceAssetDB::FindByPathKey(const std::string& pathKey) {
|
||||
auto it = m_recordsByPathKey.find(pathKey);
|
||||
return it != m_recordsByPathKey.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const SourceAssetRecord* SourceAssetDB::FindByPathKey(const std::string& pathKey) const {
|
||||
auto it = m_recordsByPathKey.find(pathKey);
|
||||
return it != m_recordsByPathKey.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
SourceAssetRecord* SourceAssetDB::FindByGuid(const AssetGUID& guid) {
|
||||
auto it = m_recordsByGuid.find(guid);
|
||||
return it != m_recordsByGuid.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const SourceAssetRecord* SourceAssetDB::FindByGuid(const AssetGUID& guid) const {
|
||||
auto it = m_recordsByGuid.find(guid);
|
||||
return it != m_recordsByGuid.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
void SourceAssetDB::Upsert(const std::string& pathKey, const SourceAssetRecord& record) {
|
||||
auto existingByPath = m_recordsByPathKey.find(pathKey);
|
||||
if (existingByPath != m_recordsByPathKey.end() && existingByPath->second.guid != record.guid) {
|
||||
m_recordsByGuid.erase(existingByPath->second.guid);
|
||||
}
|
||||
|
||||
auto existingByGuid = m_recordsByGuid.find(record.guid);
|
||||
if (existingByGuid != m_recordsByGuid.end()) {
|
||||
const std::string existingPathKey = ToStdString(MakeKey(existingByGuid->second.relativePath));
|
||||
if (existingPathKey != pathKey) {
|
||||
m_recordsByPathKey.erase(existingPathKey);
|
||||
}
|
||||
}
|
||||
|
||||
m_recordsByPathKey[pathKey] = record;
|
||||
m_recordsByGuid[record.guid] = record;
|
||||
}
|
||||
|
||||
void SourceAssetDB::EraseByPathKey(const std::string& pathKey) {
|
||||
auto it = m_recordsByPathKey.find(pathKey);
|
||||
if (it == m_recordsByPathKey.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_recordsByGuid.erase(it->second.guid);
|
||||
m_recordsByPathKey.erase(it);
|
||||
}
|
||||
|
||||
void SourceAssetDB::SetLastKnownArtifactKey(const std::string& pathKey,
|
||||
const AssetGUID& guid,
|
||||
const Containers::String& artifactKey) {
|
||||
auto pathIt = m_recordsByPathKey.find(pathKey);
|
||||
if (pathIt != m_recordsByPathKey.end()) {
|
||||
pathIt->second.lastKnownArtifactKey = artifactKey;
|
||||
}
|
||||
|
||||
auto guidIt = m_recordsByGuid.find(guid);
|
||||
if (guidIt != m_recordsByGuid.end()) {
|
||||
guidIt->second.lastKnownArtifactKey = artifactKey;
|
||||
}
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, SourceAssetRecord>& SourceAssetDB::GetRecordsByPathKey() const {
|
||||
return m_recordsByPathKey;
|
||||
}
|
||||
|
||||
const std::unordered_map<AssetGUID, SourceAssetRecord>& SourceAssetDB::GetRecordsByGuid() const {
|
||||
return m_recordsByGuid;
|
||||
}
|
||||
|
||||
void ArtifactDB::Clear() {
|
||||
m_recordsByGuid.clear();
|
||||
}
|
||||
|
||||
bool ArtifactDB::Load(const Containers::String& projectRoot, const Containers::String& artifactDbPath) {
|
||||
Clear();
|
||||
|
||||
std::ifstream input(artifactDbPath.CStr());
|
||||
if (!input.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
Core::uint32 schemaVersion = 1;
|
||||
while (std::getline(input, line)) {
|
||||
if (line.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line[0] == '#') {
|
||||
const std::string schemaToken = "schema=";
|
||||
const size_t schemaTokenPosition = line.find(schemaToken);
|
||||
if (schemaTokenPosition != std::string::npos) {
|
||||
const size_t valueStart = schemaTokenPosition + schemaToken.length();
|
||||
const std::string valueText = line.substr(valueStart);
|
||||
if (!valueText.empty()) {
|
||||
schemaVersion = static_cast<Core::uint32>(std::stoul(valueText));
|
||||
}
|
||||
} else if (line.find("storageKind") != std::string::npos &&
|
||||
line.find("mainEntryName") != std::string::npos) {
|
||||
schemaVersion = std::max(schemaVersion, kArtifactDbSchemaVersion);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> fields = SplitFields(line);
|
||||
if (fields.size() < 10) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ArtifactRecord record;
|
||||
record.artifactKey = ToContainersString(fields[0]);
|
||||
record.assetGuid = AssetGUID::ParseOrDefault(ToContainersString(fields[1]));
|
||||
record.importerName = ToContainersString(fields[2]);
|
||||
record.importerVersion = static_cast<Core::uint32>(std::stoul(fields[3]));
|
||||
record.resourceType = static_cast<ResourceType>(std::stoul(fields[4]));
|
||||
record.artifactDirectory = ToContainersString(fields[5]);
|
||||
record.mainArtifactPath = ToContainersString(fields[6]);
|
||||
record.sourceHash = ToContainersString(fields[7]);
|
||||
record.metaHash = ToContainersString(fields[8]);
|
||||
record.sourceFileSize = static_cast<Core::uint64>(std::stoull(fields[9]));
|
||||
record.sourceWriteTime =
|
||||
fields.size() > 10 ? static_cast<Core::uint64>(std::stoull(fields[10])) : 0;
|
||||
record.mainLocalID =
|
||||
fields.size() > 11 ? static_cast<LocalID>(std::stoull(fields[11])) : kMainAssetLocalID;
|
||||
size_t dependencyFieldStart = 12;
|
||||
if (schemaVersion >= kArtifactDbSchemaVersion && fields.size() > 13) {
|
||||
record.storageKind = static_cast<ArtifactStorageKind>(std::stoul(fields[12]));
|
||||
record.mainEntryName = ToContainersString(fields[13]);
|
||||
dependencyFieldStart = 14;
|
||||
}
|
||||
|
||||
if (record.storageKind == ArtifactStorageKind::Unknown) {
|
||||
record.storageKind = InferArtifactStorageKind(projectRoot, record);
|
||||
}
|
||||
if (record.mainEntryName.Empty() &&
|
||||
record.storageKind == ArtifactStorageKind::SingleFileContainer) {
|
||||
record.mainEntryName = "main";
|
||||
}
|
||||
if (record.artifactDirectory.Empty() && !record.mainArtifactPath.Empty()) {
|
||||
record.artifactDirectory = NormalizeArtifactPathString(
|
||||
fs::path(record.mainArtifactPath.CStr()).parent_path());
|
||||
}
|
||||
|
||||
for (size_t index = dependencyFieldStart; index + 3 < fields.size(); index += 4) {
|
||||
ArtifactDependencyRecord dependency;
|
||||
dependency.path = ToContainersString(fields[index + 0]);
|
||||
dependency.hash = ToContainersString(fields[index + 1]);
|
||||
dependency.fileSize = static_cast<Core::uint64>(std::stoull(fields[index + 2]));
|
||||
dependency.writeTime = static_cast<Core::uint64>(std::stoull(fields[index + 3]));
|
||||
if (!dependency.path.Empty()) {
|
||||
record.dependencies.push_back(dependency);
|
||||
}
|
||||
}
|
||||
|
||||
if (!record.assetGuid.IsValid() || record.artifactKey.Empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_recordsByGuid[record.assetGuid] = record;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ArtifactDB::Save(const Containers::String& artifactDbPath) const {
|
||||
std::ofstream output(artifactDbPath.CStr(), std::ios::out | std::ios::trunc);
|
||||
if (!output.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
output << "# schema=" << kArtifactDbSchemaVersion << "\n";
|
||||
output << "# artifactKey\tassetGuid\timporter\tversion\ttype\tartifactDir\tmainArtifact\tsourceHash\tmetaHash\tsize\twriteTime\tmainLocalID\tstorageKind\tmainEntryName\t(depPath\tdepHash\tdepSize\tdepWriteTime)...\n";
|
||||
for (const auto& [guid, record] : m_recordsByGuid) {
|
||||
(void)guid;
|
||||
output << EscapeField(ToStdString(record.artifactKey)) << '\t'
|
||||
<< EscapeField(ToStdString(record.assetGuid.ToString())) << '\t'
|
||||
<< EscapeField(ToStdString(record.importerName)) << '\t'
|
||||
<< record.importerVersion << '\t'
|
||||
<< static_cast<Core::uint32>(record.resourceType) << '\t'
|
||||
<< EscapeField(ToStdString(record.artifactDirectory)) << '\t'
|
||||
<< EscapeField(ToStdString(record.mainArtifactPath)) << '\t'
|
||||
<< EscapeField(ToStdString(record.sourceHash)) << '\t'
|
||||
<< EscapeField(ToStdString(record.metaHash)) << '\t'
|
||||
<< record.sourceFileSize << '\t'
|
||||
<< record.sourceWriteTime << '\t'
|
||||
<< record.mainLocalID << '\t'
|
||||
<< static_cast<Core::uint32>(record.storageKind) << '\t'
|
||||
<< EscapeField(ToStdString(record.mainEntryName));
|
||||
for (const ArtifactDependencyRecord& dependency : record.dependencies) {
|
||||
output << '\t' << EscapeField(ToStdString(dependency.path))
|
||||
<< '\t' << EscapeField(ToStdString(dependency.hash))
|
||||
<< '\t' << dependency.fileSize
|
||||
<< '\t' << dependency.writeTime;
|
||||
}
|
||||
output << '\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArtifactRecord* ArtifactDB::FindByGuid(const AssetGUID& guid) {
|
||||
auto it = m_recordsByGuid.find(guid);
|
||||
return it != m_recordsByGuid.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
const ArtifactRecord* ArtifactDB::FindByGuid(const AssetGUID& guid) const {
|
||||
auto it = m_recordsByGuid.find(guid);
|
||||
return it != m_recordsByGuid.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
void ArtifactDB::Upsert(const AssetGUID& guid, const ArtifactRecord& record) {
|
||||
m_recordsByGuid[guid] = record;
|
||||
}
|
||||
|
||||
void ArtifactDB::EraseByGuid(const AssetGUID& guid) {
|
||||
m_recordsByGuid.erase(guid);
|
||||
}
|
||||
|
||||
const std::unordered_map<AssetGUID, ArtifactRecord>& ArtifactDB::GetRecordsByGuid() const {
|
||||
return m_recordsByGuid;
|
||||
}
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
55
Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h
Normal file
55
Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
namespace AssetDatabaseInternal {
|
||||
|
||||
class SourceAssetDB {
|
||||
public:
|
||||
void Clear();
|
||||
bool Load(const Containers::String& sourceDbPath);
|
||||
bool Save(const Containers::String& sourceDbPath) const;
|
||||
|
||||
AssetDatabase::SourceAssetRecord* FindByPathKey(const std::string& pathKey);
|
||||
const AssetDatabase::SourceAssetRecord* FindByPathKey(const std::string& pathKey) const;
|
||||
AssetDatabase::SourceAssetRecord* FindByGuid(const AssetGUID& guid);
|
||||
const AssetDatabase::SourceAssetRecord* FindByGuid(const AssetGUID& guid) const;
|
||||
|
||||
void Upsert(const std::string& pathKey, const AssetDatabase::SourceAssetRecord& record);
|
||||
void EraseByPathKey(const std::string& pathKey);
|
||||
void SetLastKnownArtifactKey(const std::string& pathKey,
|
||||
const AssetGUID& guid,
|
||||
const Containers::String& artifactKey);
|
||||
|
||||
const std::unordered_map<std::string, AssetDatabase::SourceAssetRecord>& GetRecordsByPathKey() const;
|
||||
const std::unordered_map<AssetGUID, AssetDatabase::SourceAssetRecord>& GetRecordsByGuid() const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, AssetDatabase::SourceAssetRecord> m_recordsByPathKey;
|
||||
std::unordered_map<AssetGUID, AssetDatabase::SourceAssetRecord> m_recordsByGuid;
|
||||
};
|
||||
|
||||
class ArtifactDB {
|
||||
public:
|
||||
void Clear();
|
||||
bool Load(const Containers::String& projectRoot, const Containers::String& artifactDbPath);
|
||||
bool Save(const Containers::String& artifactDbPath) const;
|
||||
|
||||
AssetDatabase::ArtifactRecord* FindByGuid(const AssetGUID& guid);
|
||||
const AssetDatabase::ArtifactRecord* FindByGuid(const AssetGUID& guid) const;
|
||||
void Upsert(const AssetGUID& guid, const AssetDatabase::ArtifactRecord& record);
|
||||
void EraseByGuid(const AssetGUID& guid);
|
||||
|
||||
const std::unordered_map<AssetGUID, AssetDatabase::ArtifactRecord>& GetRecordsByGuid() const;
|
||||
|
||||
private:
|
||||
std::unordered_map<AssetGUID, AssetDatabase::ArtifactRecord> m_recordsByGuid;
|
||||
};
|
||||
|
||||
} // namespace AssetDatabaseInternal
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
29
Pipeline/Core/Contracts/IProjectAssetPipelineService.h
Normal file
29
Pipeline/Core/Contracts/IProjectAssetPipelineService.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "IProjectAssetResolver.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class IProjectAssetPipelineService : public IProjectAssetResolver {
|
||||
public:
|
||||
~IProjectAssetPipelineService() override = default;
|
||||
|
||||
virtual void Initialize() = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void SetProjectRoot(const Containers::String& projectRoot) = 0;
|
||||
virtual Containers::String GetLibraryRoot() const = 0;
|
||||
|
||||
virtual bool BootstrapProject() = 0;
|
||||
virtual void Refresh() = 0;
|
||||
virtual bool ClearLibraryCache() = 0;
|
||||
virtual bool RebuildLibraryCache() = 0;
|
||||
virtual bool ReimportAllAssets() = 0;
|
||||
virtual bool ReimportAsset(const Containers::String& requestPath,
|
||||
ProjectAssetImportedAsset& outAsset) = 0;
|
||||
virtual ProjectAssetImportStatusSnapshot GetLastImportStatus() const = 0;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
29
Pipeline/Core/Contracts/IProjectAssetResolver.h
Normal file
29
Pipeline/Core/Contracts/IProjectAssetResolver.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "ProjectAssetTypes.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class IProjectAssetResolver {
|
||||
public:
|
||||
virtual ~IProjectAssetResolver() = default;
|
||||
|
||||
virtual Containers::String GetProjectRoot() const = 0;
|
||||
virtual bool TryGetImportableResourceType(const Containers::String& requestPath,
|
||||
ResourceType& outType) const = 0;
|
||||
virtual bool EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
ProjectAssetImportedAsset& outAsset) = 0;
|
||||
virtual bool TryGetAssetRef(const Containers::String& requestPath,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const = 0;
|
||||
virtual bool TryGetPrimaryAssetPath(const AssetGUID& guid,
|
||||
Containers::String& outRelativePath) const = 0;
|
||||
virtual bool TryResolveAssetPath(const AssetRef& assetRef,
|
||||
Containers::String& outPath) const = 0;
|
||||
virtual void BuildLookupSnapshot(ProjectAssetLookupSnapshot& outSnapshot) const = 0;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
66
Pipeline/Core/Contracts/ProjectAssetTypes.h
Normal file
66
Pipeline/Core/Contracts/ProjectAssetTypes.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/AssetRef.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class ArtifactStorageKind : Core::uint8 {
|
||||
Unknown = 0,
|
||||
LegacyDirectory = 1,
|
||||
SingleFileContainer = 2
|
||||
};
|
||||
|
||||
struct ProjectAssetImportStatusSnapshot {
|
||||
Core::uint64 revision = 0;
|
||||
bool inProgress = false;
|
||||
bool success = false;
|
||||
Containers::String operation;
|
||||
Containers::String targetPath;
|
||||
Containers::String message;
|
||||
Core::uint64 startedAtMs = 0;
|
||||
Core::uint64 completedAtMs = 0;
|
||||
Core::uint64 durationMs = 0;
|
||||
Core::uint32 importedAssetCount = 0;
|
||||
Core::uint32 removedArtifactCount = 0;
|
||||
|
||||
bool HasValue() const {
|
||||
return revision != 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct ProjectAssetLookupSnapshot {
|
||||
std::unordered_map<std::string, AssetGUID> assetGuidByPathKey;
|
||||
std::unordered_map<AssetGUID, Containers::String> assetPathByGuid;
|
||||
|
||||
void Clear() {
|
||||
assetGuidByPathKey.clear();
|
||||
assetPathByGuid.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct ProjectAssetImportedAsset {
|
||||
bool exists = false;
|
||||
bool artifactReady = false;
|
||||
bool imported = false;
|
||||
Containers::String absolutePath;
|
||||
Containers::String relativePath;
|
||||
AssetGUID assetGuid;
|
||||
ResourceType resourceType = ResourceType::Unknown;
|
||||
Containers::String runtimeLoadPath;
|
||||
Containers::String artifactMainPath;
|
||||
Containers::String artifactMainEntryPath;
|
||||
Containers::String artifactDirectory;
|
||||
ArtifactStorageKind artifactStorageKind = ArtifactStorageKind::Unknown;
|
||||
Containers::String mainEntryName;
|
||||
LocalID mainLocalID = kMainAssetLocalID;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,5 @@
|
||||
#include <XCEngine/Core/Asset/AssetImportService.h>
|
||||
#include "AssetImportService.h"
|
||||
#include "Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h"
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
|
||||
#include <chrono>
|
||||
@@ -15,325 +16,24 @@ Containers::String ToContainersString(const std::string& value) {
|
||||
return Containers::String(value.c_str());
|
||||
}
|
||||
|
||||
Containers::String BuildStatsSuffix(const AssetDatabase::MaintenanceStats& stats) {
|
||||
Containers::String BuildStatsSuffix(Core::uint32 importedAssetCount,
|
||||
Core::uint32 removedArtifactCount) {
|
||||
std::string suffix;
|
||||
if (stats.importedAssetCount > 0) {
|
||||
if (importedAssetCount > 0) {
|
||||
suffix += " imported=";
|
||||
suffix += std::to_string(stats.importedAssetCount);
|
||||
suffix += std::to_string(importedAssetCount);
|
||||
}
|
||||
if (stats.removedArtifactCount > 0) {
|
||||
if (removedArtifactCount > 0) {
|
||||
suffix += " removedOrphans=";
|
||||
suffix += std::to_string(stats.removedArtifactCount);
|
||||
suffix += std::to_string(removedArtifactCount);
|
||||
}
|
||||
|
||||
return ToContainersString(suffix);
|
||||
}
|
||||
|
||||
Core::uint64 GetCurrentSteadyTimeMs() {
|
||||
return static_cast<Core::uint64>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void AssetImportService::Initialize() {
|
||||
}
|
||||
|
||||
void AssetImportService::Shutdown() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
m_assetDatabase.Shutdown();
|
||||
m_projectRoot.Clear();
|
||||
ResetImportStatusLocked();
|
||||
}
|
||||
|
||||
void AssetImportService::SetProjectRoot(const Containers::String& projectRoot) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (m_projectRoot == projectRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_projectRoot.Empty()) {
|
||||
m_assetDatabase.Shutdown();
|
||||
}
|
||||
|
||||
m_projectRoot = projectRoot;
|
||||
ResetImportStatusLocked();
|
||||
if (!m_projectRoot.Empty()) {
|
||||
m_assetDatabase.Initialize(m_projectRoot);
|
||||
}
|
||||
}
|
||||
|
||||
Containers::String AssetImportService::GetProjectRoot() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
return m_projectRoot;
|
||||
}
|
||||
|
||||
Containers::String AssetImportService::GetLibraryRoot() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
return m_assetDatabase.GetLibraryRoot();
|
||||
}
|
||||
|
||||
bool AssetImportService::BootstrapProject() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Bootstrap Project",
|
||||
Containers::String(),
|
||||
false,
|
||||
"Cannot bootstrap project assets without an active project.",
|
||||
AssetDatabase::MaintenanceStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Bootstrap Project",
|
||||
m_projectRoot,
|
||||
"Bootstrapping project Library state...");
|
||||
|
||||
const AssetDatabase::MaintenanceStats stats = m_assetDatabase.Refresh();
|
||||
FinishImportStatusLocked(
|
||||
"Bootstrap Project",
|
||||
m_projectRoot,
|
||||
true,
|
||||
Containers::String("Bootstrapped project Library state.") + BuildStatsSuffix(stats),
|
||||
stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssetImportService::Refresh() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (!m_projectRoot.Empty()) {
|
||||
const AssetDatabase::MaintenanceStats stats = m_assetDatabase.Refresh();
|
||||
if (stats.removedArtifactCount > 0) {
|
||||
FinishImportStatusLocked(
|
||||
"Refresh",
|
||||
m_projectRoot,
|
||||
true,
|
||||
Containers::String("Refresh removed orphan artifact entries.") + BuildStatsSuffix(stats),
|
||||
stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetImportService::ClearLibraryCache() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Clear Library",
|
||||
Containers::String(),
|
||||
false,
|
||||
"Cannot clear Library cache without an active project.",
|
||||
AssetDatabase::MaintenanceStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Clear Library",
|
||||
m_assetDatabase.GetLibraryRoot(),
|
||||
"Clearing Library cache...");
|
||||
|
||||
const Containers::String projectRoot = m_projectRoot;
|
||||
const Containers::String libraryRoot = m_assetDatabase.GetLibraryRoot();
|
||||
|
||||
m_assetDatabase.Shutdown();
|
||||
|
||||
std::error_code ec;
|
||||
fs::remove_all(fs::path(libraryRoot.CStr()), ec);
|
||||
|
||||
m_assetDatabase.Initialize(projectRoot);
|
||||
const AssetDatabase::MaintenanceStats stats = m_assetDatabase.Refresh();
|
||||
const bool succeeded = !ec;
|
||||
FinishImportStatusLocked(
|
||||
"Clear Library",
|
||||
libraryRoot,
|
||||
succeeded,
|
||||
succeeded
|
||||
? Containers::String("Cleared Library cache and rebuilt source asset lookup.") + BuildStatsSuffix(stats)
|
||||
: Containers::String("Failed to clear Library cache."),
|
||||
stats);
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool AssetImportService::RebuildLibraryCache() {
|
||||
if (!ClearLibraryCache()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ReimportAllAssets();
|
||||
}
|
||||
|
||||
bool AssetImportService::ReimportAllAssets() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Reimport All Assets",
|
||||
Containers::String(),
|
||||
false,
|
||||
"Cannot reimport assets without an active project.",
|
||||
AssetDatabase::MaintenanceStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Reimport All Assets",
|
||||
m_projectRoot,
|
||||
"Reimporting all project assets...");
|
||||
m_assetDatabase.Refresh();
|
||||
|
||||
AssetDatabase::MaintenanceStats stats;
|
||||
const bool succeeded = m_assetDatabase.ReimportAllAssets(&stats);
|
||||
FinishImportStatusLocked(
|
||||
"Reimport All Assets",
|
||||
m_projectRoot,
|
||||
succeeded,
|
||||
succeeded
|
||||
? Containers::String("Reimported all project assets.") + BuildStatsSuffix(stats)
|
||||
: Containers::String("Reimport all assets completed with failures.") + BuildStatsSuffix(stats),
|
||||
stats);
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool AssetImportService::ReimportAsset(const Containers::String& requestPath,
|
||||
ImportedAsset& outAsset) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
false,
|
||||
"Cannot reimport an asset without an active project.",
|
||||
AssetDatabase::MaintenanceStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
Containers::String("Reimporting asset: ") + requestPath);
|
||||
m_assetDatabase.Refresh();
|
||||
|
||||
AssetDatabase::ResolvedAsset resolvedAsset;
|
||||
AssetDatabase::MaintenanceStats stats;
|
||||
if (!m_assetDatabase.ReimportAsset(requestPath, resolvedAsset, &stats)) {
|
||||
const Containers::String databaseError = m_assetDatabase.GetLastErrorMessage();
|
||||
FinishImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
false,
|
||||
!databaseError.Empty()
|
||||
? Containers::String("Failed to reimport asset: ") + requestPath + " - " + databaseError
|
||||
: Containers::String("Failed to reimport asset: ") + requestPath,
|
||||
stats);
|
||||
return false;
|
||||
}
|
||||
|
||||
outAsset = ConvertResolvedAsset(resolvedAsset);
|
||||
FinishImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
true,
|
||||
Containers::String("Reimported asset: ") + requestPath + BuildStatsSuffix(stats),
|
||||
stats);
|
||||
return true;
|
||||
}
|
||||
|
||||
AssetImportService::ImportStatusSnapshot AssetImportService::GetLastImportStatus() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
return m_lastImportStatus;
|
||||
}
|
||||
|
||||
bool AssetImportService::TryGetImportableResourceType(const Containers::String& requestPath,
|
||||
ResourceType& outType) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
outType = ResourceType::Unknown;
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase.TryGetImportableResourceType(requestPath, outType);
|
||||
}
|
||||
|
||||
bool AssetImportService::EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
ImportedAsset& outAsset) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetDatabase::ResolvedAsset resolvedAsset;
|
||||
if (!m_assetDatabase.EnsureArtifact(requestPath, requestedType, resolvedAsset)) {
|
||||
const Containers::String databaseError = m_assetDatabase.GetLastErrorMessage();
|
||||
FinishImportStatusLocked(
|
||||
"Import Asset",
|
||||
requestPath,
|
||||
false,
|
||||
!databaseError.Empty()
|
||||
? Containers::String("Failed to build asset artifact: ") + requestPath + " - " + databaseError
|
||||
: Containers::String("Failed to build asset artifact: ") + requestPath,
|
||||
AssetDatabase::MaintenanceStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
outAsset = ConvertResolvedAsset(resolvedAsset);
|
||||
if (resolvedAsset.imported) {
|
||||
AssetDatabase::MaintenanceStats stats;
|
||||
stats.importedAssetCount = 1;
|
||||
FinishImportStatusLocked(
|
||||
"Import Asset",
|
||||
requestPath,
|
||||
true,
|
||||
Containers::String("Imported asset artifact: ") + requestPath,
|
||||
stats);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AssetImportService::TryGetAssetRef(const Containers::String& requestPath,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase.TryGetAssetRef(requestPath, resourceType, outRef);
|
||||
}
|
||||
|
||||
bool AssetImportService::TryResolveAssetPath(const AssetRef& assetRef,
|
||||
Containers::String& outPath) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase.TryResolveAssetPath(assetRef, outPath);
|
||||
}
|
||||
|
||||
bool AssetImportService::TryGetPrimaryAssetPath(const AssetGUID& guid, Containers::String& outRelativePath) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase.TryGetPrimaryAssetPath(guid, outRelativePath);
|
||||
}
|
||||
|
||||
void AssetImportService::BuildLookupSnapshot(LookupSnapshot& outSnapshot) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
outSnapshot.Clear();
|
||||
if (m_projectRoot.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_assetDatabase.BuildLookupSnapshot(outSnapshot.assetGuidByPathKey, outSnapshot.assetPathByGuid);
|
||||
}
|
||||
|
||||
AssetImportService::ImportedAsset AssetImportService::ConvertResolvedAsset(
|
||||
AssetImportService::ImportedAsset ConvertResolvedAsset(
|
||||
const AssetDatabase::ResolvedAsset& resolvedAsset) {
|
||||
ImportedAsset importedAsset;
|
||||
AssetImportService::ImportedAsset importedAsset;
|
||||
importedAsset.exists = resolvedAsset.exists;
|
||||
importedAsset.artifactReady = resolvedAsset.artifactReady;
|
||||
importedAsset.imported = resolvedAsset.imported;
|
||||
@@ -356,6 +56,350 @@ AssetImportService::ImportedAsset AssetImportService::ConvertResolvedAsset(
|
||||
return importedAsset;
|
||||
}
|
||||
|
||||
Core::uint64 GetCurrentSteadyTimeMs() {
|
||||
return static_cast<Core::uint64>(
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch()).count());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AssetImportService::AssetImportService() = default;
|
||||
|
||||
AssetImportService::~AssetImportService() = default;
|
||||
|
||||
void AssetImportService::Initialize() {
|
||||
if (!m_assetDatabase) {
|
||||
m_assetDatabase = std::make_unique<AssetDatabase>();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetImportService::Shutdown() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_assetDatabase) {
|
||||
m_assetDatabase->Shutdown();
|
||||
}
|
||||
m_projectRoot.Clear();
|
||||
ResetImportStatusLocked();
|
||||
}
|
||||
|
||||
void AssetImportService::SetProjectRoot(const Containers::String& projectRoot) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
|
||||
if (m_projectRoot == projectRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_projectRoot.Empty()) {
|
||||
if (m_assetDatabase) {
|
||||
m_assetDatabase->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
Initialize();
|
||||
m_projectRoot = projectRoot;
|
||||
ResetImportStatusLocked();
|
||||
if (!m_projectRoot.Empty()) {
|
||||
m_assetDatabase->Initialize(m_projectRoot);
|
||||
}
|
||||
}
|
||||
|
||||
Containers::String AssetImportService::GetProjectRoot() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
return m_projectRoot;
|
||||
}
|
||||
|
||||
Containers::String AssetImportService::GetLibraryRoot() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
return m_assetDatabase ? m_assetDatabase->GetLibraryRoot() : Containers::String();
|
||||
}
|
||||
|
||||
bool AssetImportService::BootstrapProject() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Bootstrap Project",
|
||||
Containers::String(),
|
||||
false,
|
||||
"Cannot bootstrap project assets without an active project.",
|
||||
ImportOperationStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Bootstrap Project",
|
||||
m_projectRoot,
|
||||
"Bootstrapping project Library state...");
|
||||
|
||||
const AssetDatabase::MaintenanceStats stats = m_assetDatabase->Refresh();
|
||||
ImportOperationStats serviceStats;
|
||||
serviceStats.importedAssetCount = stats.importedAssetCount;
|
||||
serviceStats.removedArtifactCount = stats.removedArtifactCount;
|
||||
FinishImportStatusLocked(
|
||||
"Bootstrap Project",
|
||||
m_projectRoot,
|
||||
true,
|
||||
Containers::String("Bootstrapped project Library state.") +
|
||||
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
|
||||
serviceStats);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssetImportService::Refresh() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (!m_projectRoot.Empty()) {
|
||||
const AssetDatabase::MaintenanceStats stats = m_assetDatabase->Refresh();
|
||||
if (stats.removedArtifactCount > 0) {
|
||||
ImportOperationStats serviceStats;
|
||||
serviceStats.importedAssetCount = stats.importedAssetCount;
|
||||
serviceStats.removedArtifactCount = stats.removedArtifactCount;
|
||||
FinishImportStatusLocked(
|
||||
"Refresh",
|
||||
m_projectRoot,
|
||||
true,
|
||||
Containers::String("Refresh removed orphan artifact entries.") +
|
||||
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
|
||||
serviceStats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetImportService::ClearLibraryCache() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Clear Library",
|
||||
Containers::String(),
|
||||
false,
|
||||
"Cannot clear Library cache without an active project.",
|
||||
ImportOperationStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Clear Library",
|
||||
m_assetDatabase->GetLibraryRoot(),
|
||||
"Clearing Library cache...");
|
||||
|
||||
const Containers::String projectRoot = m_projectRoot;
|
||||
const Containers::String libraryRoot = m_assetDatabase->GetLibraryRoot();
|
||||
|
||||
m_assetDatabase->Shutdown();
|
||||
|
||||
std::error_code ec;
|
||||
fs::remove_all(fs::path(libraryRoot.CStr()), ec);
|
||||
|
||||
m_assetDatabase->Initialize(projectRoot);
|
||||
const AssetDatabase::MaintenanceStats stats = m_assetDatabase->Refresh();
|
||||
const bool succeeded = !ec;
|
||||
ImportOperationStats serviceStats;
|
||||
serviceStats.importedAssetCount = stats.importedAssetCount;
|
||||
serviceStats.removedArtifactCount = stats.removedArtifactCount;
|
||||
FinishImportStatusLocked(
|
||||
"Clear Library",
|
||||
libraryRoot,
|
||||
succeeded,
|
||||
succeeded
|
||||
? Containers::String("Cleared Library cache and rebuilt source asset lookup.") +
|
||||
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount)
|
||||
: Containers::String("Failed to clear Library cache."),
|
||||
serviceStats);
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool AssetImportService::RebuildLibraryCache() {
|
||||
if (!ClearLibraryCache()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ReimportAllAssets();
|
||||
}
|
||||
|
||||
bool AssetImportService::ReimportAllAssets() {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Reimport All Assets",
|
||||
Containers::String(),
|
||||
false,
|
||||
"Cannot reimport assets without an active project.",
|
||||
ImportOperationStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Reimport All Assets",
|
||||
m_projectRoot,
|
||||
"Reimporting all project assets...");
|
||||
m_assetDatabase->Refresh();
|
||||
|
||||
AssetDatabase::MaintenanceStats stats;
|
||||
const bool succeeded = m_assetDatabase->ReimportAllAssets(&stats);
|
||||
ImportOperationStats serviceStats;
|
||||
serviceStats.importedAssetCount = stats.importedAssetCount;
|
||||
serviceStats.removedArtifactCount = stats.removedArtifactCount;
|
||||
FinishImportStatusLocked(
|
||||
"Reimport All Assets",
|
||||
m_projectRoot,
|
||||
succeeded,
|
||||
succeeded
|
||||
? Containers::String("Reimported all project assets.") +
|
||||
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount)
|
||||
: Containers::String("Reimport all assets completed with failures.") +
|
||||
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
|
||||
serviceStats);
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
bool AssetImportService::ReimportAsset(const Containers::String& requestPath,
|
||||
ImportedAsset& outAsset) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
FinishImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
false,
|
||||
"Cannot reimport an asset without an active project.",
|
||||
ImportOperationStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
BeginImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
Containers::String("Reimporting asset: ") + requestPath);
|
||||
m_assetDatabase->Refresh();
|
||||
|
||||
AssetDatabase::ResolvedAsset resolvedAsset;
|
||||
AssetDatabase::MaintenanceStats stats;
|
||||
if (!m_assetDatabase->ReimportAsset(requestPath, resolvedAsset, &stats)) {
|
||||
const Containers::String databaseError = m_assetDatabase->GetLastErrorMessage();
|
||||
ImportOperationStats serviceStats;
|
||||
serviceStats.importedAssetCount = stats.importedAssetCount;
|
||||
serviceStats.removedArtifactCount = stats.removedArtifactCount;
|
||||
FinishImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
false,
|
||||
!databaseError.Empty()
|
||||
? Containers::String("Failed to reimport asset: ") + requestPath + " - " + databaseError
|
||||
: Containers::String("Failed to reimport asset: ") + requestPath,
|
||||
serviceStats);
|
||||
return false;
|
||||
}
|
||||
|
||||
outAsset = ConvertResolvedAsset(resolvedAsset);
|
||||
ImportOperationStats serviceStats;
|
||||
serviceStats.importedAssetCount = stats.importedAssetCount;
|
||||
serviceStats.removedArtifactCount = stats.removedArtifactCount;
|
||||
FinishImportStatusLocked(
|
||||
"Reimport Asset",
|
||||
requestPath,
|
||||
true,
|
||||
Containers::String("Reimported asset: ") +
|
||||
requestPath +
|
||||
BuildStatsSuffix(serviceStats.importedAssetCount, serviceStats.removedArtifactCount),
|
||||
serviceStats);
|
||||
return true;
|
||||
}
|
||||
|
||||
AssetImportService::ImportStatusSnapshot AssetImportService::GetLastImportStatus() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
return m_lastImportStatus;
|
||||
}
|
||||
|
||||
bool AssetImportService::TryGetImportableResourceType(const Containers::String& requestPath,
|
||||
ResourceType& outType) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
outType = ResourceType::Unknown;
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase && m_assetDatabase->TryGetImportableResourceType(requestPath, outType);
|
||||
}
|
||||
|
||||
bool AssetImportService::EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
ImportedAsset& outAsset) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetDatabase::ResolvedAsset resolvedAsset;
|
||||
if (!m_assetDatabase->EnsureArtifact(requestPath, requestedType, resolvedAsset)) {
|
||||
const Containers::String databaseError = m_assetDatabase->GetLastErrorMessage();
|
||||
FinishImportStatusLocked(
|
||||
"Import Asset",
|
||||
requestPath,
|
||||
false,
|
||||
!databaseError.Empty()
|
||||
? Containers::String("Failed to build asset artifact: ") + requestPath + " - " + databaseError
|
||||
: Containers::String("Failed to build asset artifact: ") + requestPath,
|
||||
ImportOperationStats());
|
||||
return false;
|
||||
}
|
||||
|
||||
outAsset = ConvertResolvedAsset(resolvedAsset);
|
||||
if (resolvedAsset.imported) {
|
||||
AssetDatabase::MaintenanceStats stats;
|
||||
stats.importedAssetCount = 1;
|
||||
ImportOperationStats serviceStats;
|
||||
serviceStats.importedAssetCount = stats.importedAssetCount;
|
||||
serviceStats.removedArtifactCount = stats.removedArtifactCount;
|
||||
FinishImportStatusLocked(
|
||||
"Import Asset",
|
||||
requestPath,
|
||||
true,
|
||||
Containers::String("Imported asset artifact: ") + requestPath,
|
||||
serviceStats);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AssetImportService::TryGetAssetRef(const Containers::String& requestPath,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase && m_assetDatabase->TryGetAssetRef(requestPath, resourceType, outRef);
|
||||
}
|
||||
|
||||
bool AssetImportService::TryResolveAssetPath(const AssetRef& assetRef,
|
||||
Containers::String& outPath) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase && m_assetDatabase->TryResolveAssetPath(assetRef, outPath);
|
||||
}
|
||||
|
||||
bool AssetImportService::TryGetPrimaryAssetPath(const AssetGUID& guid, Containers::String& outRelativePath) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
if (m_projectRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_assetDatabase && m_assetDatabase->TryGetPrimaryAssetPath(guid, outRelativePath);
|
||||
}
|
||||
|
||||
void AssetImportService::BuildLookupSnapshot(LookupSnapshot& outSnapshot) const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_mutex);
|
||||
outSnapshot.Clear();
|
||||
if (m_projectRoot.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_assetDatabase) {
|
||||
m_assetDatabase->BuildLookupSnapshot(outSnapshot.assetGuidByPathKey, outSnapshot.assetPathByGuid);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetImportService::ResetImportStatusLocked() {
|
||||
m_lastImportStatus = ImportStatusSnapshot();
|
||||
m_nextImportStatusRevision = 1;
|
||||
@@ -382,7 +426,7 @@ void AssetImportService::FinishImportStatusLocked(const Containers::String& oper
|
||||
const Containers::String& targetPath,
|
||||
bool success,
|
||||
const Containers::String& message,
|
||||
const AssetDatabase::MaintenanceStats& stats) {
|
||||
const ImportOperationStats& stats) {
|
||||
const Core::uint64 completedAtMs = GetCurrentSteadyTimeMs();
|
||||
const Core::uint64 startedAtMs = m_lastImportStatus.startedAtMs != 0
|
||||
? m_lastImportStatus.startedAtMs
|
||||
76
Pipeline/Core/ImportService/AssetImportService.h
Normal file
76
Pipeline/Core/ImportService/AssetImportService.h
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class AssetDatabase;
|
||||
|
||||
class AssetImportService : public IProjectAssetPipelineService {
|
||||
public:
|
||||
using ImportStatusSnapshot = ProjectAssetImportStatusSnapshot;
|
||||
using LookupSnapshot = ProjectAssetLookupSnapshot;
|
||||
using ImportedAsset = ProjectAssetImportedAsset;
|
||||
|
||||
AssetImportService();
|
||||
~AssetImportService() override;
|
||||
|
||||
void Initialize() override;
|
||||
void Shutdown() override;
|
||||
|
||||
void SetProjectRoot(const Containers::String& projectRoot) override;
|
||||
Containers::String GetProjectRoot() const override;
|
||||
Containers::String GetLibraryRoot() const override;
|
||||
|
||||
bool BootstrapProject() override;
|
||||
void Refresh() override;
|
||||
bool ClearLibraryCache() override;
|
||||
bool RebuildLibraryCache() override;
|
||||
bool ReimportAllAssets() override;
|
||||
bool ReimportAsset(const Containers::String& requestPath,
|
||||
ImportedAsset& outAsset) override;
|
||||
ImportStatusSnapshot GetLastImportStatus() const override;
|
||||
bool TryGetImportableResourceType(const Containers::String& requestPath,
|
||||
ResourceType& outType) const override;
|
||||
|
||||
bool EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
ImportedAsset& outAsset) override;
|
||||
bool TryGetAssetRef(const Containers::String& requestPath,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const override;
|
||||
bool TryResolveAssetPath(const AssetRef& assetRef,
|
||||
Containers::String& outPath) const override;
|
||||
bool TryGetPrimaryAssetPath(const AssetGUID& guid,
|
||||
Containers::String& outRelativePath) const override;
|
||||
void BuildLookupSnapshot(LookupSnapshot& outSnapshot) const override;
|
||||
|
||||
private:
|
||||
struct ImportOperationStats {
|
||||
Core::uint32 importedAssetCount = 0;
|
||||
Core::uint32 removedArtifactCount = 0;
|
||||
};
|
||||
|
||||
void ResetImportStatusLocked();
|
||||
void BeginImportStatusLocked(const Containers::String& operation,
|
||||
const Containers::String& targetPath,
|
||||
const Containers::String& message);
|
||||
void FinishImportStatusLocked(const Containers::String& operation,
|
||||
const Containers::String& targetPath,
|
||||
bool success,
|
||||
const Containers::String& message,
|
||||
const ImportOperationStats& stats);
|
||||
|
||||
mutable std::recursive_mutex m_mutex;
|
||||
Containers::String m_projectRoot;
|
||||
std::unique_ptr<AssetDatabase> m_assetDatabase;
|
||||
ImportStatusSnapshot m_lastImportStatus;
|
||||
Core::uint64 m_nextImportStatusRevision = 1;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
153
Pipeline/Core/ProjectAssetService/ProjectAssetService.cpp
Normal file
153
Pipeline/Core/ProjectAssetService/ProjectAssetService.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "ProjectAssetService.h"
|
||||
|
||||
#include "Pipeline/Core/ImportService/AssetImportService.h"
|
||||
#include "Pipeline/Core/SourceIndex/ProjectAssetIndex.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
struct ProjectAssetService::Impl {
|
||||
mutable AssetImportService assetImportService;
|
||||
mutable ProjectAssetIndex projectAssetIndex;
|
||||
};
|
||||
|
||||
ProjectAssetService::ProjectAssetService()
|
||||
: m_impl(std::make_unique<Impl>()) {
|
||||
}
|
||||
|
||||
ProjectAssetService::~ProjectAssetService() = default;
|
||||
|
||||
void ProjectAssetService::Initialize() {
|
||||
m_impl->assetImportService.Initialize();
|
||||
}
|
||||
|
||||
void ProjectAssetService::Shutdown() {
|
||||
m_impl->assetImportService.Shutdown();
|
||||
m_impl->projectAssetIndex.ResetProjectRoot();
|
||||
}
|
||||
|
||||
void ProjectAssetService::SetProjectRoot(const Containers::String& projectRoot) {
|
||||
m_impl->assetImportService.SetProjectRoot(projectRoot);
|
||||
if (projectRoot.Empty()) {
|
||||
m_impl->projectAssetIndex.ResetProjectRoot();
|
||||
}
|
||||
}
|
||||
|
||||
Containers::String ProjectAssetService::GetProjectRoot() const {
|
||||
return m_impl->assetImportService.GetProjectRoot();
|
||||
}
|
||||
|
||||
Containers::String ProjectAssetService::GetLibraryRoot() const {
|
||||
return m_impl->assetImportService.GetLibraryRoot();
|
||||
}
|
||||
|
||||
bool ProjectAssetService::BootstrapProject() {
|
||||
const bool bootstrapped = m_impl->assetImportService.BootstrapProject();
|
||||
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
|
||||
return bootstrapped;
|
||||
}
|
||||
|
||||
void ProjectAssetService::Refresh() {
|
||||
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_impl->assetImportService.Refresh();
|
||||
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
|
||||
}
|
||||
|
||||
bool ProjectAssetService::ClearLibraryCache() {
|
||||
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool cleared = m_impl->assetImportService.ClearLibraryCache();
|
||||
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
|
||||
return cleared;
|
||||
}
|
||||
|
||||
bool ProjectAssetService::RebuildLibraryCache() {
|
||||
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool rebuilt = m_impl->assetImportService.RebuildLibraryCache();
|
||||
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
|
||||
return rebuilt;
|
||||
}
|
||||
|
||||
bool ProjectAssetService::ReimportAllAssets() {
|
||||
if (m_impl->assetImportService.GetProjectRoot().Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool reimported = m_impl->assetImportService.ReimportAllAssets();
|
||||
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
|
||||
return reimported;
|
||||
}
|
||||
|
||||
bool ProjectAssetService::ReimportAsset(const Containers::String& requestPath,
|
||||
ProjectAssetImportedAsset& outAsset) {
|
||||
if (m_impl->assetImportService.GetProjectRoot().Empty() || requestPath.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool reimported = m_impl->assetImportService.ReimportAsset(requestPath, outAsset);
|
||||
m_impl->projectAssetIndex.RefreshFrom(m_impl->assetImportService);
|
||||
if (reimported && outAsset.assetGuid.IsValid() && !outAsset.relativePath.Empty()) {
|
||||
m_impl->projectAssetIndex.RememberResolvedPath(outAsset.assetGuid, outAsset.relativePath);
|
||||
}
|
||||
|
||||
return reimported;
|
||||
}
|
||||
|
||||
ProjectAssetImportStatusSnapshot ProjectAssetService::GetLastImportStatus() const {
|
||||
return m_impl->assetImportService.GetLastImportStatus();
|
||||
}
|
||||
|
||||
bool ProjectAssetService::TryGetImportableResourceType(const Containers::String& requestPath,
|
||||
ResourceType& outType) const {
|
||||
return m_impl->assetImportService.TryGetImportableResourceType(requestPath, outType);
|
||||
}
|
||||
|
||||
bool ProjectAssetService::EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
ProjectAssetImportedAsset& outAsset) {
|
||||
const bool resolved =
|
||||
m_impl->assetImportService.EnsureArtifact(requestPath, requestedType, outAsset);
|
||||
if (resolved && outAsset.assetGuid.IsValid() && !outAsset.relativePath.Empty()) {
|
||||
m_impl->projectAssetIndex.RememberResolvedPath(outAsset.assetGuid, outAsset.relativePath);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
bool ProjectAssetService::TryGetAssetRef(const Containers::String& requestPath,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const {
|
||||
return m_impl->projectAssetIndex.TryGetAssetRef(
|
||||
m_impl->assetImportService,
|
||||
requestPath,
|
||||
resourceType,
|
||||
outRef);
|
||||
}
|
||||
|
||||
bool ProjectAssetService::TryGetPrimaryAssetPath(const AssetGUID& guid,
|
||||
Containers::String& outRelativePath) const {
|
||||
return m_impl->assetImportService.TryGetPrimaryAssetPath(guid, outRelativePath);
|
||||
}
|
||||
|
||||
bool ProjectAssetService::TryResolveAssetPath(const AssetRef& assetRef,
|
||||
Containers::String& outPath) const {
|
||||
return m_impl->projectAssetIndex.TryResolveAssetPath(
|
||||
m_impl->assetImportService,
|
||||
assetRef,
|
||||
outPath);
|
||||
}
|
||||
|
||||
void ProjectAssetService::BuildLookupSnapshot(ProjectAssetLookupSnapshot& outSnapshot) const {
|
||||
m_impl->assetImportService.BuildLookupSnapshot(outSnapshot);
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
51
Pipeline/Core/ProjectAssetService/ProjectAssetService.h
Normal file
51
Pipeline/Core/ProjectAssetService/ProjectAssetService.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class ProjectAssetService final : public IProjectAssetPipelineService {
|
||||
public:
|
||||
ProjectAssetService();
|
||||
~ProjectAssetService() override;
|
||||
|
||||
void Initialize() override;
|
||||
void Shutdown() override;
|
||||
|
||||
void SetProjectRoot(const Containers::String& projectRoot) override;
|
||||
Containers::String GetProjectRoot() const override;
|
||||
Containers::String GetLibraryRoot() const override;
|
||||
|
||||
bool BootstrapProject() override;
|
||||
void Refresh() override;
|
||||
bool ClearLibraryCache() override;
|
||||
bool RebuildLibraryCache() override;
|
||||
bool ReimportAllAssets() override;
|
||||
bool ReimportAsset(const Containers::String& requestPath,
|
||||
ProjectAssetImportedAsset& outAsset) override;
|
||||
ProjectAssetImportStatusSnapshot GetLastImportStatus() const override;
|
||||
|
||||
bool TryGetImportableResourceType(const Containers::String& requestPath,
|
||||
ResourceType& outType) const override;
|
||||
bool EnsureArtifact(const Containers::String& requestPath,
|
||||
ResourceType requestedType,
|
||||
ProjectAssetImportedAsset& outAsset) override;
|
||||
bool TryGetAssetRef(const Containers::String& requestPath,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const override;
|
||||
bool TryGetPrimaryAssetPath(const AssetGUID& guid,
|
||||
Containers::String& outRelativePath) const override;
|
||||
bool TryResolveAssetPath(const AssetRef& assetRef,
|
||||
Containers::String& outPath) const override;
|
||||
void BuildLookupSnapshot(ProjectAssetLookupSnapshot& outSnapshot) const override;
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,4 @@
|
||||
#include <XCEngine/Core/Asset/ProjectAssetIndex.h>
|
||||
|
||||
#include <XCEngine/Core/Asset/AssetImportService.h>
|
||||
#include "ProjectAssetIndex.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
@@ -75,11 +73,11 @@ void ProjectAssetIndex::ResetProjectRoot(const Containers::String& projectRoot)
|
||||
m_assetPathByGuid.clear();
|
||||
}
|
||||
|
||||
void ProjectAssetIndex::RefreshFrom(const AssetImportService& importService) {
|
||||
AssetImportService::LookupSnapshot snapshot;
|
||||
const Containers::String projectRoot = importService.GetProjectRoot();
|
||||
void ProjectAssetIndex::RefreshFrom(const IProjectAssetResolver& resolver) {
|
||||
ProjectAssetLookupSnapshot snapshot;
|
||||
const Containers::String projectRoot = resolver.GetProjectRoot();
|
||||
if (!projectRoot.Empty()) {
|
||||
importService.BuildLookupSnapshot(snapshot);
|
||||
resolver.BuildLookupSnapshot(snapshot);
|
||||
}
|
||||
|
||||
std::unique_lock<std::shared_mutex> lock(m_mutex);
|
||||
@@ -88,7 +86,7 @@ void ProjectAssetIndex::RefreshFrom(const AssetImportService& importService) {
|
||||
m_assetPathByGuid = std::move(snapshot.assetPathByGuid);
|
||||
}
|
||||
|
||||
bool ProjectAssetIndex::TryGetAssetRef(AssetImportService& importService,
|
||||
bool ProjectAssetIndex::TryGetAssetRef(IProjectAssetPipelineService& pipelineService,
|
||||
const Containers::String& path,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const {
|
||||
@@ -108,21 +106,21 @@ bool ProjectAssetIndex::TryGetAssetRef(AssetImportService& importService,
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
resolved = importService.TryGetAssetRef(path, resourceType, outRef);
|
||||
resolved = pipelineService.TryGetAssetRef(path, resourceType, outRef);
|
||||
if (!resolved) {
|
||||
const Containers::String projectRoot = importService.GetProjectRoot();
|
||||
const Containers::String projectRoot = pipelineService.GetProjectRoot();
|
||||
const Containers::String relativePath = MakeAssetLookupRelativePath(projectRoot, path);
|
||||
if (!relativePath.Empty() && !projectRoot.Empty()) {
|
||||
auto* index = const_cast<ProjectAssetIndex*>(this);
|
||||
importService.Refresh();
|
||||
index->RefreshFrom(importService);
|
||||
resolved = importService.TryGetAssetRef(path, resourceType, outRef);
|
||||
pipelineService.Refresh();
|
||||
index->RefreshFrom(pipelineService);
|
||||
resolved = pipelineService.TryGetAssetRef(path, resourceType, outRef);
|
||||
}
|
||||
}
|
||||
|
||||
if (resolved) {
|
||||
Containers::String relativePath;
|
||||
if (importService.TryGetPrimaryAssetPath(outRef.assetGuid, relativePath)) {
|
||||
if (pipelineService.TryGetPrimaryAssetPath(outRef.assetGuid, relativePath)) {
|
||||
const_cast<ProjectAssetIndex*>(this)->RememberResolvedPath(outRef.assetGuid, relativePath);
|
||||
}
|
||||
}
|
||||
@@ -131,7 +129,7 @@ bool ProjectAssetIndex::TryGetAssetRef(AssetImportService& importService,
|
||||
return resolved;
|
||||
}
|
||||
|
||||
bool ProjectAssetIndex::TryResolveAssetPath(AssetImportService& importService,
|
||||
bool ProjectAssetIndex::TryResolveAssetPath(const IProjectAssetResolver& resolver,
|
||||
const AssetRef& assetRef,
|
||||
Containers::String& outPath) const {
|
||||
if (!assetRef.IsValid()) {
|
||||
@@ -139,7 +137,7 @@ bool ProjectAssetIndex::TryResolveAssetPath(AssetImportService& importService,
|
||||
}
|
||||
|
||||
if (assetRef.localID != kMainAssetLocalID) {
|
||||
return importService.TryResolveAssetPath(assetRef, outPath);
|
||||
return resolver.TryResolveAssetPath(assetRef, outPath);
|
||||
}
|
||||
|
||||
bool resolved = false;
|
||||
@@ -153,7 +151,7 @@ bool ProjectAssetIndex::TryResolveAssetPath(AssetImportService& importService,
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
resolved = importService.TryResolveAssetPath(assetRef, outPath);
|
||||
resolved = resolver.TryResolveAssetPath(assetRef, outPath);
|
||||
if (resolved) {
|
||||
const_cast<ProjectAssetIndex*>(this)->RememberResolvedPath(assetRef.assetGuid, outPath);
|
||||
}
|
||||
33
Pipeline/Core/SourceIndex/ProjectAssetIndex.h
Normal file
33
Pipeline/Core/SourceIndex/ProjectAssetIndex.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
|
||||
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class ProjectAssetIndex {
|
||||
public:
|
||||
void ResetProjectRoot(const Containers::String& projectRoot = Containers::String());
|
||||
void RefreshFrom(const IProjectAssetResolver& resolver);
|
||||
|
||||
bool TryGetAssetRef(IProjectAssetPipelineService& pipelineService,
|
||||
const Containers::String& path,
|
||||
ResourceType resourceType,
|
||||
AssetRef& outRef) const;
|
||||
bool TryResolveAssetPath(const IProjectAssetResolver& resolver,
|
||||
const AssetRef& assetRef,
|
||||
Containers::String& outPath) const;
|
||||
void RememberResolvedPath(const AssetGUID& assetGuid, const Containers::String& relativePath);
|
||||
|
||||
private:
|
||||
mutable std::shared_mutex m_mutex;
|
||||
Containers::String m_projectRoot;
|
||||
std::unordered_map<std::string, AssetGUID> m_assetGuidByPathKey;
|
||||
std::unordered_map<AssetGUID, Containers::String> m_assetPathByGuid;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
107
docs/plan/runtime-rendering-framedata-structure-subplan.md
Normal file
107
docs/plan/runtime-rendering-framedata-structure-subplan.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# XCEngine 子计划:Runtime Rendering FrameData 第一刀
|
||||
|
||||
## 1. 为什么现在切这个
|
||||
|
||||
截至 `2026-05-01`:
|
||||
|
||||
- `Runtime/Asset`
|
||||
- `Runtime/Resources`
|
||||
- `Runtime/Scene`
|
||||
- `Runtime/Rendering/Caches`
|
||||
- `Runtime/Rendering/Shadow`
|
||||
- `Runtime/Rendering/Materials`
|
||||
- `Runtime/Rendering/Picking`
|
||||
|
||||
`Picking` 已完成后,下一步优先选择 `Runtime/Rendering/FrameData`,原因是:
|
||||
|
||||
- `FrameData` 主要由稳定数据头和少量 header-only 工具组成
|
||||
- 依赖虽然广,但大多是只读消费,不牵涉主流程重构
|
||||
- 可以继续按低风险方式把 rendering 运行时数据契约落到真实树
|
||||
- 能为后续 `Extraction` 收口提前建立稳定内部路径
|
||||
|
||||
## 2. 本子计划切什么
|
||||
|
||||
这一轮聚焦:
|
||||
|
||||
- `RenderCameraData.h`
|
||||
- `RenderEnvironmentData.h`
|
||||
- `CullingResults.h`
|
||||
- `VisibleRenderItem.h`
|
||||
- `VisibleGaussianSplatItem.h`
|
||||
- `VisibleVolumeItem.h`
|
||||
- `RenderSceneData.h`
|
||||
- `RendererListUtils.h`
|
||||
|
||||
如果这些文件继续保持 header-only,就只迁真实头文件位置并收口 include。
|
||||
|
||||
这一轮明确不碰:
|
||||
|
||||
- `RenderSceneExtractor.cpp`
|
||||
- `RenderSceneUtility.cpp`
|
||||
- `Builtin*Pass` 行为逻辑
|
||||
- `RenderGraph`
|
||||
- `RenderPipeline`
|
||||
|
||||
除非只是机械性的 include / CMake 路径调整。
|
||||
|
||||
## 3. 目标
|
||||
|
||||
这一刀至少达到:
|
||||
|
||||
- frame data 真实定义迁到 `engine/Runtime/Rendering/FrameData/*`
|
||||
- 旧 `engine/include/XCEngine/Rendering/FrameData/*` public 头收窄成兼容 forwarding 层
|
||||
- 内部实现侧 include 改用稳定内部路径
|
||||
- `XCEditor` 编译和 12 秒 editor smoke 继续稳定
|
||||
|
||||
## 4. 建议切法
|
||||
|
||||
### Step 1:先切基础数据头
|
||||
|
||||
优先处理:
|
||||
|
||||
- `RenderCameraData.h`
|
||||
- `RenderEnvironmentData.h`
|
||||
- `CullingResults.h`
|
||||
|
||||
原因:
|
||||
|
||||
- 是更上层 `RenderSceneData` 的直接基础
|
||||
- 结构独立、风险最低
|
||||
|
||||
### Step 2:再切 visible item 与 scene data
|
||||
|
||||
随后处理:
|
||||
|
||||
- `VisibleRenderItem.h`
|
||||
- `VisibleGaussianSplatItem.h`
|
||||
- `VisibleVolumeItem.h`
|
||||
- `RenderSceneData.h`
|
||||
|
||||
原因:
|
||||
|
||||
- 构成 render scene 抽取结果的主体数据契约
|
||||
- 对后续 `Extraction` 收口是前置条件
|
||||
|
||||
### Step 3:最后切 `RendererListUtils`
|
||||
|
||||
统一收口:
|
||||
|
||||
- `RendererListUtils.h`
|
||||
- `engine/CMakeLists.txt`
|
||||
- 旧 public 头 forwarding
|
||||
- 内部真实路径 include
|
||||
- 阶段验证
|
||||
|
||||
## 5. 验证要求
|
||||
|
||||
本阶段完成后继续保持:
|
||||
|
||||
1. 编译 `XCEditor`
|
||||
2. 执行 12 秒 `editor_ui_smoke_runner`
|
||||
|
||||
## 6. 当前状态
|
||||
|
||||
截至 `2026-05-01`:
|
||||
|
||||
- `Runtime/Rendering/Picking` 第一刀已完成
|
||||
- 下一刀从 `RenderCameraData` 开始
|
||||
@@ -37,8 +37,8 @@
|
||||
代码库按执行阶段拆分为:
|
||||
|
||||
- `engine/foundation`:基础设施层
|
||||
- `engine/shared`:跨阶段稳定契约层
|
||||
- `engine/runtime`:运行时系统
|
||||
- `engine/Shared`:跨阶段稳定契约层
|
||||
- `engine/Runtime`:运行时系统
|
||||
- `pipeline`:导入、构建、烘焙系统
|
||||
- `editor`:当前编辑器应用树
|
||||
- `programs`:各类可执行入口
|
||||
@@ -135,11 +135,11 @@ XCEngine/
|
||||
passes/
|
||||
pipelines/
|
||||
frame_graph/
|
||||
gpu_cache/
|
||||
Caches/
|
||||
audio/
|
||||
physics/
|
||||
scripting/
|
||||
pipeline/
|
||||
Pipeline/
|
||||
core/
|
||||
SourceAssetDB/
|
||||
ArtifactDB/
|
||||
@@ -187,7 +187,7 @@ XCEngine/
|
||||
foundation/
|
||||
shared/
|
||||
runtime/
|
||||
pipeline/
|
||||
Pipeline/
|
||||
editor/
|
||||
e2e/
|
||||
docs/
|
||||
@@ -210,7 +210,7 @@ XCEngine/
|
||||
- 序列化
|
||||
- 文件系统封装
|
||||
|
||||
### 5.2 `engine/shared`
|
||||
### 5.2 `engine/Shared`
|
||||
|
||||
共享契约层承载跨阶段稳定数据结构:
|
||||
|
||||
@@ -223,7 +223,7 @@ XCEngine/
|
||||
|
||||
这一层只放数据契约,不放 manager、loader、cache、importer、orchestrator。
|
||||
|
||||
### 5.3 `engine/runtime`
|
||||
### 5.3 `engine/Runtime`
|
||||
|
||||
运行时层负责:
|
||||
|
||||
@@ -282,13 +282,13 @@ editor 内部目录重组不属于第一阶段工作。
|
||||
|
||||
当前主要系统映射如下:
|
||||
|
||||
- `ResourceManager` -> `engine/runtime/asset/AssetManager`
|
||||
- `ResourceCache` -> `engine/runtime/asset/ResourceStore`
|
||||
- `AsyncLoader` -> `engine/runtime/asset/AsyncLoad`
|
||||
- `ProjectAssetIndex` -> `pipeline/core/SourceIndex`
|
||||
- `AssetImportService` -> `pipeline/core/ImportService`
|
||||
- `AssetDatabase` -> `pipeline/core/SourceAssetDB` + `pipeline/core/ArtifactDB`
|
||||
- `RenderResourceCache` -> `engine/runtime/rendering/gpu_cache`
|
||||
- `ResourceManager` -> `engine/Runtime/Asset/AssetManager`
|
||||
- `ResourceCache` -> `engine/Runtime/Asset/ResourceStore`
|
||||
- `AsyncLoader` -> `engine/Runtime/Asset/AsyncLoad`
|
||||
- `ProjectAssetIndex` -> `Pipeline/Core/SourceIndex`
|
||||
- `AssetImportService` -> `Pipeline/Core/ImportService`
|
||||
- `AssetDatabase` -> `Pipeline/Core/SourceAssetDB` + `Pipeline/Core/ArtifactDB`
|
||||
- `RenderResourceCache` -> `engine/Runtime/Rendering/Caches`
|
||||
|
||||
重构过程中,editor 迭代路径先保持在现有项目工作流上,再逐步完成职责分拆。
|
||||
|
||||
@@ -375,7 +375,7 @@ xcshadercook
|
||||
|
||||
工作项:
|
||||
|
||||
- 把文件迁移到 `engine/foundation`、`engine/shared`、`engine/runtime`、`pipeline`
|
||||
- 把文件迁移到 `engine/foundation`、`engine/Shared`、`engine/Runtime`、`pipeline`
|
||||
- 更新 include、CMake 和测试
|
||||
- 除非前置拆分强依赖,否则不在这一阶段重组 editor 内部树
|
||||
|
||||
@@ -411,3 +411,29 @@ xcshadercook
|
||||
4. shared 层只保留稳定数据契约。
|
||||
5. phases 1 到 3 期间,editor 日常迭代路径保持连续可用。
|
||||
6. 每个阶段完成后都要先编译 `XCEditor`,再执行 12 秒冒烟测试。
|
||||
|
||||
## 10. 当前推进状态
|
||||
|
||||
截至 `2026-05-01`:
|
||||
|
||||
- `engine/Shared/Asset` 已承接 `ArtifactContainer`、`AssetGUID`、`ResourceTypes`、`ResourceDependencyGraph` 的真实定义与实现
|
||||
- `Pipeline/Core` 已承接 `AssetDatabase`、`AssetImportService`、`ProjectAssetIndex`、`ProjectAssetService` 与相关 contracts
|
||||
- `engine/Runtime/Asset` 已承接 `ResourceManager`、`ResourceCache`、`AsyncLoader`
|
||||
- `engine/Runtime/Resources` 已承接 `Texture`、`Mesh`、`AudioClip`、`Shader`、`Material`
|
||||
- 对应旧 `engine/include/XCEngine/Core/Asset/*` 与 `engine/include/XCEngine/Resources/*` 的迁移对象已逐步收窄为兼容 forwarding 层
|
||||
- `engine/Runtime/Scene` 已承接 `Scene`、`SceneRuntime`、`SceneManager`、`RuntimeLoop`、`ModelSceneInstantiation`
|
||||
- `Scene` 这一刀已完成 `XCEditor` 编译与 12 秒 editor smoke 验证
|
||||
|
||||
当前活跃子计划:
|
||||
|
||||
- `Runtime/Rendering/FrameData` 第一刀
|
||||
|
||||
下一步优先项:
|
||||
|
||||
- `Runtime/Rendering/Caches` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Caches`
|
||||
- `Runtime/Rendering/Shadow` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Shadow`
|
||||
- `Runtime/Rendering/Materials` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Materials`
|
||||
- `Runtime/Rendering/Picking` 第一刀已完成,真实定义已迁到 `engine/Runtime/Rendering/Picking`
|
||||
- 继续收窄旧 `engine/include/XCEngine/Rendering/FrameData/*` public 头
|
||||
- 将 `RenderCameraData`、`RenderEnvironmentData`、`CullingResults`、`Visible*`、`RenderSceneData`、`RendererListUtils` 的真实定义迁到 `engine/Runtime/Rendering/FrameData`
|
||||
- 在不重组渲染主流程的前提下,继续把低耦合 rendering 支撑块从旧 `include/src` 物理布局中抽离出来
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
if(MSVC AND POLICY CMP0141)
|
||||
cmake_policy(SET CMP0141 NEW)
|
||||
@@ -101,20 +101,24 @@ endif()
|
||||
set(XCENGINE_RENDERING_EDITOR_SUPPORT_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/ObjectIdCodec.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/ObjectIdCodec.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/RenderObjectIdRegistry.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinObjectIdPass.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Picking/RenderObjectIdRegistry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/RenderObjectIdRegistry.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPass.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinObjectIdPassResources.cpp
|
||||
)
|
||||
|
||||
add_library(XCEngineCore INTERFACE)
|
||||
target_include_directories(XCEngineCore INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||
)
|
||||
|
||||
add_library(XCEngineInput INTERFACE)
|
||||
target_include_directories(XCEngineInput INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||
)
|
||||
@@ -157,6 +161,7 @@ add_library(XCEngineUI STATIC
|
||||
)
|
||||
|
||||
target_include_directories(XCEngineUI PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||
)
|
||||
@@ -183,6 +188,151 @@ else()
|
||||
target_compile_options(XCEngineUI PRIVATE -Wall)
|
||||
endif()
|
||||
|
||||
set(XCENGINE_SHARED_ASSET_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IResource.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetGUID.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetRef.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactFormats.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactContainer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceDependencyGraph.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/AssetGUID/AssetGUID.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/AssetGUID/AssetGUID.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ArtifactContainer/ArtifactContainer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ArtifactContainer/ArtifactContainer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ResourceType/ResourceTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/ResourceType/ResourceTypes.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/DependencyGraph/ResourceDependencyGraph.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Shared/Asset/DependencyGraph/ResourceDependencyGraph.cpp
|
||||
)
|
||||
|
||||
set(XCENGINE_PIPELINE_CORE_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IProjectAssetResolver.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IProjectAssetPipelineService.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetDatabase.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetImportService.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetIndex.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetService.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/Contracts/ProjectAssetTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/Contracts/IProjectAssetResolver.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/Contracts/IProjectAssetPipelineService.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Facade/AssetDatabase.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ImportService/AssetImportService.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ProjectAssetService/ProjectAssetService.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/SourceIndex/ProjectAssetIndex.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Facade/AssetDatabase.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Import/AssetDatabaseImport.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Maintenance/AssetDatabaseMaintenance.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Shared/AssetDatabaseShared.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/AssetDatabase/Stores/AssetDatabaseStores.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ImportService/AssetImportService.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/ProjectAssetService/ProjectAssetService.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../Pipeline/Core/SourceIndex/ProjectAssetIndex.cpp
|
||||
)
|
||||
|
||||
set(XCENGINE_RUNTIME_ASSET_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ImportSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceHandle.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceManager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AsyncLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AssetManager/ResourceManager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AssetManager/ResourceManager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/ResourceStore/ResourceCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/ResourceStore/ResourceCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AsyncLoad/AsyncLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Asset/AsyncLoad/AsyncLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/IResourceLoader.cpp
|
||||
)
|
||||
|
||||
function(xcengine_configure_internal_asset_target target_name)
|
||||
target_include_directories(${target_name} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/GLAD/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/stb
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/assimp/include
|
||||
)
|
||||
|
||||
target_include_directories(${target_name} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
)
|
||||
|
||||
if(XCENGINE_HAS_NANOVDB)
|
||||
target_include_directories(${target_name} PRIVATE
|
||||
${XCENGINE_NANOVDB_INCLUDE_DIR}
|
||||
)
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
XCENGINE_HAS_NANOVDB=1
|
||||
)
|
||||
endif()
|
||||
|
||||
if(XCENGINE_ENABLE_PHYSX)
|
||||
target_include_directories(${target_name} PRIVATE
|
||||
${XCENGINE_PHYSX_INCLUDE_DIR}
|
||||
)
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
XCENGINE_ENABLE_PHYSX=1
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
XCENGINE_ENABLE_PHYSX=0
|
||||
)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${target_name} PRIVATE
|
||||
XCENGINE_SUPPORT_OPENGL
|
||||
XCENGINE_SUPPORT_VULKAN
|
||||
)
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(${target_name} PRIVATE
|
||||
/FS
|
||||
/W3
|
||||
$<$<CONFIG:Debug,RelWithDebInfo>:/Z7>)
|
||||
set_target_properties(${target_name} PROPERTIES
|
||||
MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>"
|
||||
COMPILE_PDB_NAME "${target_name}-compile"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Debug"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/Release"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/MinSizeRel"
|
||||
COMPILE_PDB_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_CURRENT_BINARY_DIR}/compile-pdb/RelWithDebInfo"
|
||||
)
|
||||
else()
|
||||
target_compile_options(${target_name} PRIVATE -Wall)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
add_library(xc_shared_asset STATIC
|
||||
${XCENGINE_SHARED_ASSET_SOURCES}
|
||||
)
|
||||
xcengine_configure_internal_asset_target(xc_shared_asset)
|
||||
|
||||
add_library(xc_pipeline_core STATIC
|
||||
${XCENGINE_PIPELINE_CORE_SOURCES}
|
||||
)
|
||||
xcengine_configure_internal_asset_target(xc_pipeline_core)
|
||||
target_link_libraries(xc_pipeline_core PUBLIC
|
||||
xc_shared_asset
|
||||
)
|
||||
|
||||
add_library(xc_runtime_asset STATIC
|
||||
${XCENGINE_RUNTIME_ASSET_SOURCES}
|
||||
)
|
||||
xcengine_configure_internal_asset_target(xc_runtime_asset)
|
||||
target_link_libraries(xc_runtime_asset PUBLIC
|
||||
xc_shared_asset
|
||||
)
|
||||
|
||||
add_library(XCEngine STATIC
|
||||
# Core (Types, RefCounted, SmartPtr, Event)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Types.h
|
||||
@@ -414,40 +564,12 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/RHIFactory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/RHI/RHIScreenshot.cpp
|
||||
|
||||
# Core/Asset (Resource System Core)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/IResource.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetGUID.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetRef.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactFormats.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ArtifactContainer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetDatabase.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AssetImportService.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ProjectAssetIndex.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ImportSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceHandle.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceManager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/AsyncLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/Asset/ResourceDependencyGraph.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AssetGUID.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ArtifactContainer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AssetDatabase.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AssetImportService.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ProjectAssetIndex.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceManager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/AsyncLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceTypes.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/Asset/ResourceDependencyGraph.cpp
|
||||
|
||||
# Core/IO (File System Abstraction)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/IResourceLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/ResourcePath.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/ResourceFileSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/FileArchive.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Core/IO/ResourcePackage.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/IResourceLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/ResourcePath.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/ResourceFileSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Core/IO/FileArchive.cpp
|
||||
@@ -458,6 +580,9 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/Texture.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/TextureLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Texture/TextureImportSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/Texture.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureImportSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/BuiltinResources.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Model/AssimpModelImporter.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Model/Model.h
|
||||
@@ -469,22 +594,43 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/Mesh.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Mesh/MeshImportSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/Mesh.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshImportSettings.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Material/Material.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Material/MaterialLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Material/MaterialRenderState.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/Material.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/MaterialLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/MaterialRenderState.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/Shader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/ShaderCompilationCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/ShaderKeywordTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Shader/ShaderLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Shader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderCompilationCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderIR.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderSourceUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderAuthoringParser.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderFileUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderRuntimeBuildUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderArtifactLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringInternal.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Volume/VolumeField.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/Volume/VolumeFieldLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/AudioClip/AudioClip.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/AudioClip/AudioLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioClip.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocumentTypes.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocumentCompiler.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocuments.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Resources/UI/UIDocumentLoaders.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/Texture.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/TextureLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Texture/TextureImportSettings.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/Texture.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Texture/TextureImportSettings.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/BuiltinResources.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Model/AssimpModelImporter.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Model/Model.cpp
|
||||
@@ -494,36 +640,28 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/GaussianSplat/GaussianSplatArtifactIO.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/GaussianSplat/GaussianSplatLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/GaussianSplat/Internal/GaussianSplatPlyImporter.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/Mesh.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Mesh/MeshImportSettings.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Material/Material.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Material/MaterialLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Shader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderCompilationCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderIR.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderFileUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderFileUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderRuntimeBuildUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderRuntimeBuildUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderArtifactLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringInternal.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringTextUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringDirectiveUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringShared.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/Internal/ShaderAuthoringParserCore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderSourceUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderSourceUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderAuthoringParser.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Shader/ShaderAuthoringParser.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/Mesh.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Mesh/MeshImportSettings.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/Material.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Material/MaterialLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Shader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderCompilationCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderFileUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderRuntimeBuildUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderArtifactLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringTextUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringDirectiveUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringShared.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/Internal/ShaderAuthoringParserCore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderSourceUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/Shader/ShaderAuthoringParser.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Volume/VolumeField.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/Volume/VolumeFieldLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/AudioClip/AudioClip.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/AudioClip/AudioLoader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioClip.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Resources/AudioClip/AudioLoader.cpp
|
||||
${XCENGINE_OPTIONAL_UI_RESOURCE_SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/UI/UIDocuments.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/UI/UIDocumentLoaders.cpp
|
||||
@@ -595,11 +733,20 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/SceneRenderer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/CullingResults.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RenderCameraData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RenderEnvironmentData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RenderSceneData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/RendererListUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/VisibleGaussianSplatItem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/VisibleRenderItem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/FrameData/VisibleVolumeItem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/CullingResults.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RenderCameraData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RenderEnvironmentData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RenderSceneData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/RendererListUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/VisibleGaussianSplatItem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/VisibleRenderItem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/FrameData/VisibleVolumeItem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/CameraRenderRequest.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/FinalColorPassFactory.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/FinalColorSettings.h
|
||||
@@ -607,6 +754,8 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Planning/SceneRenderRequestUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/ObjectIdCodec.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Picking/RenderObjectIdRegistry.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/ObjectIdCodec.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Picking/RenderObjectIdRegistry.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Extraction/RenderSceneExtractor.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Extraction/RenderSceneUtility.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphTypes.h
|
||||
@@ -619,6 +768,8 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Graph/RenderGraphExecutor.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Materials/RenderMaterialResolve.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Materials/RenderMaterialStateUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Materials/RenderMaterialResolve.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Materials/RenderMaterialStateUtils.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPass.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPassGraphContract.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/RenderPipeline.h
|
||||
@@ -633,8 +784,12 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Execution/SceneRenderSequence.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Caches/RenderResourceCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Caches/DirectionalShadowSurfaceCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/RenderResourceCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/DirectionalShadowSurfaceCache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Shadow/DirectionalShadowData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Shadow/DirectionalShadowRuntime.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowData.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowRuntime.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthStylePassBase.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinDepthOnlyPass.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Rendering/Passes/BuiltinShadowCasterPass.h
|
||||
@@ -676,7 +831,7 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/Internal/CameraFrameGraph/SurfaceUtils.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPipelineFactory.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Internal/RenderPipelineFactory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/DirectionalShadowSurfaceCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/DirectionalShadowSurfaceCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPassGraphContract.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/RenderPipelineStageGraphContract.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Passes/BuiltinDepthStylePassBase.cpp
|
||||
@@ -699,7 +854,7 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Graph/RenderGraph.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Graph/RenderGraphCompiler.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Graph/RenderGraphExecutor.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Caches/RenderResourceCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Caches/RenderResourceCache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/CameraFramePlanBuilder.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/CameraFramePlanBuilder.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/SceneRenderRequestPlanner.cpp
|
||||
@@ -707,8 +862,8 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/CameraFrameFullscreenStagePlanner.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Planning/Internal/DirectionalShadowPlanning.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Shadow/DirectionalShadowData.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Shadow/DirectionalShadowRuntime.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowData.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Rendering/Shadow/DirectionalShadowRuntime.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Execution/SceneRenderer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/BuiltinForwardPipeline.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Rendering/Pipelines/NativeSceneRecorder.cpp
|
||||
@@ -728,11 +883,16 @@ add_library(XCEngine STATIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneRuntime.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/RuntimeLoop.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/ModelSceneInstantiation.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneRuntime.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/RuntimeLoop.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/Scene.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/ModelSceneInstantiation.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneRuntime.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/RuntimeLoop.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneManager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/Scene.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/ModelSceneInstantiation.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneRuntime.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/RuntimeLoop.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Runtime/Scene/SceneManager.cpp
|
||||
|
||||
# Platform
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Platform/PlatformTypes.h
|
||||
@@ -836,6 +996,7 @@ add_library(XCEngine STATIC
|
||||
)
|
||||
|
||||
target_include_directories(XCEngine PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party
|
||||
@@ -846,6 +1007,7 @@ target_include_directories(XCEngine PUBLIC
|
||||
|
||||
target_include_directories(XCEngine PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
)
|
||||
|
||||
if(XCENGINE_HAS_NANOVDB)
|
||||
@@ -866,6 +1028,9 @@ endif()
|
||||
|
||||
target_link_libraries(XCEngine PUBLIC
|
||||
XCEngineUI
|
||||
xc_shared_asset
|
||||
xc_pipeline_core
|
||||
xc_runtime_asset
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/third_party/assimp/lib/assimp-vc143-mt.lib
|
||||
opengl32
|
||||
Vulkan::Vulkan
|
||||
@@ -971,6 +1136,7 @@ if(XCENGINE_ENABLE_RENDERING_EDITOR_SUPPORT)
|
||||
)
|
||||
|
||||
target_include_directories(XCEngineRenderingEditorSupport PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include "ResourceManager.h"
|
||||
#include "Pipeline/Core/ProjectAssetService/ProjectAssetService.h"
|
||||
#include <XCEngine/Core/Asset/ResourceHandle.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
|
||||
#include <XCEngine/Core/IO/ResourceFileSystem.h>
|
||||
#include <XCEngine/Resources/GaussianSplat/GaussianSplatLoader.h>
|
||||
#include <XCEngine/Resources/AudioClip/AudioLoader.h>
|
||||
#include "engine/Runtime/Resources/AudioClip/AudioLoader.h"
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include "engine/Runtime/Resources/Material/MaterialLoader.h"
|
||||
#include <XCEngine/Resources/Model/ModelLoader.h>
|
||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
||||
#include "engine/Runtime/Resources/Mesh/MeshLoader.h"
|
||||
#include "engine/Runtime/Resources/Shader/ShaderLoader.h"
|
||||
#include "engine/Runtime/Resources/Texture/TextureLoader.h"
|
||||
#include <XCEngine/Resources/UI/UIDocumentLoaders.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeFieldLoader.h>
|
||||
#include <exception>
|
||||
@@ -81,6 +82,20 @@ void ResourceManager::Initialize() {
|
||||
EnsureInitialized();
|
||||
}
|
||||
|
||||
void ResourceManager::SetProjectAssetPipelineService(
|
||||
IProjectAssetPipelineService* projectAssetPipelineService) {
|
||||
if (m_ownedProjectAssetPipelineService) {
|
||||
m_ownedProjectAssetPipelineService->Shutdown();
|
||||
m_ownedProjectAssetPipelineService.reset();
|
||||
}
|
||||
|
||||
m_projectAssetPipelineService = projectAssetPipelineService;
|
||||
m_projectAssetResolver = projectAssetPipelineService;
|
||||
if (m_projectAssetPipelineService != nullptr) {
|
||||
m_projectAssetPipelineService->SetProjectRoot(m_resourceRoot);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::EnsureInitialized() {
|
||||
if (m_asyncLoader) {
|
||||
return;
|
||||
@@ -105,7 +120,7 @@ void ResourceManager::EnsureInitialized() {
|
||||
RegisterBuiltinLoader(*this, g_uiThemeLoader);
|
||||
RegisterBuiltinLoader(*this, g_uiSchemaLoader);
|
||||
RegisterBuiltinLoader(*this, g_volumeFieldLoader);
|
||||
m_assetImportService.Initialize();
|
||||
EnsureProjectAssetPipelineService();
|
||||
|
||||
m_asyncLoader = std::move(asyncLoader);
|
||||
}
|
||||
@@ -117,9 +132,14 @@ void ResourceManager::Shutdown() {
|
||||
m_asyncLoader.reset();
|
||||
}
|
||||
|
||||
m_assetImportService.Shutdown();
|
||||
if (m_ownedProjectAssetPipelineService) {
|
||||
m_ownedProjectAssetPipelineService->Shutdown();
|
||||
m_ownedProjectAssetPipelineService.reset();
|
||||
}
|
||||
m_projectAssetPipelineService = nullptr;
|
||||
m_projectAssetResolver = nullptr;
|
||||
ResourceFileSystem::Get().Shutdown();
|
||||
m_projectAssetIndex.ResetProjectRoot();
|
||||
m_resourceRoot.Clear();
|
||||
|
||||
std::lock_guard<std::mutex> inFlightLock(m_inFlightLoadsMutex);
|
||||
m_inFlightLoads.clear();
|
||||
@@ -127,15 +147,18 @@ void ResourceManager::Shutdown() {
|
||||
|
||||
void ResourceManager::SetResourceRoot(const Containers::String& rootPath) {
|
||||
EnsureInitialized();
|
||||
IProjectAssetPipelineService& projectAssetPipelineService =
|
||||
EnsureProjectAssetPipelineService();
|
||||
m_resourceRoot = rootPath;
|
||||
if (!m_resourceRoot.Empty()) {
|
||||
ResourceFileSystem::Get().Initialize(rootPath);
|
||||
m_assetImportService.SetProjectRoot(rootPath);
|
||||
BootstrapProjectAssets();
|
||||
projectAssetPipelineService.SetProjectRoot(rootPath);
|
||||
if (m_ownedProjectAssetPipelineService) {
|
||||
projectAssetPipelineService.BootstrapProject();
|
||||
}
|
||||
} else {
|
||||
m_assetImportService.SetProjectRoot(Containers::String());
|
||||
projectAssetPipelineService.SetProjectRoot(Containers::String());
|
||||
ResourceFileSystem::Get().Shutdown();
|
||||
m_projectAssetIndex.ResetProjectRoot();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,16 +166,6 @@ const Containers::String& ResourceManager::GetResourceRoot() const {
|
||||
return m_resourceRoot;
|
||||
}
|
||||
|
||||
bool ResourceManager::BootstrapProjectAssets() {
|
||||
if (m_resourceRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool bootstrapped = m_assetImportService.BootstrapProject();
|
||||
m_projectAssetIndex.RefreshFrom(m_assetImportService);
|
||||
return bootstrapped;
|
||||
}
|
||||
|
||||
void ResourceManager::AddRef(ResourceGUID guid) {
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
@@ -415,71 +428,11 @@ void ResourceManager::UnloadGroup(const Containers::Array<ResourceGUID>& guids)
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::RefreshProjectAssets() {
|
||||
if (!m_resourceRoot.Empty()) {
|
||||
m_assetImportService.Refresh();
|
||||
m_projectAssetIndex.RefreshFrom(m_assetImportService);
|
||||
}
|
||||
}
|
||||
|
||||
bool ResourceManager::CanReimportProjectAsset(const Containers::String& path) const {
|
||||
if (m_resourceRoot.Empty() || path.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceType importType = ResourceType::Unknown;
|
||||
return m_assetImportService.TryGetImportableResourceType(path, importType);
|
||||
}
|
||||
|
||||
bool ResourceManager::ReimportProjectAsset(const Containers::String& path) {
|
||||
if (m_resourceRoot.Empty() || path.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UnloadAll();
|
||||
|
||||
AssetImportService::ImportedAsset importedAsset;
|
||||
const bool reimported = m_assetImportService.ReimportAsset(path, importedAsset);
|
||||
m_projectAssetIndex.RefreshFrom(m_assetImportService);
|
||||
if (reimported && importedAsset.assetGuid.IsValid() && !importedAsset.relativePath.Empty()) {
|
||||
m_projectAssetIndex.RememberResolvedPath(importedAsset.assetGuid, importedAsset.relativePath);
|
||||
}
|
||||
|
||||
return reimported;
|
||||
}
|
||||
|
||||
bool ResourceManager::ClearProjectLibraryCache() {
|
||||
if (m_resourceRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UnloadAll();
|
||||
const bool cleared = m_assetImportService.ClearLibraryCache();
|
||||
m_projectAssetIndex.RefreshFrom(m_assetImportService);
|
||||
return cleared;
|
||||
}
|
||||
|
||||
bool ResourceManager::RebuildProjectAssetCache() {
|
||||
if (m_resourceRoot.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UnloadAll();
|
||||
const bool rebuilt = m_assetImportService.RebuildLibraryCache();
|
||||
m_projectAssetIndex.RefreshFrom(m_assetImportService);
|
||||
return rebuilt;
|
||||
}
|
||||
|
||||
Containers::String ResourceManager::GetProjectLibraryRoot() const {
|
||||
return m_assetImportService.GetLibraryRoot();
|
||||
}
|
||||
|
||||
AssetImportService::ImportStatusSnapshot ResourceManager::GetProjectAssetImportStatus() const {
|
||||
return m_assetImportService.GetLastImportStatus();
|
||||
}
|
||||
|
||||
bool ResourceManager::TryGetAssetRef(const Containers::String& path, ResourceType resourceType, AssetRef& outRef) const {
|
||||
const bool resolved = m_projectAssetIndex.TryGetAssetRef(m_assetImportService, path, resourceType, outRef);
|
||||
const IProjectAssetResolver* projectAssetResolver = m_projectAssetResolver;
|
||||
const bool resolved =
|
||||
projectAssetResolver != nullptr &&
|
||||
projectAssetResolver->TryGetAssetRef(path, resourceType, outRef);
|
||||
|
||||
if (ShouldTraceResourcePath(path)) {
|
||||
Debug::Logger::Get().Info(
|
||||
@@ -497,7 +450,10 @@ bool ResourceManager::TryGetAssetRef(const Containers::String& path, ResourceTyp
|
||||
}
|
||||
|
||||
bool ResourceManager::TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath) const {
|
||||
const bool resolved = m_projectAssetIndex.TryResolveAssetPath(m_assetImportService, assetRef, outPath);
|
||||
const IProjectAssetResolver* projectAssetResolver = m_projectAssetResolver;
|
||||
const bool resolved =
|
||||
projectAssetResolver != nullptr &&
|
||||
projectAssetResolver->TryResolveAssetPath(assetRef, outPath);
|
||||
|
||||
if (resolved && ShouldTraceResourcePath(outPath)) {
|
||||
Debug::Logger::Get().Info(
|
||||
@@ -527,6 +483,18 @@ bool ResourceManager::IsDeferredSceneLoadEnabled() const {
|
||||
return m_deferredSceneLoadDepth.load() > 0;
|
||||
}
|
||||
|
||||
IProjectAssetPipelineService& ResourceManager::EnsureProjectAssetPipelineService() const {
|
||||
if (m_projectAssetPipelineService == nullptr) {
|
||||
auto projectAssetPipelineService = Core::MakeUnique<ProjectAssetService>();
|
||||
projectAssetPipelineService->Initialize();
|
||||
m_projectAssetPipelineService = projectAssetPipelineService.get();
|
||||
m_projectAssetResolver = projectAssetPipelineService.get();
|
||||
m_ownedProjectAssetPipelineService = std::move(projectAssetPipelineService);
|
||||
}
|
||||
|
||||
return *m_projectAssetPipelineService;
|
||||
}
|
||||
|
||||
LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
||||
ResourceType type,
|
||||
ImportSettings* settings) {
|
||||
@@ -553,17 +521,18 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
||||
|
||||
Containers::String loadPath = path;
|
||||
Containers::String cachePath = path;
|
||||
AssetImportService::ImportedAsset resolvedAsset;
|
||||
ProjectAssetImportedAsset resolvedAsset;
|
||||
ResourceType importableType = ResourceType::Unknown;
|
||||
IProjectAssetResolver* projectAssetResolver = m_projectAssetResolver;
|
||||
const bool shouldUseProjectArtifact =
|
||||
!m_resourceRoot.Empty() &&
|
||||
m_assetImportService.TryGetImportableResourceType(path, importableType) &&
|
||||
projectAssetResolver != nullptr &&
|
||||
projectAssetResolver->TryGetImportableResourceType(path, importableType) &&
|
||||
importableType == type;
|
||||
|
||||
if (shouldUseProjectArtifact &&
|
||||
m_assetImportService.EnsureArtifact(path, type, resolvedAsset) &&
|
||||
projectAssetResolver->EnsureArtifact(path, type, resolvedAsset) &&
|
||||
resolvedAsset.artifactReady) {
|
||||
m_projectAssetIndex.RememberResolvedPath(resolvedAsset.assetGuid, resolvedAsset.relativePath);
|
||||
loadPath = resolvedAsset.runtimeLoadPath;
|
||||
cachePath = loadPath;
|
||||
if (ShouldTraceResourcePath(path)) {
|
||||
@@ -578,13 +547,14 @@ LoadResult ResourceManager::LoadResource(const Containers::String& path,
|
||||
Containers::String builtinShaderAssetPath;
|
||||
if (TryResolveBuiltinShaderAssetPath(path, builtinShaderAssetPath)) {
|
||||
ResourceType builtinImportableType = ResourceType::Unknown;
|
||||
AssetImportService::ImportedAsset builtinResolvedAsset;
|
||||
ProjectAssetImportedAsset builtinResolvedAsset;
|
||||
if (!m_resourceRoot.Empty() &&
|
||||
m_assetImportService.TryGetImportableResourceType(
|
||||
projectAssetResolver != nullptr &&
|
||||
projectAssetResolver->TryGetImportableResourceType(
|
||||
builtinShaderAssetPath,
|
||||
builtinImportableType) &&
|
||||
builtinImportableType == type &&
|
||||
m_assetImportService.EnsureArtifact(
|
||||
projectAssetResolver->EnsureArtifact(
|
||||
builtinShaderAssetPath,
|
||||
type,
|
||||
builtinResolvedAsset) &&
|
||||
185
engine/Runtime/Asset/AssetManager/ResourceManager.h
Normal file
185
engine/Runtime/Asset/AssetManager/ResourceManager.h
Normal file
@@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IProjectAssetPipelineService.h>
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
#include <XCEngine/Core/Asset/ResourceHandle.h>
|
||||
#include <XCEngine/Core/Asset/ResourceCache.h>
|
||||
#include <XCEngine/Core/Asset/AsyncLoader.h>
|
||||
#include <XCEngine/Core/Asset/ImportSettings.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Containers/HashMap.h>
|
||||
#include <XCEngine/Threading/Mutex.h>
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <type_traits>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
class ScopedDeferredSceneLoad {
|
||||
public:
|
||||
explicit ScopedDeferredSceneLoad(ResourceManager& manager = ResourceManager::Get());
|
||||
ScopedDeferredSceneLoad(const ScopedDeferredSceneLoad&) = delete;
|
||||
ScopedDeferredSceneLoad& operator=(const ScopedDeferredSceneLoad&) = delete;
|
||||
~ScopedDeferredSceneLoad();
|
||||
|
||||
private:
|
||||
ResourceManager* m_manager = nullptr;
|
||||
};
|
||||
|
||||
static ResourceManager& Get();
|
||||
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
void SetProjectAssetPipelineService(IProjectAssetPipelineService* projectAssetPipelineService);
|
||||
|
||||
void SetResourceRoot(const Containers::String& rootPath);
|
||||
const Containers::String& GetResourceRoot() const;
|
||||
|
||||
template<typename T>
|
||||
ResourceHandle<T> Load(const Containers::String& path, ImportSettings* settings = nullptr) {
|
||||
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
|
||||
|
||||
LoadResult result = LoadResource(path, GetResourceType<T>(), settings);
|
||||
if (!result || result.resource == nullptr) {
|
||||
return ResourceHandle<T>();
|
||||
}
|
||||
|
||||
return ResourceHandle<T>(static_cast<T*>(result.resource));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ResourceHandle<T> Load(const AssetRef& assetRef, ImportSettings* settings = nullptr) {
|
||||
static_assert(std::is_base_of_v<IResource, T>, "T must derive from IResource");
|
||||
|
||||
Containers::String path;
|
||||
if (!TryResolveAssetPath(assetRef, path)) {
|
||||
return ResourceHandle<T>();
|
||||
}
|
||||
|
||||
return Load<T>(path, settings);
|
||||
}
|
||||
|
||||
void LoadAsync(const Containers::String& path, ResourceType type,
|
||||
std::function<void(LoadResult)> callback);
|
||||
void LoadAsync(const Containers::String& path, ResourceType type, ImportSettings* settings,
|
||||
std::function<void(LoadResult)> callback);
|
||||
void UpdateAsyncLoads();
|
||||
bool IsAsyncLoading() const;
|
||||
Core::uint32 GetAsyncPendingCount() const;
|
||||
|
||||
void Unload(const Containers::String& path);
|
||||
void Unload(ResourceGUID guid);
|
||||
void UnloadUnused();
|
||||
void UnloadAll();
|
||||
|
||||
void AddRef(ResourceGUID guid);
|
||||
void Release(ResourceGUID guid);
|
||||
Core::uint32 GetRefCount(ResourceGUID guid) const;
|
||||
|
||||
void RegisterLoader(IResourceLoader* loader);
|
||||
void UnregisterLoader(ResourceType type);
|
||||
IResourceLoader* GetLoader(ResourceType type) const;
|
||||
|
||||
void SetMemoryBudget(size_t bytes);
|
||||
size_t GetMemoryUsage() const;
|
||||
size_t GetMemoryBudget() const;
|
||||
void FlushCache();
|
||||
|
||||
IResource* Find(const Containers::String& path);
|
||||
IResource* Find(ResourceGUID guid);
|
||||
bool Exists(const Containers::String& path) const;
|
||||
bool Exists(ResourceGUID guid) const;
|
||||
|
||||
Containers::String ResolvePath(const Containers::String& relativePath) const;
|
||||
|
||||
template<typename T>
|
||||
void LoadGroup(const Containers::Array<Containers::String>& paths,
|
||||
std::function<void(ResourceHandle<T>)> callback) {
|
||||
for (const auto& path : paths) {
|
||||
LoadAsync(path, GetResourceType<T>(), [callback](LoadResult result) {
|
||||
if (result && result.resource) {
|
||||
callback(ResourceHandle<T>(static_cast<T*>(result.resource)));
|
||||
} else {
|
||||
callback(ResourceHandle<T>());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Containers::Array<Containers::String> GetResourcePaths() const;
|
||||
void UnloadGroup(const Containers::Array<ResourceGUID>& guids);
|
||||
bool TryGetAssetRef(const Containers::String& path, ResourceType resourceType, AssetRef& outRef) const;
|
||||
bool TryResolveAssetPath(const AssetRef& assetRef, Containers::String& outPath) const;
|
||||
void BeginDeferredSceneLoad();
|
||||
void EndDeferredSceneLoad();
|
||||
bool IsDeferredSceneLoadEnabled() const;
|
||||
|
||||
private:
|
||||
struct InFlightLoadKey {
|
||||
ResourceGUID guid;
|
||||
ResourceType type = ResourceType::Unknown;
|
||||
|
||||
bool operator==(const InFlightLoadKey& other) const {
|
||||
return guid == other.guid && type == other.type;
|
||||
}
|
||||
};
|
||||
|
||||
struct InFlightLoadKeyHasher {
|
||||
size_t operator()(const InFlightLoadKey& key) const noexcept {
|
||||
return std::hash<ResourceGUID>{}(key.guid) ^
|
||||
(static_cast<size_t>(key.type) << 1);
|
||||
}
|
||||
};
|
||||
|
||||
struct InFlightLoadState {
|
||||
bool completed = false;
|
||||
bool success = false;
|
||||
Containers::String errorMessage;
|
||||
std::condition_variable condition;
|
||||
};
|
||||
|
||||
ResourceManager() = default;
|
||||
~ResourceManager() = default;
|
||||
|
||||
void EnsureInitialized();
|
||||
IResource* FindInCache(ResourceGUID guid);
|
||||
void AddToCache(ResourceGUID guid, IResource* resource);
|
||||
IResourceLoader* FindLoader(ResourceType type);
|
||||
void ReloadResource(ResourceGUID guid);
|
||||
LoadResult LoadResource(const Containers::String& path, ResourceType type, ImportSettings* settings);
|
||||
IProjectAssetPipelineService& EnsureProjectAssetPipelineService() const;
|
||||
|
||||
Containers::String m_resourceRoot;
|
||||
Containers::HashMap<ResourceGUID, IResource*> m_resourceCache;
|
||||
Containers::HashMap<ResourceGUID, Core::uint32> m_refCounts;
|
||||
Containers::HashMap<ResourceGUID, Containers::String> m_guidToPath;
|
||||
Containers::HashMap<ResourceType, IResourceLoader*> m_loaders;
|
||||
|
||||
size_t m_memoryUsage = 0;
|
||||
size_t m_memoryBudget = 512 * 1024 * 1024;
|
||||
|
||||
mutable Core::UniqueRef<IProjectAssetPipelineService> m_ownedProjectAssetPipelineService;
|
||||
mutable IProjectAssetPipelineService* m_projectAssetPipelineService = nullptr;
|
||||
mutable IProjectAssetResolver* m_projectAssetResolver = nullptr;
|
||||
ResourceCache m_cache;
|
||||
Core::UniqueRef<AsyncLoader> m_asyncLoader;
|
||||
Threading::Mutex m_mutex;
|
||||
std::mutex m_initializeMutex;
|
||||
std::mutex m_inFlightLoadsMutex;
|
||||
std::unordered_map<InFlightLoadKey, std::shared_ptr<InFlightLoadState>, InFlightLoadKeyHasher> m_inFlightLoads;
|
||||
std::atomic<Core::uint32> m_deferredSceneLoadDepth{0};
|
||||
|
||||
friend class ResourceHandleBase;
|
||||
friend class AsyncLoader;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <XCEngine/Core/Asset/AsyncLoader.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include "AsyncLoader.h"
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
|
||||
#include <XCEngine/Debug/Logger.h>
|
||||
|
||||
namespace XCEngine {
|
||||
108
engine/Runtime/Asset/AsyncLoad/AsyncLoader.h
Normal file
108
engine/Runtime/Asset/AsyncLoad/AsyncLoader.h
Normal file
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
#include <XCEngine/Core/Asset/ImportSettings.h>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
struct LoadRequest {
|
||||
Containers::String path;
|
||||
ResourceType type;
|
||||
std::function<void(LoadResult)> callback;
|
||||
ImportSettings* settings;
|
||||
Core::uint64 requestId;
|
||||
|
||||
LoadRequest() : requestId(0), settings(nullptr) {}
|
||||
|
||||
LoadRequest(const Containers::String& p, ResourceType t,
|
||||
std::function<void(LoadResult)> cb, ImportSettings* s = nullptr)
|
||||
: path(p), type(t), callback(std::move(cb)),
|
||||
settings(s), requestId(GenerateRequestId()) {}
|
||||
|
||||
LoadRequest(LoadRequest&& other) noexcept
|
||||
: path(std::move(other.path)), type(other.type),
|
||||
callback(std::move(other.callback)), settings(other.settings),
|
||||
requestId(other.requestId) {
|
||||
other.settings = nullptr;
|
||||
}
|
||||
|
||||
LoadRequest& operator=(LoadRequest&& other) noexcept {
|
||||
if (this != &other) {
|
||||
path = std::move(other.path);
|
||||
type = other.type;
|
||||
callback = std::move(other.callback);
|
||||
settings = other.settings;
|
||||
requestId = other.requestId;
|
||||
other.settings = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
LoadRequest(const LoadRequest&) = default;
|
||||
LoadRequest& operator=(const LoadRequest&) = default;
|
||||
|
||||
private:
|
||||
static Core::uint64 GenerateRequestId();
|
||||
};
|
||||
|
||||
struct CompletedLoadRequest {
|
||||
LoadRequest request;
|
||||
LoadResult result;
|
||||
|
||||
CompletedLoadRequest(LoadRequest inRequest, LoadResult inResult)
|
||||
: request(std::move(inRequest)), result(std::move(inResult)) {}
|
||||
};
|
||||
|
||||
class AsyncLoader {
|
||||
public:
|
||||
static AsyncLoader& Get();
|
||||
|
||||
void Initialize(Core::uint32 workerThreadCount = 2);
|
||||
void Shutdown();
|
||||
|
||||
void Submit(const Containers::String& path, ResourceType type,
|
||||
std::function<void(LoadResult)> callback);
|
||||
void Submit(const Containers::String& path, ResourceType type, ImportSettings* settings,
|
||||
std::function<void(LoadResult)> callback);
|
||||
|
||||
void Update();
|
||||
bool IsLoading() const { return m_pendingCount > 0; }
|
||||
Core::uint32 GetPendingCount() const { return m_pendingCount; }
|
||||
float GetProgress() const;
|
||||
void CancelAll();
|
||||
void Cancel(Core::uint64 requestId);
|
||||
|
||||
~AsyncLoader() = default;
|
||||
|
||||
AsyncLoader() = default;
|
||||
|
||||
private:
|
||||
void SubmitInternal(LoadRequest request);
|
||||
IResourceLoader* FindLoader(ResourceType type) const;
|
||||
void WorkerThread();
|
||||
void QueueCompleted(LoadRequest request, LoadResult result);
|
||||
|
||||
std::mutex m_queueMutex;
|
||||
std::condition_variable m_pendingCondition;
|
||||
std::deque<LoadRequest> m_pendingQueue;
|
||||
|
||||
std::mutex m_completedMutex;
|
||||
std::deque<CompletedLoadRequest> m_completedQueue;
|
||||
std::vector<std::thread> m_workerThreads;
|
||||
|
||||
std::atomic<bool> m_running{false};
|
||||
std::atomic<Core::uint32> m_pendingCount{0};
|
||||
std::atomic<Core::uint32> m_completedCount{0};
|
||||
std::atomic<Core::uint64> m_totalRequested{0};
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <XCEngine/Core/Asset/ResourceCache.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include "ResourceCache.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
64
engine/Runtime/Asset/ResourceStore/ResourceCache.h
Normal file
64
engine/Runtime/Asset/ResourceStore/ResourceCache.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include <XCEngine/Core/Containers/HashMap.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Threading/Mutex.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class IResource;
|
||||
|
||||
struct CacheEntry {
|
||||
IResource* resource;
|
||||
ResourceGUID guid;
|
||||
size_t memorySize;
|
||||
Core::uint64 lastAccessTime;
|
||||
Core::uint32 accessCount;
|
||||
|
||||
CacheEntry() : resource(nullptr), memorySize(0), lastAccessTime(0), accessCount(0) {}
|
||||
CacheEntry(IResource* res, size_t size);
|
||||
|
||||
static Core::uint64 GetCurrentTick();
|
||||
};
|
||||
|
||||
class ResourceCache {
|
||||
public:
|
||||
ResourceCache();
|
||||
~ResourceCache();
|
||||
|
||||
void Add(ResourceGUID guid, IResource* resource);
|
||||
void Remove(ResourceGUID guid);
|
||||
IResource* Find(ResourceGUID guid) const;
|
||||
void Touch(ResourceGUID guid);
|
||||
|
||||
size_t GetSize() const { return m_cache.Size(); }
|
||||
size_t GetMemoryUsage() const { return m_memoryUsage; }
|
||||
|
||||
void SetMemoryBudget(size_t bytes);
|
||||
size_t GetMemoryBudget() const { return m_memoryBudget; }
|
||||
|
||||
void OnMemoryPressure(size_t requiredBytes);
|
||||
void OnZeroRefCount(ResourceGUID guid);
|
||||
void Flush();
|
||||
void Clear();
|
||||
|
||||
Containers::Array<ResourceGUID> GetLRUList(size_t count) const;
|
||||
|
||||
private:
|
||||
void Evict(size_t requiredBytes);
|
||||
void UpdateMemoryStats();
|
||||
|
||||
Containers::HashMap<ResourceGUID, CacheEntry> m_cache;
|
||||
Containers::Array<ResourceGUID> m_lruOrder;
|
||||
size_t m_memoryUsage = 0;
|
||||
size_t m_memoryBudget = 512 * 1024 * 1024;
|
||||
|
||||
Threading::Mutex m_mutex;
|
||||
mutable Threading::Mutex m_cacheMutex;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Rendering/Caches/DirectionalShadowSurfaceCache.h"
|
||||
#include "DirectionalShadowSurfaceCache.h"
|
||||
|
||||
#include "Rendering/RenderContext.h"
|
||||
#include "Rendering/Shadow/DirectionalShadowData.h"
|
||||
#include "engine/Runtime/Rendering/Shadow/DirectionalShadowData.h"
|
||||
#include "RHI/RHIDevice.h"
|
||||
#include "RHI/RHIResourceView.h"
|
||||
#include "RHI/RHITexture.h"
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/RenderSurface.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
class RHIDevice;
|
||||
class RHIResourceView;
|
||||
class RHITexture;
|
||||
} // namespace RHI
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct DirectionalShadowRenderPlan;
|
||||
struct RenderContext;
|
||||
|
||||
struct DirectionalShadowSurfaceAllocation {
|
||||
RenderSurface surface = {};
|
||||
RHI::RHIResourceView* depthShaderView = nullptr;
|
||||
|
||||
bool IsValid() const {
|
||||
return surface.GetDepthAttachment() != nullptr &&
|
||||
depthShaderView != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
class DirectionalShadowSurfaceCache {
|
||||
public:
|
||||
DirectionalShadowSurfaceCache() = default;
|
||||
DirectionalShadowSurfaceCache(const DirectionalShadowSurfaceCache&) = delete;
|
||||
DirectionalShadowSurfaceCache& operator=(const DirectionalShadowSurfaceCache&) = delete;
|
||||
~DirectionalShadowSurfaceCache();
|
||||
|
||||
const DirectionalShadowSurfaceAllocation* Resolve(
|
||||
const RenderContext& context,
|
||||
const DirectionalShadowRenderPlan& plan);
|
||||
|
||||
private:
|
||||
bool Matches(const RenderContext& context, const DirectionalShadowRenderPlan& plan) const;
|
||||
void Reset();
|
||||
|
||||
RHI::RHIDevice* m_device = nullptr;
|
||||
uint32_t m_width = 0;
|
||||
uint32_t m_height = 0;
|
||||
RHI::RHITexture* m_depthTexture = nullptr;
|
||||
RHI::RHIResourceView* m_depthView = nullptr;
|
||||
DirectionalShadowSurfaceAllocation m_allocation = {};
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Rendering/Caches/RenderResourceCache.h"
|
||||
#include "RenderResourceCache.h"
|
||||
|
||||
#include "Debug/Logger.h"
|
||||
|
||||
160
engine/Runtime/Rendering/Caches/RenderResourceCache.h
Normal file
160
engine/Runtime/Rendering/Caches/RenderResourceCache.h
Normal file
@@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/RHI/RHIBuffer.h>
|
||||
#include <XCEngine/RHI/RHIDevice.h>
|
||||
#include <XCEngine/RHI/RHIResourceView.h>
|
||||
#include <XCEngine/RHI/RHITexture.h>
|
||||
#include <XCEngine/Resources/GaussianSplat/GaussianSplat.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
#include <XCEngine/Resources/Volume/VolumeField.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
class RenderResourceCache {
|
||||
public:
|
||||
enum class GaussianSplatResidencyState {
|
||||
Uninitialized = 0,
|
||||
CpuReady,
|
||||
GpuUploading,
|
||||
GpuReady,
|
||||
Failed
|
||||
};
|
||||
|
||||
struct CachedMesh {
|
||||
RHI::RHIBuffer* vertexBuffer = nullptr;
|
||||
RHI::RHIResourceView* vertexBufferView = nullptr;
|
||||
RHI::RHIBuffer* indexBuffer = nullptr;
|
||||
RHI::RHIResourceView* indexBufferView = nullptr;
|
||||
uint32_t vertexCount = 0;
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t vertexStride = 0;
|
||||
bool uses32BitIndices = false;
|
||||
};
|
||||
|
||||
struct CachedTexture {
|
||||
RHI::RHITexture* texture = nullptr;
|
||||
RHI::RHIResourceView* shaderResourceView = nullptr;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
struct CachedBufferView {
|
||||
RHI::RHIResourceView* resourceView = nullptr;
|
||||
};
|
||||
|
||||
struct CachedVolumeField {
|
||||
RHI::RHIBuffer* payloadBuffer = nullptr;
|
||||
RHI::RHIResourceView* shaderResourceView = nullptr;
|
||||
uint32_t elementStride = 0;
|
||||
uint32_t elementCount = 0;
|
||||
uint64_t payloadSize = 0;
|
||||
Resources::VolumeStorageKind storageKind = Resources::VolumeStorageKind::Unknown;
|
||||
};
|
||||
|
||||
struct CachedGaussianSplatSection {
|
||||
RHI::RHIBuffer* buffer = nullptr;
|
||||
RHI::RHIResourceView* shaderResourceView = nullptr;
|
||||
Resources::GaussianSplatSectionType type = Resources::GaussianSplatSectionType::Unknown;
|
||||
Resources::GaussianSplatSectionFormat format = Resources::GaussianSplatSectionFormat::Unknown;
|
||||
uint32_t elementStride = 0;
|
||||
uint32_t elementCount = 0;
|
||||
uint64_t payloadSize = 0;
|
||||
};
|
||||
|
||||
struct CachedGaussianSplat {
|
||||
GaussianSplatResidencyState residencyState = GaussianSplatResidencyState::Uninitialized;
|
||||
uint32_t contentVersion = 0;
|
||||
uint32_t splatCount = 0;
|
||||
uint32_t chunkCount = 0;
|
||||
uint32_t cameraCount = 0;
|
||||
Math::Bounds bounds;
|
||||
CachedGaussianSplatSection positions;
|
||||
CachedGaussianSplatSection other;
|
||||
CachedGaussianSplatSection color;
|
||||
CachedGaussianSplatSection sh;
|
||||
CachedGaussianSplatSection chunks;
|
||||
};
|
||||
|
||||
~RenderResourceCache();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
const CachedMesh* GetOrCreateMesh(RHI::RHIDevice* device, const Resources::Mesh* mesh);
|
||||
const CachedTexture* GetOrCreateTexture(RHI::RHIDevice* device, const Resources::Texture* texture);
|
||||
const CachedVolumeField* GetOrCreateVolumeField(
|
||||
RHI::RHIDevice* device,
|
||||
const Resources::VolumeField* volumeField);
|
||||
const CachedGaussianSplat* GetOrCreateGaussianSplat(
|
||||
RHI::RHIDevice* device,
|
||||
const Resources::GaussianSplat* gaussianSplat);
|
||||
const CachedBufferView* GetOrCreateBufferView(
|
||||
RHI::RHIDevice* device,
|
||||
RHI::RHIBuffer* buffer,
|
||||
RHI::ResourceViewType viewType,
|
||||
const RHI::ResourceViewDesc& viewDesc);
|
||||
|
||||
private:
|
||||
struct BufferViewCacheKey {
|
||||
const RHI::RHIBuffer* buffer = nullptr;
|
||||
RHI::ResourceViewType viewType = RHI::ResourceViewType::ShaderResource;
|
||||
uint32_t format = 0;
|
||||
RHI::ResourceViewDimension dimension = RHI::ResourceViewDimension::Unknown;
|
||||
uint64_t bufferLocation = 0;
|
||||
uint32_t firstElement = 0;
|
||||
uint32_t elementCount = 0;
|
||||
uint32_t structureByteStride = 0;
|
||||
|
||||
bool operator==(const BufferViewCacheKey& other) const {
|
||||
return buffer == other.buffer &&
|
||||
viewType == other.viewType &&
|
||||
format == other.format &&
|
||||
dimension == other.dimension &&
|
||||
bufferLocation == other.bufferLocation &&
|
||||
firstElement == other.firstElement &&
|
||||
elementCount == other.elementCount &&
|
||||
structureByteStride == other.structureByteStride;
|
||||
}
|
||||
};
|
||||
|
||||
struct BufferViewCacheKeyHash {
|
||||
size_t operator()(const BufferViewCacheKey& key) const noexcept;
|
||||
};
|
||||
|
||||
bool UploadMesh(RHI::RHIDevice* device, const Resources::Mesh* mesh, CachedMesh& cachedMesh);
|
||||
bool UploadTexture(RHI::RHIDevice* device, const Resources::Texture* texture, CachedTexture& cachedTexture);
|
||||
bool UploadVolumeField(
|
||||
RHI::RHIDevice* device,
|
||||
const Resources::VolumeField* volumeField,
|
||||
CachedVolumeField& cachedVolumeField);
|
||||
bool UploadGaussianSplat(
|
||||
RHI::RHIDevice* device,
|
||||
const Resources::GaussianSplat* gaussianSplat,
|
||||
CachedGaussianSplat& cachedGaussianSplat);
|
||||
bool UploadGaussianSplatSection(
|
||||
RHI::RHIDevice* device,
|
||||
const Resources::GaussianSplat* gaussianSplat,
|
||||
Resources::GaussianSplatSectionType sectionType,
|
||||
Resources::GaussianSplatSectionFormat requiredFormat,
|
||||
uint32_t requiredStride,
|
||||
bool requiredSection,
|
||||
CachedGaussianSplatSection& cachedSection);
|
||||
bool CreateBufferView(
|
||||
RHI::RHIDevice* device,
|
||||
RHI::RHIBuffer* buffer,
|
||||
RHI::ResourceViewType viewType,
|
||||
const RHI::ResourceViewDesc& viewDesc,
|
||||
CachedBufferView& cachedBufferView);
|
||||
|
||||
std::unordered_map<const Resources::Mesh*, CachedMesh> m_meshCache;
|
||||
std::unordered_map<const Resources::Texture*, CachedTexture> m_textureCache;
|
||||
std::unordered_map<const Resources::VolumeField*, CachedVolumeField> m_volumeFieldCache;
|
||||
std::unordered_map<const Resources::GaussianSplat*, CachedGaussianSplat> m_gaussianSplatCache;
|
||||
std::unordered_map<BufferViewCacheKey, CachedBufferView, BufferViewCacheKeyHash> m_bufferViewCache;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
104
engine/Runtime/Rendering/FrameData/CullingResults.h
Normal file
104
engine/Runtime/Rendering/FrameData/CullingResults.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class RendererListType : Core::uint32 {
|
||||
AllVisible = 0,
|
||||
Opaque = 1,
|
||||
Transparent = 2,
|
||||
ShadowCaster = 3,
|
||||
ObjectId = 4
|
||||
};
|
||||
|
||||
enum class RendererSortMode : Core::uint32 {
|
||||
None = 0,
|
||||
FrontToBack = 1,
|
||||
BackToFront = 2
|
||||
};
|
||||
|
||||
struct FilteringSettings {
|
||||
Core::int32 renderQueueMin = std::numeric_limits<Core::int32>::lowest();
|
||||
Core::int32 renderQueueMax = std::numeric_limits<Core::int32>::max();
|
||||
Core::uint32 renderLayerMask = std::numeric_limits<Core::uint32>::max();
|
||||
bool requireShadowCasting = false;
|
||||
bool requireRenderObjectId = false;
|
||||
|
||||
bool operator==(const FilteringSettings& other) const {
|
||||
return renderQueueMin == other.renderQueueMin &&
|
||||
renderQueueMax == other.renderQueueMax &&
|
||||
renderLayerMask == other.renderLayerMask &&
|
||||
requireShadowCasting == other.requireShadowCasting &&
|
||||
requireRenderObjectId == other.requireRenderObjectId;
|
||||
}
|
||||
};
|
||||
|
||||
struct SortingSettings {
|
||||
RendererSortMode sortMode = RendererSortMode::None;
|
||||
|
||||
bool operator==(const SortingSettings& other) const {
|
||||
return sortMode == other.sortMode;
|
||||
}
|
||||
};
|
||||
|
||||
struct RendererListDesc {
|
||||
RendererListType type = RendererListType::AllVisible;
|
||||
FilteringSettings filtering = {};
|
||||
SortingSettings sorting = {};
|
||||
|
||||
bool operator==(const RendererListDesc& other) const {
|
||||
return type == other.type &&
|
||||
filtering == other.filtering &&
|
||||
sorting == other.sorting;
|
||||
}
|
||||
};
|
||||
|
||||
struct RendererList {
|
||||
RendererListDesc desc = {};
|
||||
std::vector<Core::uint32> visibleRenderItemIndices;
|
||||
|
||||
bool Empty() const {
|
||||
return visibleRenderItemIndices.empty();
|
||||
}
|
||||
|
||||
size_t Size() const {
|
||||
return visibleRenderItemIndices.size();
|
||||
}
|
||||
};
|
||||
|
||||
struct CullingResults {
|
||||
std::vector<RendererList> rendererLists;
|
||||
|
||||
void Clear() {
|
||||
rendererLists.clear();
|
||||
}
|
||||
|
||||
RendererList* FindRendererList(RendererListType type) {
|
||||
for (RendererList& rendererList : rendererLists) {
|
||||
if (rendererList.desc.type == type) {
|
||||
return &rendererList;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const RendererList* FindRendererList(RendererListType type) const {
|
||||
for (const RendererList& rendererList : rendererLists) {
|
||||
if (rendererList.desc.type == type) {
|
||||
return &rendererList;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
54
engine/Runtime/Rendering/FrameData/RenderCameraData.h
Normal file
54
engine/Runtime/Rendering/FrameData/RenderCameraData.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
#include <XCEngine/Core/Math/Matrix4.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class RenderClearFlags : uint8_t {
|
||||
None = 0,
|
||||
Color = 1 << 0,
|
||||
Depth = 1 << 1,
|
||||
All = (1 << 0) | (1 << 1)
|
||||
};
|
||||
|
||||
constexpr RenderClearFlags operator|(RenderClearFlags lhs, RenderClearFlags rhs) {
|
||||
return static_cast<RenderClearFlags>(
|
||||
static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
constexpr RenderClearFlags operator&(RenderClearFlags lhs, RenderClearFlags rhs) {
|
||||
return static_cast<RenderClearFlags>(
|
||||
static_cast<uint8_t>(lhs) & static_cast<uint8_t>(rhs));
|
||||
}
|
||||
|
||||
constexpr bool HasRenderClearFlag(RenderClearFlags flags, RenderClearFlags flag) {
|
||||
return static_cast<uint8_t>(flags & flag) != 0;
|
||||
}
|
||||
|
||||
struct RenderCameraData {
|
||||
Math::Matrix4x4 view = Math::Matrix4x4::Identity();
|
||||
Math::Matrix4x4 projection = Math::Matrix4x4::Identity();
|
||||
Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity();
|
||||
Math::Vector3 worldPosition = Math::Vector3::Zero();
|
||||
Math::Vector3 worldRight = Math::Vector3::Right();
|
||||
Math::Vector3 worldUp = Math::Vector3::Up();
|
||||
Math::Vector3 worldForward = Math::Vector3::Forward();
|
||||
Math::Color clearColor = Math::Color::Black();
|
||||
RenderClearFlags clearFlags = RenderClearFlags::All;
|
||||
bool perspectiveProjection = true;
|
||||
float verticalFovRadians = 60.0f * Math::DEG_TO_RAD;
|
||||
float orthographicSize = 5.0f;
|
||||
float aspectRatio = 1.0f;
|
||||
float nearClipPlane = 0.1f;
|
||||
float farClipPlane = 1000.0f;
|
||||
uint32_t viewportWidth = 0;
|
||||
uint32_t viewportHeight = 0;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
55
engine/Runtime/Rendering/FrameData/RenderEnvironmentData.h
Normal file
55
engine/Runtime/Rendering/FrameData/RenderEnvironmentData.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Color.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
class Material;
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class RenderEnvironmentMode : uint32_t {
|
||||
None = 0,
|
||||
ProceduralSkybox,
|
||||
MaterialSkybox
|
||||
};
|
||||
|
||||
struct ProceduralSkyboxData {
|
||||
Math::Color topColor = Math::Color(0.18f, 0.36f, 0.74f, 1.0f);
|
||||
Math::Color horizonColor = Math::Color(0.78f, 0.84f, 0.92f, 1.0f);
|
||||
Math::Color bottomColor = Math::Color(0.92f, 0.93f, 0.95f, 1.0f);
|
||||
};
|
||||
|
||||
struct MaterialSkyboxData {
|
||||
const Resources::Material* material = nullptr;
|
||||
|
||||
bool IsValid() const {
|
||||
return material != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderEnvironmentData {
|
||||
RenderEnvironmentMode mode = RenderEnvironmentMode::None;
|
||||
ProceduralSkyboxData skybox = {};
|
||||
MaterialSkyboxData materialSkybox = {};
|
||||
|
||||
bool HasProceduralSkybox() const {
|
||||
return mode == RenderEnvironmentMode::ProceduralSkybox;
|
||||
}
|
||||
|
||||
bool HasMaterialSkybox() const {
|
||||
return mode == RenderEnvironmentMode::MaterialSkybox && materialSkybox.IsValid();
|
||||
}
|
||||
|
||||
bool HasSkybox() const {
|
||||
return HasProceduralSkybox() || HasMaterialSkybox();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
107
engine/Runtime/Rendering/FrameData/RenderSceneData.h
Normal file
107
engine/Runtime/Rendering/FrameData/RenderSceneData.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Vector2.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Core/Math/Vector4.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderKeywordTypes.h>
|
||||
|
||||
#include "engine/Runtime/Rendering/FrameData/CullingResults.h"
|
||||
#include "engine/Runtime/Rendering/FrameData/RenderCameraData.h"
|
||||
#include "engine/Runtime/Rendering/FrameData/RenderEnvironmentData.h"
|
||||
#include "engine/Runtime/Rendering/FrameData/VisibleGaussianSplatItem.h"
|
||||
#include "engine/Runtime/Rendering/FrameData/VisibleRenderItem.h"
|
||||
#include "engine/Runtime/Rendering/FrameData/VisibleVolumeItem.h"
|
||||
#include "engine/Runtime/Rendering/Shadow/DirectionalShadowData.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
class CameraComponent;
|
||||
} // namespace Components
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct RenderDirectionalLightData {
|
||||
bool enabled = false;
|
||||
bool castsShadows = false;
|
||||
Math::Vector3 direction = Math::Vector3::Back();
|
||||
float intensity = 1.0f;
|
||||
Math::Color color = Math::Color::White();
|
||||
};
|
||||
|
||||
enum class RenderLightType : uint32_t {
|
||||
Directional = 0,
|
||||
Point = 1,
|
||||
Spot = 2
|
||||
};
|
||||
|
||||
struct RenderAdditionalLightData {
|
||||
RenderLightType type = RenderLightType::Point;
|
||||
bool enabled = false;
|
||||
bool castsShadows = false;
|
||||
Math::Color color = Math::Color::White();
|
||||
float intensity = 1.0f;
|
||||
Math::Vector3 position = Math::Vector3::Zero();
|
||||
Math::Vector3 direction = Math::Vector3::Back();
|
||||
float range = 0.0f;
|
||||
float spotAngle = 0.0f;
|
||||
};
|
||||
|
||||
struct RenderLightingData {
|
||||
static constexpr uint32_t kMaxAdditionalLightCount = 8u;
|
||||
|
||||
RenderDirectionalLightData mainDirectionalLight;
|
||||
RenderDirectionalShadowData mainDirectionalShadow;
|
||||
std::array<RenderAdditionalLightData, kMaxAdditionalLightCount> additionalLights = {};
|
||||
uint32_t additionalLightCount = 0u;
|
||||
|
||||
bool HasMainDirectionalLight() const {
|
||||
return mainDirectionalLight.enabled;
|
||||
}
|
||||
|
||||
bool HasMainDirectionalShadow() const {
|
||||
return mainDirectionalShadow.IsValid();
|
||||
}
|
||||
|
||||
bool HasAdditionalLights() const {
|
||||
return additionalLightCount > 0u;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderSceneData {
|
||||
Components::CameraComponent* camera = nullptr;
|
||||
RenderCameraData cameraData;
|
||||
RenderEnvironmentData environment;
|
||||
RenderLightingData lighting;
|
||||
Resources::ShaderKeywordSet globalShaderKeywords;
|
||||
CullingResults cullingResults;
|
||||
std::vector<VisibleRenderItem> visibleItems;
|
||||
std::vector<VisibleGaussianSplatItem> visibleGaussianSplats;
|
||||
std::vector<VisibleVolumeItem> visibleVolumes;
|
||||
|
||||
bool HasCamera() const {
|
||||
return camera != nullptr;
|
||||
}
|
||||
|
||||
RendererList* FindRendererList(RendererListType type) {
|
||||
return cullingResults.FindRendererList(type);
|
||||
}
|
||||
|
||||
const RendererList* FindRendererList(RendererListType type) const {
|
||||
return cullingResults.FindRendererList(type);
|
||||
}
|
||||
|
||||
const VisibleRenderItem* TryGetVisibleRenderItem(Core::uint32 index) const {
|
||||
if (index >= visibleItems.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &visibleItems[index];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
219
engine/Runtime/Rendering/FrameData/RendererListUtils.h
Normal file
219
engine/Runtime/Rendering/FrameData/RendererListUtils.h
Normal file
@@ -0,0 +1,219 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
|
||||
#include "engine/Runtime/Rendering/FrameData/RenderSceneData.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
inline FilteringSettings BuildDefaultFilteringSettings(RendererListType type) {
|
||||
FilteringSettings filtering = {};
|
||||
|
||||
switch (type) {
|
||||
case RendererListType::AllVisible:
|
||||
break;
|
||||
case RendererListType::Opaque:
|
||||
filtering.renderQueueMax =
|
||||
static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent) - 1;
|
||||
break;
|
||||
case RendererListType::Transparent:
|
||||
filtering.renderQueueMin =
|
||||
static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
|
||||
break;
|
||||
case RendererListType::ShadowCaster:
|
||||
filtering.requireShadowCasting = true;
|
||||
break;
|
||||
case RendererListType::ObjectId:
|
||||
filtering.requireRenderObjectId = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return filtering;
|
||||
}
|
||||
|
||||
inline SortingSettings BuildDefaultSortingSettings(RendererListType type) {
|
||||
SortingSettings sorting = {};
|
||||
|
||||
switch (type) {
|
||||
case RendererListType::Opaque:
|
||||
case RendererListType::ShadowCaster:
|
||||
case RendererListType::ObjectId:
|
||||
sorting.sortMode = RendererSortMode::FrontToBack;
|
||||
break;
|
||||
case RendererListType::Transparent:
|
||||
sorting.sortMode = RendererSortMode::BackToFront;
|
||||
break;
|
||||
case RendererListType::AllVisible:
|
||||
sorting.sortMode = RendererSortMode::None;
|
||||
break;
|
||||
}
|
||||
|
||||
return sorting;
|
||||
}
|
||||
|
||||
inline RendererListDesc BuildDefaultRendererListDesc(RendererListType type) {
|
||||
RendererListDesc desc = {};
|
||||
desc.type = type;
|
||||
desc.filtering = BuildDefaultFilteringSettings(type);
|
||||
desc.sorting = BuildDefaultSortingSettings(type);
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline bool MatchesFilteringSettings(
|
||||
const VisibleRenderItem& visibleItem,
|
||||
const FilteringSettings& filtering) {
|
||||
if (visibleItem.renderQueue < filtering.renderQueueMin ||
|
||||
visibleItem.renderQueue > filtering.renderQueueMax) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (visibleItem.renderLayer >= 32u ||
|
||||
((filtering.renderLayerMask &
|
||||
(1u << visibleItem.renderLayer)) == 0u)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filtering.requireShadowCasting &&
|
||||
visibleItem.meshRenderer != nullptr &&
|
||||
!visibleItem.meshRenderer->GetCastShadows()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filtering.requireRenderObjectId &&
|
||||
!IsValidRenderObjectId(visibleItem.renderObjectId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool MatchesRendererListDesc(
|
||||
const VisibleRenderItem& visibleItem,
|
||||
const RendererListDesc& desc) {
|
||||
return MatchesFilteringSettings(visibleItem, desc.filtering);
|
||||
}
|
||||
|
||||
inline RendererList BuildRendererList(
|
||||
RendererListType type,
|
||||
const std::vector<VisibleRenderItem>& visibleItems) {
|
||||
RendererList rendererList = {};
|
||||
rendererList.desc = BuildDefaultRendererListDesc(type);
|
||||
rendererList.visibleRenderItemIndices.reserve(visibleItems.size());
|
||||
|
||||
for (Core::uint32 visibleItemIndex = 0;
|
||||
visibleItemIndex < static_cast<Core::uint32>(visibleItems.size());
|
||||
++visibleItemIndex) {
|
||||
if (!MatchesRendererListDesc(
|
||||
visibleItems[visibleItemIndex],
|
||||
rendererList.desc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rendererList.visibleRenderItemIndices.push_back(visibleItemIndex);
|
||||
}
|
||||
|
||||
return rendererList;
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
inline void VisitRendererListVisibleItems(
|
||||
const RenderSceneData& sceneData,
|
||||
RendererListType rendererListType,
|
||||
Visitor&& visitor) {
|
||||
const RendererList* rendererList = sceneData.FindRendererList(rendererListType);
|
||||
if (rendererList != nullptr) {
|
||||
for (Core::uint32 visibleItemIndex : rendererList->visibleRenderItemIndices) {
|
||||
const VisibleRenderItem* visibleItem =
|
||||
sceneData.TryGetVisibleRenderItem(visibleItemIndex);
|
||||
if (visibleItem == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visitor(*visibleItem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const RendererListDesc fallbackDesc = BuildDefaultRendererListDesc(rendererListType);
|
||||
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
|
||||
if (!MatchesRendererListDesc(visibleItem, fallbackDesc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visitor(visibleItem);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Visitor>
|
||||
inline void VisitRendererListVisibleItems(
|
||||
const RenderSceneData& sceneData,
|
||||
const RendererListDesc& rendererListDesc,
|
||||
Visitor&& visitor) {
|
||||
const RendererListDesc defaultDesc =
|
||||
BuildDefaultRendererListDesc(
|
||||
rendererListDesc.type);
|
||||
if (rendererListDesc == defaultDesc) {
|
||||
VisitRendererListVisibleItems(
|
||||
sceneData,
|
||||
rendererListDesc.type,
|
||||
std::forward<Visitor>(visitor));
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const VisibleRenderItem*> matchedItems;
|
||||
matchedItems.reserve(sceneData.visibleItems.size());
|
||||
for (const VisibleRenderItem& visibleItem : sceneData.visibleItems) {
|
||||
if (!MatchesRendererListDesc(
|
||||
visibleItem,
|
||||
rendererListDesc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matchedItems.push_back(&visibleItem);
|
||||
}
|
||||
|
||||
switch (rendererListDesc.sorting.sortMode) {
|
||||
case RendererSortMode::FrontToBack:
|
||||
std::stable_sort(
|
||||
matchedItems.begin(),
|
||||
matchedItems.end(),
|
||||
[](const VisibleRenderItem* lhs, const VisibleRenderItem* rhs) {
|
||||
return lhs != nullptr &&
|
||||
rhs != nullptr &&
|
||||
lhs->cameraDistanceSq <
|
||||
rhs->cameraDistanceSq;
|
||||
});
|
||||
break;
|
||||
case RendererSortMode::BackToFront:
|
||||
std::stable_sort(
|
||||
matchedItems.begin(),
|
||||
matchedItems.end(),
|
||||
[](const VisibleRenderItem* lhs, const VisibleRenderItem* rhs) {
|
||||
return lhs != nullptr &&
|
||||
rhs != nullptr &&
|
||||
lhs->cameraDistanceSq >
|
||||
rhs->cameraDistanceSq;
|
||||
});
|
||||
break;
|
||||
case RendererSortMode::None:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (const VisibleRenderItem* visibleItem : matchedItems) {
|
||||
if (visibleItem == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visitor(*visibleItem);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Matrix4.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
class GameObject;
|
||||
class GaussianSplatRendererComponent;
|
||||
} // namespace Components
|
||||
|
||||
namespace Resources {
|
||||
class GaussianSplat;
|
||||
class Material;
|
||||
} // namespace Resources
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct VisibleGaussianSplatItem {
|
||||
Components::GameObject* gameObject = nullptr;
|
||||
Components::GaussianSplatRendererComponent* gaussianSplatRenderer = nullptr;
|
||||
Resources::GaussianSplat* gaussianSplat = nullptr;
|
||||
const Resources::Material* material = nullptr;
|
||||
Core::int32 renderQueue = 0;
|
||||
float cameraDistanceSq = 0.0f;
|
||||
Math::Matrix4x4 localToWorld = Math::Matrix4x4::Identity();
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
35
engine/Runtime/Rendering/FrameData/VisibleRenderItem.h
Normal file
35
engine/Runtime/Rendering/FrameData/VisibleRenderItem.h
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Matrix4.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
|
||||
#include "engine/Runtime/Rendering/Picking/ObjectIdCodec.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
class GameObject;
|
||||
class MeshFilterComponent;
|
||||
class MeshRendererComponent;
|
||||
} // namespace Components
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct VisibleRenderItem {
|
||||
Components::GameObject* gameObject = nullptr;
|
||||
Components::MeshFilterComponent* meshFilter = nullptr;
|
||||
Components::MeshRendererComponent* meshRenderer = nullptr;
|
||||
Resources::Mesh* mesh = nullptr;
|
||||
const Resources::Material* material = nullptr;
|
||||
RenderObjectId renderObjectId = kInvalidRenderObjectId;
|
||||
Core::uint32 materialIndex = 0;
|
||||
Core::uint32 sectionIndex = 0;
|
||||
bool hasSection = false;
|
||||
Core::uint32 renderLayer = 0;
|
||||
Core::int32 renderQueue = 0;
|
||||
float cameraDistanceSq = 0.0f;
|
||||
Math::Matrix4x4 localToWorld = Math::Matrix4x4::Identity();
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
30
engine/Runtime/Rendering/FrameData/VisibleVolumeItem.h
Normal file
30
engine/Runtime/Rendering/FrameData/VisibleVolumeItem.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Matrix4.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
class GameObject;
|
||||
class VolumeRendererComponent;
|
||||
} // namespace Components
|
||||
|
||||
namespace Resources {
|
||||
class Material;
|
||||
class VolumeField;
|
||||
} // namespace Resources
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct VisibleVolumeItem {
|
||||
Components::GameObject* gameObject = nullptr;
|
||||
Components::VolumeRendererComponent* volumeRenderer = nullptr;
|
||||
Resources::VolumeField* volumeField = nullptr;
|
||||
const Resources::Material* material = nullptr;
|
||||
Core::int32 renderQueue = 0;
|
||||
float cameraDistanceSq = 0.0f;
|
||||
Math::Matrix4x4 localToWorld = Math::Matrix4x4::Identity();
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
509
engine/Runtime/Rendering/Materials/RenderMaterialResolve.h
Normal file
509
engine/Runtime/Rendering/Materials/RenderMaterialResolve.h
Normal file
@@ -0,0 +1,509 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/RHI/RHIBuffer.h>
|
||||
#include <XCEngine/RHI/RHIEnums.h>
|
||||
#include <XCEngine/RHI/RHITypes.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
|
||||
#include <XCEngine/Rendering/FrameData/VisibleRenderItem.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
enum class BuiltinSkyboxTextureMode : Core::uint8 {
|
||||
None = 0,
|
||||
Panoramic = 1,
|
||||
Cubemap = 2
|
||||
};
|
||||
|
||||
struct BuiltinSkyboxMaterialData {
|
||||
Math::Vector4 tint = Math::Vector4::One();
|
||||
float exposure = 1.0f;
|
||||
float rotationDegrees = 0.0f;
|
||||
BuiltinSkyboxTextureMode textureMode = BuiltinSkyboxTextureMode::None;
|
||||
};
|
||||
|
||||
struct BuiltinDepthStyleMaterialConstants {
|
||||
Math::Vector4 baseColorFactor = Math::Vector4::One();
|
||||
Math::Vector4 alphaCutoffParams = Math::Vector4(0.5f, 0.0f, 0.0f, 0.0f);
|
||||
};
|
||||
|
||||
struct MaterialConstantLayoutView {
|
||||
const Resources::MaterialConstantFieldDesc* fields = nullptr;
|
||||
size_t count = 0;
|
||||
size_t size = 0;
|
||||
|
||||
bool IsValid() const {
|
||||
return fields != nullptr && count > 0 && size > 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct MaterialConstantPayloadView {
|
||||
const void* data = nullptr;
|
||||
size_t size = 0;
|
||||
MaterialConstantLayoutView layout = {};
|
||||
|
||||
bool IsValid() const {
|
||||
return data != nullptr && size > 0 && layout.IsValid() && layout.size == size;
|
||||
}
|
||||
};
|
||||
|
||||
struct MaterialBufferResourceView {
|
||||
RHI::RHIBuffer* buffer = nullptr;
|
||||
RHI::ResourceViewType viewType = RHI::ResourceViewType::ShaderResource;
|
||||
RHI::ResourceViewDesc viewDesc = {};
|
||||
|
||||
bool IsValid() const {
|
||||
return buffer != nullptr &&
|
||||
(viewType == RHI::ResourceViewType::ShaderResource ||
|
||||
viewType == RHI::ResourceViewType::UnorderedAccess) &&
|
||||
viewDesc.dimension != RHI::ResourceViewDimension::Unknown;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool IsMaterialBufferResourceType(Resources::ShaderResourceType type) {
|
||||
switch (type) {
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline const Resources::ShaderPropertyDesc* FindShaderPropertyBySemantic(
|
||||
const Resources::Material* material,
|
||||
const Containers::String& semantic) {
|
||||
if (material == nullptr || material->GetShader() == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Containers::String normalizedSemantic = NormalizeBuiltinPassMetadataValue(semantic);
|
||||
for (const Resources::ShaderPropertyDesc& property : material->GetShader()->GetProperties()) {
|
||||
if (NormalizeBuiltinPassMetadataValue(property.semantic) == normalizedSemantic) {
|
||||
return &property;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline Math::Vector4 ResolveBuiltinBaseColorFactor(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return Math::Vector4::One();
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColor")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Color ||
|
||||
property->type == Resources::ShaderPropertyType::Vector)) {
|
||||
return material->GetFloat4(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
return Math::Vector4::One();
|
||||
}
|
||||
|
||||
inline const Resources::Texture* ResolveBuiltinBaseColorTexture(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColorTexture")) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
|
||||
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline float ResolveBuiltinAlphaCutoff(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "AlphaCutoff")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Float ||
|
||||
property->type == Resources::ShaderPropertyType::Range)) {
|
||||
return material->GetFloat(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
inline bool IsCubemapSkyboxTextureType(Resources::TextureType type) {
|
||||
return type == Resources::TextureType::TextureCube ||
|
||||
type == Resources::TextureType::TextureCubeArray;
|
||||
}
|
||||
|
||||
inline bool IsPanoramicSkyboxTextureType(Resources::TextureType type) {
|
||||
return type == Resources::TextureType::Texture2D ||
|
||||
type == Resources::TextureType::Texture2DArray;
|
||||
}
|
||||
|
||||
inline const Resources::Texture* ResolveSkyboxPanoramicTexture(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxPanoramicTexture")) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
|
||||
if (textureHandle.Get() != nullptr &&
|
||||
textureHandle->IsValid() &&
|
||||
IsPanoramicSkyboxTextureType(textureHandle->GetTextureType())) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
|
||||
if (textureHandle.Get() != nullptr &&
|
||||
textureHandle->IsValid() &&
|
||||
IsPanoramicSkyboxTextureType(textureHandle->GetTextureType())) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline const Resources::Texture* ResolveSkyboxCubemapTexture(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
|
||||
if (textureHandle.Get() != nullptr &&
|
||||
textureHandle->IsValid() &&
|
||||
IsCubemapSkyboxTextureType(textureHandle->GetTextureType())) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline BuiltinSkyboxTextureMode ResolveSkyboxTextureMode(const Resources::Material* material) {
|
||||
if (ResolveSkyboxPanoramicTexture(material) != nullptr) {
|
||||
return BuiltinSkyboxTextureMode::Panoramic;
|
||||
}
|
||||
if (ResolveSkyboxCubemapTexture(material) != nullptr) {
|
||||
return BuiltinSkyboxTextureMode::Cubemap;
|
||||
}
|
||||
return BuiltinSkyboxTextureMode::None;
|
||||
}
|
||||
|
||||
inline Math::Vector4 ResolveSkyboxTint(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return Math::Vector4::One();
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Tint")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Color ||
|
||||
property->type == Resources::ShaderPropertyType::Vector)) {
|
||||
return material->GetFloat4(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
return Math::Vector4::One();
|
||||
}
|
||||
|
||||
inline float ResolveSkyboxExposure(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Exposure")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Float ||
|
||||
property->type == Resources::ShaderPropertyType::Range)) {
|
||||
return material->GetFloat(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
inline float ResolveSkyboxRotationDegrees(const Resources::Material* material) {
|
||||
if (material == nullptr) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Rotation")) {
|
||||
if (material->HasProperty(property->name) &&
|
||||
(property->type == Resources::ShaderPropertyType::Float ||
|
||||
property->type == Resources::ShaderPropertyType::Range)) {
|
||||
return material->GetFloat(property->name);
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
inline BuiltinSkyboxMaterialData BuildBuiltinSkyboxMaterialData(const Resources::Material* material) {
|
||||
BuiltinSkyboxMaterialData data = {};
|
||||
data.tint = ResolveSkyboxTint(material);
|
||||
data.exposure = ResolveSkyboxExposure(material);
|
||||
data.rotationDegrees = ResolveSkyboxRotationDegrees(material);
|
||||
data.textureMode = ResolveSkyboxTextureMode(material);
|
||||
return data;
|
||||
}
|
||||
|
||||
inline MaterialConstantPayloadView ResolveSchemaMaterialConstantPayload(const Resources::Material* material) {
|
||||
if (material == nullptr || material->GetShader() == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const Containers::Array<Resources::MaterialConstantFieldDesc>& constantLayout = material->GetConstantLayout();
|
||||
const Containers::Array<Core::uint8>& constantBufferData = material->GetConstantBufferData();
|
||||
if (constantLayout.Empty() || constantBufferData.Empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
MaterialConstantLayoutView layoutView = {};
|
||||
layoutView.fields = constantLayout.Data();
|
||||
layoutView.count = constantLayout.Size();
|
||||
layoutView.size = constantBufferData.Size();
|
||||
|
||||
return { constantBufferData.Data(), constantBufferData.Size(), layoutView };
|
||||
}
|
||||
|
||||
inline BuiltinDepthStyleMaterialConstants BuildBuiltinDepthStyleMaterialConstants(
|
||||
const Resources::Material* material) {
|
||||
BuiltinDepthStyleMaterialConstants constants = {};
|
||||
constants.baseColorFactor = ResolveBuiltinBaseColorFactor(material);
|
||||
constants.alphaCutoffParams = Math::Vector4(ResolveBuiltinAlphaCutoff(material), 0.0f, 0.0f, 0.0f);
|
||||
return constants;
|
||||
}
|
||||
|
||||
inline MaterialConstantPayloadView ResolveBuiltinDepthStyleMaterialConstantPayload(
|
||||
const Resources::Material* material,
|
||||
BuiltinDepthStyleMaterialConstants& outConstants,
|
||||
Resources::MaterialConstantFieldDesc (&outLayout)[2]) {
|
||||
outConstants = BuildBuiltinDepthStyleMaterialConstants(material);
|
||||
|
||||
outLayout[0].name = "gBaseColorFactor";
|
||||
outLayout[0].type = Resources::MaterialPropertyType::Float4;
|
||||
outLayout[0].offset = 0u;
|
||||
outLayout[0].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
|
||||
outLayout[0].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
|
||||
|
||||
outLayout[1].name = "gAlphaCutoffParams";
|
||||
outLayout[1].type = Resources::MaterialPropertyType::Float4;
|
||||
outLayout[1].offset = static_cast<Core::uint32>(sizeof(Math::Vector4));
|
||||
outLayout[1].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
|
||||
outLayout[1].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
|
||||
|
||||
MaterialConstantLayoutView layoutView = {};
|
||||
layoutView.fields = outLayout;
|
||||
layoutView.count = 2u;
|
||||
layoutView.size = sizeof(BuiltinDepthStyleMaterialConstants);
|
||||
|
||||
return { &outConstants, sizeof(BuiltinDepthStyleMaterialConstants), layoutView };
|
||||
}
|
||||
|
||||
inline bool TryResolveMaterialBufferResourceView(
|
||||
const Resources::Material* material,
|
||||
const BuiltinPassResourceBindingDesc& binding,
|
||||
MaterialBufferResourceView& outView) {
|
||||
outView = {};
|
||||
if (material == nullptr || !IsMaterialBufferResourceType(binding.resourceType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::MaterialBufferBinding* materialBinding = material->FindBufferBinding(binding.name);
|
||||
if (materialBinding == nullptr || materialBinding->buffer == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outView.buffer = materialBinding->buffer;
|
||||
outView.viewDesc.firstElement = materialBinding->viewDesc.firstElement;
|
||||
outView.viewDesc.elementCount = materialBinding->viewDesc.elementCount;
|
||||
|
||||
switch (binding.resourceType) {
|
||||
case Resources::ShaderResourceType::StructuredBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::ShaderResource;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
|
||||
outView.viewDesc.structureByteStride =
|
||||
materialBinding->viewDesc.structureByteStride > 0
|
||||
? materialBinding->viewDesc.structureByteStride
|
||||
: materialBinding->buffer->GetStride();
|
||||
break;
|
||||
case Resources::ShaderResourceType::RawBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::ShaderResource;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
|
||||
break;
|
||||
case Resources::ShaderResourceType::RWStructuredBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
|
||||
outView.viewDesc.structureByteStride =
|
||||
materialBinding->viewDesc.structureByteStride > 0
|
||||
? materialBinding->viewDesc.structureByteStride
|
||||
: materialBinding->buffer->GetStride();
|
||||
break;
|
||||
case Resources::ShaderResourceType::RWRawBuffer:
|
||||
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
|
||||
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outView.viewDesc.dimension == RHI::ResourceViewDimension::StructuredBuffer &&
|
||||
outView.viewDesc.structureByteStride == 0) {
|
||||
outView = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
return outView.IsValid();
|
||||
}
|
||||
|
||||
inline const Resources::Material* ResolveMaterial(
|
||||
const Components::MeshRendererComponent* meshRenderer,
|
||||
const Resources::Mesh* mesh,
|
||||
Core::uint32 materialIndex) {
|
||||
if (meshRenderer != nullptr && materialIndex < meshRenderer->GetMaterialCount()) {
|
||||
if (const Resources::Material* material = meshRenderer->GetMaterial(materialIndex)) {
|
||||
return material;
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh != nullptr && materialIndex < mesh->GetMaterials().Size()) {
|
||||
if (const Resources::Material* material = mesh->GetMaterials()[materialIndex]) {
|
||||
return material;
|
||||
}
|
||||
}
|
||||
|
||||
if (meshRenderer != nullptr && meshRenderer->GetMaterialCount() > 0) {
|
||||
if (const Resources::Material* material = meshRenderer->GetMaterial(0)) {
|
||||
return material;
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh != nullptr && mesh->GetMaterials().Size() > 0) {
|
||||
return mesh->GetMaterials()[0];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline const Resources::Material* ResolveMaterial(const VisibleRenderItem& visibleItem) {
|
||||
if (visibleItem.material != nullptr) {
|
||||
return visibleItem.material;
|
||||
}
|
||||
|
||||
return ResolveMaterial(visibleItem.meshRenderer, visibleItem.mesh, visibleItem.materialIndex);
|
||||
}
|
||||
|
||||
inline bool TryResolveRenderQueueTagValue(
|
||||
const Containers::String& queueValue,
|
||||
Core::int32& outRenderQueue) {
|
||||
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(queueValue);
|
||||
if (normalized.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (normalized == Containers::String("background")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Background);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("geometry")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("alphatest")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::AlphaTest);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("transparent")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
|
||||
return true;
|
||||
}
|
||||
if (normalized == Containers::String("overlay")) {
|
||||
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Overlay);
|
||||
return true;
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
const long parsedValue = std::strtol(normalized.CStr(), &end, 10);
|
||||
if (end != nullptr && *end == '\0') {
|
||||
outRenderQueue = static_cast<Core::int32>(parsedValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool TryResolveShaderPassRenderQueue(const Resources::ShaderPass& shaderPass, Core::int32& outRenderQueue) {
|
||||
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
|
||||
if (NormalizeBuiltinPassMetadataValue(tag.name) == Containers::String("queue") &&
|
||||
TryResolveRenderQueueTagValue(tag.value, outRenderQueue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline Core::int32 ResolveMaterialRenderQueue(const Resources::Material* material) {
|
||||
const Core::int32 defaultQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
|
||||
if (material == nullptr) {
|
||||
return defaultQueue;
|
||||
}
|
||||
|
||||
const Core::int32 materialQueue = material->GetRenderQueue();
|
||||
if (materialQueue != defaultQueue) {
|
||||
return materialQueue;
|
||||
}
|
||||
|
||||
if (const Resources::Shader* shader = material->GetShader()) {
|
||||
for (const Resources::ShaderPass& pass : shader->GetPasses()) {
|
||||
Core::int32 shaderQueue = defaultQueue;
|
||||
if (TryResolveShaderPassRenderQueue(pass, shaderQueue)) {
|
||||
return shaderQueue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return materialQueue;
|
||||
}
|
||||
|
||||
inline bool IsTransparentRenderQueue(Core::int32 renderQueue) {
|
||||
return renderQueue >= static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
|
||||
}
|
||||
|
||||
inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) {
|
||||
if (material == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Resources::Shader* shader = material->GetShader();
|
||||
if (shader == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const Resources::ShaderPass& shaderPassEntry : shader->GetPasses()) {
|
||||
if (ShaderPassMatchesBuiltinPass(shaderPassEntry, pass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
311
engine/Runtime/Rendering/Materials/RenderMaterialStateUtils.h
Normal file
311
engine/Runtime/Rendering/Materials/RenderMaterialStateUtils.h
Normal file
@@ -0,0 +1,311 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <XCEngine/Rendering/Execution/RenderStateBlock.h>
|
||||
#include <XCEngine/RHI/RHICommandList.h>
|
||||
#include <XCEngine/RHI/RHITypes.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
inline RHI::CullMode ToRHICullMode(Resources::MaterialCullMode mode) {
|
||||
switch (mode) {
|
||||
case Resources::MaterialCullMode::Front:
|
||||
return RHI::CullMode::Front;
|
||||
case Resources::MaterialCullMode::Back:
|
||||
return RHI::CullMode::Back;
|
||||
case Resources::MaterialCullMode::None:
|
||||
default:
|
||||
return RHI::CullMode::None;
|
||||
}
|
||||
}
|
||||
|
||||
inline RHI::ComparisonFunc ToRHIComparisonFunc(Resources::MaterialComparisonFunc func) {
|
||||
switch (func) {
|
||||
case Resources::MaterialComparisonFunc::Never:
|
||||
return RHI::ComparisonFunc::Never;
|
||||
case Resources::MaterialComparisonFunc::Equal:
|
||||
return RHI::ComparisonFunc::Equal;
|
||||
case Resources::MaterialComparisonFunc::LessEqual:
|
||||
return RHI::ComparisonFunc::LessEqual;
|
||||
case Resources::MaterialComparisonFunc::Greater:
|
||||
return RHI::ComparisonFunc::Greater;
|
||||
case Resources::MaterialComparisonFunc::NotEqual:
|
||||
return RHI::ComparisonFunc::NotEqual;
|
||||
case Resources::MaterialComparisonFunc::GreaterEqual:
|
||||
return RHI::ComparisonFunc::GreaterEqual;
|
||||
case Resources::MaterialComparisonFunc::Always:
|
||||
return RHI::ComparisonFunc::Always;
|
||||
case Resources::MaterialComparisonFunc::Less:
|
||||
default:
|
||||
return RHI::ComparisonFunc::Less;
|
||||
}
|
||||
}
|
||||
|
||||
inline RHI::BlendFactor ToRHIBlendFactor(Resources::MaterialBlendFactor factor) {
|
||||
switch (factor) {
|
||||
case Resources::MaterialBlendFactor::Zero:
|
||||
return RHI::BlendFactor::Zero;
|
||||
case Resources::MaterialBlendFactor::SrcColor:
|
||||
return RHI::BlendFactor::SrcColor;
|
||||
case Resources::MaterialBlendFactor::InvSrcColor:
|
||||
return RHI::BlendFactor::InvSrcColor;
|
||||
case Resources::MaterialBlendFactor::SrcAlpha:
|
||||
return RHI::BlendFactor::SrcAlpha;
|
||||
case Resources::MaterialBlendFactor::InvSrcAlpha:
|
||||
return RHI::BlendFactor::InvSrcAlpha;
|
||||
case Resources::MaterialBlendFactor::DstAlpha:
|
||||
return RHI::BlendFactor::DstAlpha;
|
||||
case Resources::MaterialBlendFactor::InvDstAlpha:
|
||||
return RHI::BlendFactor::InvDstAlpha;
|
||||
case Resources::MaterialBlendFactor::DstColor:
|
||||
return RHI::BlendFactor::DstColor;
|
||||
case Resources::MaterialBlendFactor::InvDstColor:
|
||||
return RHI::BlendFactor::InvDstColor;
|
||||
case Resources::MaterialBlendFactor::SrcAlphaSat:
|
||||
return RHI::BlendFactor::SrcAlphaSat;
|
||||
case Resources::MaterialBlendFactor::BlendFactor:
|
||||
return RHI::BlendFactor::BlendFactor;
|
||||
case Resources::MaterialBlendFactor::InvBlendFactor:
|
||||
return RHI::BlendFactor::InvBlendFactor;
|
||||
case Resources::MaterialBlendFactor::Src1Color:
|
||||
return RHI::BlendFactor::Src1Color;
|
||||
case Resources::MaterialBlendFactor::InvSrc1Color:
|
||||
return RHI::BlendFactor::InvSrc1Color;
|
||||
case Resources::MaterialBlendFactor::Src1Alpha:
|
||||
return RHI::BlendFactor::Src1Alpha;
|
||||
case Resources::MaterialBlendFactor::InvSrc1Alpha:
|
||||
return RHI::BlendFactor::InvSrc1Alpha;
|
||||
case Resources::MaterialBlendFactor::One:
|
||||
default:
|
||||
return RHI::BlendFactor::One;
|
||||
}
|
||||
}
|
||||
|
||||
inline RHI::BlendOp ToRHIBlendOp(Resources::MaterialBlendOp op) {
|
||||
switch (op) {
|
||||
case Resources::MaterialBlendOp::Subtract:
|
||||
return RHI::BlendOp::Subtract;
|
||||
case Resources::MaterialBlendOp::ReverseSubtract:
|
||||
return RHI::BlendOp::ReverseSubtract;
|
||||
case Resources::MaterialBlendOp::Min:
|
||||
return RHI::BlendOp::Min;
|
||||
case Resources::MaterialBlendOp::Max:
|
||||
return RHI::BlendOp::Max;
|
||||
case Resources::MaterialBlendOp::Add:
|
||||
default:
|
||||
return RHI::BlendOp::Add;
|
||||
}
|
||||
}
|
||||
|
||||
inline RHI::StencilOp ToRHIStencilOp(Resources::MaterialStencilOp op) {
|
||||
switch (op) {
|
||||
case Resources::MaterialStencilOp::Zero:
|
||||
return RHI::StencilOp::Zero;
|
||||
case Resources::MaterialStencilOp::Replace:
|
||||
return RHI::StencilOp::Replace;
|
||||
case Resources::MaterialStencilOp::IncrSat:
|
||||
return RHI::StencilOp::IncrSat;
|
||||
case Resources::MaterialStencilOp::DecrSat:
|
||||
return RHI::StencilOp::DecrSat;
|
||||
case Resources::MaterialStencilOp::Invert:
|
||||
return RHI::StencilOp::Invert;
|
||||
case Resources::MaterialStencilOp::IncrWrap:
|
||||
return RHI::StencilOp::Incr;
|
||||
case Resources::MaterialStencilOp::DecrWrap:
|
||||
return RHI::StencilOp::Decr;
|
||||
case Resources::MaterialStencilOp::Keep:
|
||||
default:
|
||||
return RHI::StencilOp::Keep;
|
||||
}
|
||||
}
|
||||
|
||||
inline Resources::MaterialRenderState ResolveEffectiveRenderState(
|
||||
const Resources::ShaderPass* shaderPass,
|
||||
const Resources::Material* material) {
|
||||
Resources::MaterialRenderState renderState = {};
|
||||
|
||||
if (shaderPass != nullptr && shaderPass->hasFixedFunctionState) {
|
||||
renderState = shaderPass->fixedFunctionState;
|
||||
} else if (material != nullptr) {
|
||||
renderState = material->GetRenderState();
|
||||
}
|
||||
|
||||
if (material != nullptr && material->HasRenderStateOverride()) {
|
||||
renderState = material->GetRenderState();
|
||||
}
|
||||
|
||||
return renderState;
|
||||
}
|
||||
|
||||
inline Resources::MaterialRenderState ApplyRenderStateBlock(
|
||||
Resources::MaterialRenderState renderState,
|
||||
const RenderStateBlock* renderStateBlock) {
|
||||
if (renderStateBlock == nullptr ||
|
||||
!renderStateBlock->HasOverrides()) {
|
||||
return renderState;
|
||||
}
|
||||
|
||||
if (renderStateBlock->HasDepthOverride()) {
|
||||
renderState.depthWriteEnable =
|
||||
renderStateBlock->depthState.writeEnabled;
|
||||
renderState.depthFunc =
|
||||
renderStateBlock->depthState.compareFunction;
|
||||
}
|
||||
|
||||
if (renderStateBlock->HasStencilOverride()) {
|
||||
renderState.stencil.enabled =
|
||||
renderStateBlock->stencilState.enabled;
|
||||
renderState.stencil.readMask =
|
||||
renderStateBlock->stencilState.readMask;
|
||||
renderState.stencil.writeMask =
|
||||
renderStateBlock->stencilState.writeMask;
|
||||
renderState.stencil.reference =
|
||||
renderStateBlock->stencilReference;
|
||||
renderState.stencil.front.failOp =
|
||||
renderStateBlock->stencilState.frontFace.failOp;
|
||||
renderState.stencil.front.passOp =
|
||||
renderStateBlock->stencilState.frontFace.passOp;
|
||||
renderState.stencil.front.depthFailOp =
|
||||
renderStateBlock->stencilState.frontFace.depthFailOp;
|
||||
renderState.stencil.front.func =
|
||||
renderStateBlock->stencilState.frontFace.compareFunction;
|
||||
renderState.stencil.back.failOp =
|
||||
renderStateBlock->stencilState.backFace.failOp;
|
||||
renderState.stencil.back.passOp =
|
||||
renderStateBlock->stencilState.backFace.passOp;
|
||||
renderState.stencil.back.depthFailOp =
|
||||
renderStateBlock->stencilState.backFace.depthFailOp;
|
||||
renderState.stencil.back.func =
|
||||
renderStateBlock->stencilState.backFace.compareFunction;
|
||||
}
|
||||
|
||||
return renderState;
|
||||
}
|
||||
|
||||
inline RHI::RasterizerDesc BuildRasterizerState(const Resources::MaterialRenderState& renderState) {
|
||||
RHI::RasterizerDesc desc = {};
|
||||
desc.fillMode = static_cast<uint32_t>(RHI::FillMode::Solid);
|
||||
desc.cullMode = static_cast<uint32_t>(RHI::CullMode::None);
|
||||
desc.frontFace = static_cast<uint32_t>(RHI::FrontFace::CounterClockwise);
|
||||
desc.depthClipEnable = true;
|
||||
desc.cullMode = static_cast<uint32_t>(ToRHICullMode(renderState.cullMode));
|
||||
desc.depthBias = renderState.depthBiasUnits;
|
||||
desc.slopeScaledDepthBias = renderState.depthBiasFactor;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline RHI::BlendDesc BuildBlendState(const Resources::MaterialRenderState& renderState) {
|
||||
RHI::BlendDesc desc = {};
|
||||
desc.blendEnable = renderState.blendEnable;
|
||||
desc.srcBlend = static_cast<uint32_t>(ToRHIBlendFactor(renderState.srcBlend));
|
||||
desc.dstBlend = static_cast<uint32_t>(ToRHIBlendFactor(renderState.dstBlend));
|
||||
desc.srcBlendAlpha = static_cast<uint32_t>(ToRHIBlendFactor(renderState.srcBlendAlpha));
|
||||
desc.dstBlendAlpha = static_cast<uint32_t>(ToRHIBlendFactor(renderState.dstBlendAlpha));
|
||||
desc.blendOp = static_cast<uint32_t>(ToRHIBlendOp(renderState.blendOp));
|
||||
desc.blendOpAlpha = static_cast<uint32_t>(ToRHIBlendOp(renderState.blendOpAlpha));
|
||||
desc.colorWriteMask = renderState.colorWriteMask;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline RHI::DepthStencilStateDesc BuildDepthStencilState(const Resources::MaterialRenderState& renderState) {
|
||||
RHI::DepthStencilStateDesc desc = {};
|
||||
desc.depthTestEnable = renderState.depthTestEnable;
|
||||
desc.depthWriteEnable = renderState.depthWriteEnable;
|
||||
desc.depthFunc = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.depthFunc));
|
||||
desc.stencilEnable = renderState.stencil.enabled;
|
||||
desc.stencilReadMask = renderState.stencil.readMask;
|
||||
desc.stencilWriteMask = renderState.stencil.writeMask;
|
||||
desc.front.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.failOp));
|
||||
desc.front.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.passOp));
|
||||
desc.front.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.front.depthFailOp));
|
||||
desc.front.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.front.func));
|
||||
desc.back.failOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.failOp));
|
||||
desc.back.passOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.passOp));
|
||||
desc.back.depthFailOp = static_cast<uint32_t>(ToRHIStencilOp(renderState.stencil.back.depthFailOp));
|
||||
desc.back.func = static_cast<uint32_t>(ToRHIComparisonFunc(renderState.stencil.back.func));
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
inline void ApplyRenderState(const Resources::MaterialRenderState& renderState, RHI::GraphicsPipelineDesc& pipelineDesc) {
|
||||
pipelineDesc.rasterizerState = BuildRasterizerState(renderState);
|
||||
pipelineDesc.blendState = BuildBlendState(renderState);
|
||||
pipelineDesc.depthStencilState = BuildDepthStencilState(renderState);
|
||||
}
|
||||
|
||||
inline void ApplyMaterialRenderState(const Resources::Material* material, RHI::GraphicsPipelineDesc& pipelineDesc) {
|
||||
ApplyRenderState(ResolveEffectiveRenderState(nullptr, material), pipelineDesc);
|
||||
}
|
||||
|
||||
inline void ApplyResolvedRenderState(
|
||||
const Resources::ShaderPass* shaderPass,
|
||||
const Resources::Material* material,
|
||||
RHI::GraphicsPipelineDesc& pipelineDesc) {
|
||||
ApplyRenderState(ResolveEffectiveRenderState(shaderPass, material), pipelineDesc);
|
||||
}
|
||||
|
||||
inline void ApplyDynamicRenderState(
|
||||
const Resources::MaterialRenderState& renderState,
|
||||
RHI::RHICommandList& commandList) {
|
||||
if (renderState.stencil.enabled) {
|
||||
commandList.SetStencilRef(renderState.stencil.reference);
|
||||
}
|
||||
}
|
||||
|
||||
inline Resources::MaterialRenderState BuildStaticPipelineRenderStateKey(
|
||||
const Resources::MaterialRenderState& renderState) {
|
||||
Resources::MaterialRenderState keyState = renderState;
|
||||
keyState.stencil.reference = 0;
|
||||
return keyState;
|
||||
}
|
||||
|
||||
struct MaterialRenderStateHash {
|
||||
size_t operator()(const Resources::MaterialRenderState& state) const noexcept {
|
||||
size_t hash = 2166136261u;
|
||||
auto combine = [&hash](size_t value) {
|
||||
hash ^= value + 0x9e3779b9u + (hash << 6) + (hash >> 2);
|
||||
};
|
||||
auto combineFloat = [&combine](float value) {
|
||||
Core::uint32 bits = 0;
|
||||
std::memcpy(&bits, &value, sizeof(bits));
|
||||
combine(static_cast<size_t>(bits));
|
||||
};
|
||||
|
||||
combine(static_cast<size_t>(state.blendEnable));
|
||||
combine(static_cast<size_t>(state.srcBlend));
|
||||
combine(static_cast<size_t>(state.dstBlend));
|
||||
combine(static_cast<size_t>(state.srcBlendAlpha));
|
||||
combine(static_cast<size_t>(state.dstBlendAlpha));
|
||||
combine(static_cast<size_t>(state.blendOp));
|
||||
combine(static_cast<size_t>(state.blendOpAlpha));
|
||||
combine(static_cast<size_t>(state.colorWriteMask));
|
||||
combine(static_cast<size_t>(state.depthTestEnable));
|
||||
combine(static_cast<size_t>(state.depthWriteEnable));
|
||||
combine(static_cast<size_t>(state.depthFunc));
|
||||
combine(static_cast<size_t>(state.cullMode));
|
||||
combineFloat(state.depthBiasFactor);
|
||||
combine(static_cast<size_t>(state.depthBiasUnits));
|
||||
combine(static_cast<size_t>(state.stencil.enabled));
|
||||
combine(static_cast<size_t>(state.stencil.readMask));
|
||||
combine(static_cast<size_t>(state.stencil.writeMask));
|
||||
combine(static_cast<size_t>(state.stencil.reference));
|
||||
combine(static_cast<size_t>(state.stencil.front.failOp));
|
||||
combine(static_cast<size_t>(state.stencil.front.passOp));
|
||||
combine(static_cast<size_t>(state.stencil.front.depthFailOp));
|
||||
combine(static_cast<size_t>(state.stencil.front.func));
|
||||
combine(static_cast<size_t>(state.stencil.back.failOp));
|
||||
combine(static_cast<size_t>(state.stencil.back.passOp));
|
||||
combine(static_cast<size_t>(state.stencil.back.depthFailOp));
|
||||
combine(static_cast<size_t>(state.stencil.back.func));
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
56
engine/Runtime/Rendering/Picking/ObjectIdCodec.h
Normal file
56
engine/Runtime/Rendering/Picking/ObjectIdCodec.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Vector4.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
using RenderObjectId = Core::uint32;
|
||||
using EncodedObjectId = RenderObjectId;
|
||||
|
||||
static constexpr RenderObjectId kInvalidRenderObjectId = 0u;
|
||||
|
||||
inline bool IsValidRenderObjectId(RenderObjectId renderObjectId) {
|
||||
return renderObjectId != kInvalidRenderObjectId;
|
||||
}
|
||||
|
||||
inline EncodedObjectId EncodeRenderObjectIdToEncodedId(RenderObjectId renderObjectId) {
|
||||
return renderObjectId;
|
||||
}
|
||||
|
||||
inline EncodedObjectId EncodeRenderObjectIdToUInt32(RenderObjectId renderObjectId) {
|
||||
return EncodeRenderObjectIdToEncodedId(renderObjectId);
|
||||
}
|
||||
|
||||
inline Math::Vector4 EncodeRenderObjectIdToColor(RenderObjectId renderObjectId) {
|
||||
const EncodedObjectId encodedId = EncodeRenderObjectIdToEncodedId(renderObjectId);
|
||||
constexpr float kInv255 = 1.0f / 255.0f;
|
||||
return Math::Vector4(
|
||||
static_cast<float>((encodedId >> 0) & 0xFFu) * kInv255,
|
||||
static_cast<float>((encodedId >> 8) & 0xFFu) * kInv255,
|
||||
static_cast<float>((encodedId >> 16) & 0xFFu) * kInv255,
|
||||
static_cast<float>((encodedId >> 24) & 0xFFu) * kInv255);
|
||||
}
|
||||
|
||||
inline EncodedObjectId DecodeEncodedObjectIdFromColor(
|
||||
Core::uint8 r,
|
||||
Core::uint8 g,
|
||||
Core::uint8 b,
|
||||
Core::uint8 a) {
|
||||
return static_cast<EncodedObjectId>(r) |
|
||||
(static_cast<EncodedObjectId>(g) << 8u) |
|
||||
(static_cast<EncodedObjectId>(b) << 16u) |
|
||||
(static_cast<EncodedObjectId>(a) << 24u);
|
||||
}
|
||||
|
||||
inline RenderObjectId DecodeRenderObjectIdFromColor(
|
||||
Core::uint8 r,
|
||||
Core::uint8 g,
|
||||
Core::uint8 b,
|
||||
Core::uint8 a) {
|
||||
return DecodeEncodedObjectIdFromColor(r, g, b, a);
|
||||
}
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEngine/Rendering/Picking/RenderObjectIdRegistry.h>
|
||||
#include "engine/Runtime/Rendering/Picking/RenderObjectIdRegistry.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
48
engine/Runtime/Rendering/Picking/RenderObjectIdRegistry.h
Normal file
48
engine/Runtime/Rendering/Picking/RenderObjectIdRegistry.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
#include "engine/Runtime/Rendering/Picking/ObjectIdCodec.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
class RenderObjectIdRegistry {
|
||||
public:
|
||||
static RenderObjectIdRegistry& Get();
|
||||
|
||||
RenderObjectIdRegistry(const RenderObjectIdRegistry&) = delete;
|
||||
RenderObjectIdRegistry& operator=(const RenderObjectIdRegistry&) = delete;
|
||||
|
||||
RenderObjectId GetOrAllocateRenderObjectId(Core::uint64 runtimeObjectId);
|
||||
bool TryGetRenderObjectId(
|
||||
Core::uint64 runtimeObjectId,
|
||||
RenderObjectId& outRenderObjectId) const;
|
||||
bool TryResolveRuntimeObjectId(
|
||||
RenderObjectId renderObjectId,
|
||||
Core::uint64& outRuntimeObjectId) const;
|
||||
Core::uint64 ResolveRuntimeObjectId(RenderObjectId renderObjectId) const;
|
||||
|
||||
void Reset();
|
||||
Core::uint64 GetGeneration() const;
|
||||
|
||||
private:
|
||||
RenderObjectIdRegistry() = default;
|
||||
|
||||
RenderObjectId AllocateRenderObjectIdLocked();
|
||||
void BumpGenerationLocked();
|
||||
|
||||
mutable std::mutex m_mutex = {};
|
||||
RenderObjectId m_nextRenderObjectId = 1u;
|
||||
Core::uint64 m_generation = 1u;
|
||||
std::unordered_map<Core::uint64, RenderObjectId>
|
||||
m_renderObjectIdByRuntimeObjectId = {};
|
||||
std::unordered_map<RenderObjectId, Core::uint64>
|
||||
m_runtimeObjectIdByRenderObjectId = {};
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Rendering/Shadow/DirectionalShadowData.h"
|
||||
#include "DirectionalShadowData.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
82
engine/Runtime/Rendering/Shadow/DirectionalShadowData.h
Normal file
82
engine/Runtime/Rendering/Shadow/DirectionalShadowData.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Math/Matrix4.h>
|
||||
#include <XCEngine/Core/Math/Vector2.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Rendering/FrameData/RenderCameraData.h>
|
||||
#include <XCEngine/Rendering/Shadow/DirectionalShadowSettings.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
class RHIResourceView;
|
||||
} // namespace RHI
|
||||
|
||||
namespace Rendering {
|
||||
|
||||
struct DirectionalShadowRenderPlan {
|
||||
bool enabled = false;
|
||||
Math::Vector3 lightDirection = Math::Vector3::Back();
|
||||
Math::Vector3 focusPoint = Math::Vector3::Zero();
|
||||
float orthographicHalfExtent = 0.0f;
|
||||
float texelWorldSize = 0.0f;
|
||||
float nearClipPlane = 0.1f;
|
||||
float farClipPlane = 0.0f;
|
||||
DirectionalShadowSamplingSettings sampling = {};
|
||||
DirectionalShadowCasterBiasSettings casterBias = {};
|
||||
uint32_t mapWidth = 0;
|
||||
uint32_t mapHeight = 0;
|
||||
RenderCameraData cameraData = {};
|
||||
|
||||
bool IsValid() const {
|
||||
return enabled &&
|
||||
mapWidth > 0 &&
|
||||
mapHeight > 0 &&
|
||||
cameraData.viewportWidth == mapWidth &&
|
||||
cameraData.viewportHeight == mapHeight;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderDirectionalShadowMapMetrics {
|
||||
Math::Vector2 inverseMapSize = Math::Vector2::Zero();
|
||||
float worldTexelSize = 0.0f;
|
||||
float padding = 0.0f;
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(RenderDirectionalShadowMapMetrics) == sizeof(float) * 4u,
|
||||
"RenderDirectionalShadowMapMetrics must stay float4-sized for GPU constant layout");
|
||||
|
||||
struct RenderDirectionalShadowSamplingData {
|
||||
float enabled = 0.0f;
|
||||
DirectionalShadowSamplingSettings settings = {};
|
||||
};
|
||||
|
||||
static_assert(
|
||||
sizeof(RenderDirectionalShadowSamplingData) == sizeof(float) * 4u,
|
||||
"RenderDirectionalShadowSamplingData must stay float4-sized for GPU constant layout");
|
||||
|
||||
struct RenderDirectionalShadowCasterBiasData {
|
||||
DirectionalShadowCasterBiasSettings settings = {};
|
||||
};
|
||||
|
||||
struct RenderDirectionalShadowData {
|
||||
bool enabled = false;
|
||||
Math::Matrix4x4 viewProjection = Math::Matrix4x4::Identity();
|
||||
RenderDirectionalShadowMapMetrics mapMetrics = {};
|
||||
RenderDirectionalShadowSamplingData sampling = {};
|
||||
RenderDirectionalShadowCasterBiasData casterBias = {};
|
||||
RHI::RHIResourceView* shadowMap = nullptr;
|
||||
|
||||
bool IsValid() const {
|
||||
return enabled && shadowMap != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
RenderDirectionalShadowData BuildRenderDirectionalShadowData(
|
||||
const DirectionalShadowRenderPlan& plan,
|
||||
RHI::RHIResourceView* shadowMapView);
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Rendering/Shadow/DirectionalShadowRuntime.h"
|
||||
#include "DirectionalShadowRuntime.h"
|
||||
|
||||
#include "Rendering/Execution/CameraFramePlan.h"
|
||||
|
||||
#include <XCEngine/Rendering/RenderPipeline.h>
|
||||
|
||||
namespace XCEngine {
|
||||
30
engine/Runtime/Rendering/Shadow/DirectionalShadowRuntime.h
Normal file
30
engine/Runtime/Rendering/Shadow/DirectionalShadowRuntime.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Rendering/Execution/DirectionalShadowExecutionState.h>
|
||||
|
||||
#include "engine/Runtime/Rendering/Caches/DirectionalShadowSurfaceCache.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Rendering {
|
||||
|
||||
struct CameraFramePlan;
|
||||
class RenderPipeline;
|
||||
|
||||
class DirectionalShadowRuntime {
|
||||
public:
|
||||
DirectionalShadowRuntime() = default;
|
||||
DirectionalShadowRuntime(const DirectionalShadowRuntime&) = delete;
|
||||
DirectionalShadowRuntime& operator=(const DirectionalShadowRuntime&) = delete;
|
||||
~DirectionalShadowRuntime() = default;
|
||||
|
||||
bool ResolveExecutionState(
|
||||
const CameraFramePlan& plan,
|
||||
const RenderPipeline& pipeline,
|
||||
DirectionalShadowExecutionState& outShadowState);
|
||||
|
||||
private:
|
||||
DirectionalShadowSurfaceCache m_surfaceCache;
|
||||
};
|
||||
|
||||
} // namespace Rendering
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEngine/Resources/AudioClip/AudioClip.h>
|
||||
#include "AudioClip.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
105
engine/Runtime/Resources/AudioClip/AudioClip.h
Normal file
105
engine/Runtime/Resources/AudioClip/AudioClip.h
Normal file
@@ -0,0 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class AudioFormat : Core::uint8 {
|
||||
Unknown,
|
||||
WAV,
|
||||
OGG,
|
||||
MP3,
|
||||
FLAC
|
||||
};
|
||||
|
||||
enum class AudioType : Core::uint8 {
|
||||
SoundEffect,
|
||||
Music,
|
||||
Voice,
|
||||
Ambient
|
||||
};
|
||||
|
||||
class AudioClip : public IResource {
|
||||
public:
|
||||
AudioClip();
|
||||
virtual ~AudioClip() override;
|
||||
|
||||
ResourceType GetType() const override { return ResourceType::AudioClip; }
|
||||
const Containers::String& GetName() const override { return m_name; }
|
||||
const Containers::String& GetPath() const override { return m_path; }
|
||||
ResourceGUID GetGUID() const override { return m_guid; }
|
||||
bool IsValid() const override { return m_isValid; }
|
||||
size_t GetMemorySize() const override { return m_memorySize; }
|
||||
void Release() override;
|
||||
|
||||
void SetPCMData(const Containers::Array<Core::uint8>& data);
|
||||
const Containers::Array<Core::uint8>& GetPCMData() const { return m_pcmData; }
|
||||
size_t GetPCMDataSize() const { return m_pcmData.Size(); }
|
||||
|
||||
// Legacy compatibility: audio data now means decoded/interpretable PCM bytes.
|
||||
void SetAudioData(const Containers::Array<Core::uint8>& data) { SetPCMData(data); }
|
||||
const Containers::Array<Core::uint8>& GetAudioData() const { return GetPCMData(); }
|
||||
|
||||
void SetSampleRate(Core::uint32 rate);
|
||||
Core::uint32 GetSampleRate() const { return m_sampleRate; }
|
||||
|
||||
void SetChannels(Core::uint32 channels);
|
||||
Core::uint32 GetChannels() const { return m_channels; }
|
||||
|
||||
void SetBitsPerSample(Core::uint32 bits);
|
||||
Core::uint32 GetBitsPerSample() const { return m_bitsPerSample; }
|
||||
|
||||
void SetDuration(float seconds) { m_duration = seconds; }
|
||||
float GetDuration() const { return m_duration; }
|
||||
|
||||
const std::vector<float>& GetDecodedPCMData() const;
|
||||
bool HasDecodedPCMData() const { return m_decodedPCMValid; }
|
||||
|
||||
Core::uint64 GetFrameCount() const;
|
||||
Core::uint64 GetSampleCount() const;
|
||||
|
||||
void SetAudioFormat(AudioFormat format) { m_format = format; }
|
||||
AudioFormat GetAudioFormat() const { return m_format; }
|
||||
|
||||
void SetAudioType(AudioType type) { m_audioType = type; }
|
||||
AudioType GetAudioType() const { return m_audioType; }
|
||||
|
||||
void SetIs3D(bool is3D) { m_is3D = is3D; }
|
||||
bool Is3D() const { return m_is3D; }
|
||||
|
||||
void SetLoop(bool loop) { m_loop = loop; }
|
||||
bool IsLoop() const { return m_loop; }
|
||||
|
||||
class IRHIAudioBuffer* GetRHIResource() const { return m_rhiResource; }
|
||||
void SetRHIResource(class IRHIAudioBuffer* resource);
|
||||
|
||||
private:
|
||||
void RefreshDerivedData();
|
||||
void RefreshMemorySize();
|
||||
void InvalidateDecodedPCMData();
|
||||
void BuildDecodedPCMData() const;
|
||||
|
||||
Containers::Array<Core::uint8> m_pcmData;
|
||||
mutable std::vector<float> m_decodedPCMData;
|
||||
mutable bool m_decodedPCMValid = false;
|
||||
|
||||
Core::uint32 m_sampleRate = 44100;
|
||||
Core::uint32 m_channels = 2;
|
||||
Core::uint32 m_bitsPerSample = 16;
|
||||
float m_duration = 0.0f;
|
||||
|
||||
AudioFormat m_format = AudioFormat::WAV;
|
||||
AudioType m_audioType = AudioType::SoundEffect;
|
||||
|
||||
bool m_is3D = false;
|
||||
bool m_loop = false;
|
||||
|
||||
class IRHIAudioBuffer* m_rhiResource = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <XCEngine/Resources/AudioClip/AudioLoader.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include "AudioLoader.h"
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace XCEngine {
|
||||
26
engine/Runtime/Resources/AudioClip/AudioLoader.h
Normal file
26
engine/Runtime/Resources/AudioClip/AudioLoader.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
#include "AudioClip.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class AudioLoader : public IResourceLoader {
|
||||
public:
|
||||
AudioLoader();
|
||||
virtual ~AudioLoader() override;
|
||||
|
||||
ResourceType GetResourceType() const override { return ResourceType::AudioClip; }
|
||||
Containers::Array<Containers::String> GetSupportedExtensions() const override;
|
||||
bool CanLoad(const Containers::String& path) const override;
|
||||
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
|
||||
ImportSettings* GetDefaultSettings() const override;
|
||||
|
||||
private:
|
||||
bool ParseWAVData(const Containers::Array<Core::uint8>& data, AudioClip* audioClip);
|
||||
AudioFormat DetectAudioFormat(const Containers::String& path, const Containers::Array<Core::uint8>& data);
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,5 @@
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include "Material.h"
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
221
engine/Runtime/Resources/Material/Material.h
Normal file
221
engine/Runtime/Resources/Material/Material.h
Normal file
@@ -0,0 +1,221 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Core/Asset/AssetRef.h>
|
||||
#include <XCEngine/Core/Asset/ResourceHandle.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
|
||||
#include "MaterialRenderState.h"
|
||||
#include "engine/Runtime/Resources/Shader/Shader.h"
|
||||
|
||||
#include <XCEngine/Core/Containers/HashMap.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/Core/Math/Vector2.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Core/Math/Vector4.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
class RHIBuffer;
|
||||
}
|
||||
namespace Resources {
|
||||
|
||||
enum class MaterialRenderQueue : Core::int32 {
|
||||
Background = 1000,
|
||||
Geometry = 2000,
|
||||
AlphaTest = 2450,
|
||||
Transparent = 3000,
|
||||
Overlay = 4000
|
||||
};
|
||||
|
||||
enum class MaterialPropertyType {
|
||||
Float, Float2, Float3, Float4,
|
||||
Int, Int2, Int3, Int4,
|
||||
Bool, Texture, Cubemap
|
||||
};
|
||||
|
||||
struct MaterialProperty {
|
||||
Containers::String name;
|
||||
MaterialPropertyType type;
|
||||
|
||||
union Value {
|
||||
float floatValue[4];
|
||||
Core::int32 intValue[4];
|
||||
bool boolValue;
|
||||
|
||||
Value() { memset(this, 0, sizeof(Value)); }
|
||||
} value;
|
||||
|
||||
Core::uint32 refCount = 0;
|
||||
|
||||
MaterialProperty() : type(MaterialPropertyType::Float), refCount(0) {}
|
||||
};
|
||||
|
||||
struct MaterialConstantFieldDesc {
|
||||
Containers::String name;
|
||||
MaterialPropertyType type = MaterialPropertyType::Float;
|
||||
Core::uint32 offset = 0;
|
||||
Core::uint32 size = 0;
|
||||
Core::uint32 alignedSize = 0;
|
||||
};
|
||||
|
||||
struct MaterialTagEntry {
|
||||
Containers::String name;
|
||||
Containers::String value;
|
||||
};
|
||||
|
||||
struct PendingTextureLoadState {
|
||||
IResource* resource = nullptr;
|
||||
Containers::String errorMessage;
|
||||
bool completed = false;
|
||||
};
|
||||
|
||||
struct MaterialTextureBinding {
|
||||
Containers::String name;
|
||||
Core::uint32 slot = 0;
|
||||
ResourceHandle<Texture> texture;
|
||||
AssetRef textureRef;
|
||||
Containers::String texturePath;
|
||||
std::shared_ptr<PendingTextureLoadState> pendingLoad;
|
||||
};
|
||||
|
||||
struct MaterialBufferBindingViewDesc {
|
||||
Core::uint32 firstElement = 0;
|
||||
Core::uint32 elementCount = 0;
|
||||
Core::uint32 structureByteStride = 0;
|
||||
};
|
||||
|
||||
struct MaterialBufferBinding {
|
||||
Containers::String name;
|
||||
RHI::RHIBuffer* buffer = nullptr;
|
||||
MaterialBufferBindingViewDesc viewDesc = {};
|
||||
};
|
||||
|
||||
class Material : public IResource {
|
||||
public:
|
||||
Material();
|
||||
virtual ~Material() override;
|
||||
|
||||
ResourceType GetType() const override { return ResourceType::Material; }
|
||||
const Containers::String& GetName() const override { return m_name; }
|
||||
const Containers::String& GetPath() const override { return m_path; }
|
||||
ResourceGUID GetGUID() const override { return m_guid; }
|
||||
bool IsValid() const override { return m_isValid; }
|
||||
size_t GetMemorySize() const override { return m_memorySize; }
|
||||
void Release() override;
|
||||
|
||||
void SetShader(const ResourceHandle<class Shader>& shader);
|
||||
class Shader* GetShader() const { return m_shader.Get(); }
|
||||
|
||||
void SetRenderQueue(Core::int32 renderQueue);
|
||||
void SetRenderQueue(MaterialRenderQueue renderQueue);
|
||||
Core::int32 GetRenderQueue() const { return m_renderQueue; }
|
||||
|
||||
void SetRenderState(const MaterialRenderState& renderState);
|
||||
const MaterialRenderState& GetRenderState() const { return m_renderState; }
|
||||
bool HasRenderStateOverride() const { return m_hasRenderStateOverride; }
|
||||
void SetRenderStateOverrideEnabled(bool enabled);
|
||||
|
||||
void SetTag(const Containers::String& name, const Containers::String& value);
|
||||
Containers::String GetTag(const Containers::String& name) const;
|
||||
bool HasTag(const Containers::String& name) const;
|
||||
void RemoveTag(const Containers::String& name);
|
||||
void ClearTags();
|
||||
Core::uint32 GetTagCount() const { return static_cast<Core::uint32>(m_tags.Size()); }
|
||||
Containers::String GetTagName(Core::uint32 index) const;
|
||||
Containers::String GetTagValue(Core::uint32 index) const;
|
||||
const Containers::Array<MaterialTagEntry>& GetTags() const { return m_tags; }
|
||||
|
||||
void EnableKeyword(const Containers::String& keyword);
|
||||
void DisableKeyword(const Containers::String& keyword);
|
||||
void SetKeywordEnabled(const Containers::String& keyword, bool enabled);
|
||||
bool IsKeywordEnabled(const Containers::String& keyword) const;
|
||||
void ClearKeywords();
|
||||
Core::uint32 GetKeywordCount() const { return static_cast<Core::uint32>(m_keywordSet.enabledKeywords.Size()); }
|
||||
Containers::String GetKeyword(Core::uint32 index) const;
|
||||
const ShaderKeywordSet& GetKeywordSet() const { return m_keywordSet; }
|
||||
|
||||
void SetFloat(const Containers::String& name, float value);
|
||||
void SetFloat2(const Containers::String& name, const Math::Vector2& value);
|
||||
void SetFloat3(const Containers::String& name, const Math::Vector3& value);
|
||||
void SetFloat4(const Containers::String& name, const Math::Vector4& value);
|
||||
void SetInt(const Containers::String& name, Core::int32 value);
|
||||
void SetBool(const Containers::String& name, bool value);
|
||||
void SetTexture(const Containers::String& name, const ResourceHandle<Texture>& texture);
|
||||
void SetBuffer(const Containers::String& name, RHI::RHIBuffer* buffer);
|
||||
void SetBuffer(
|
||||
const Containers::String& name,
|
||||
RHI::RHIBuffer* buffer,
|
||||
const MaterialBufferBindingViewDesc& viewDesc);
|
||||
void SetTextureAssetRef(const Containers::String& name,
|
||||
const AssetRef& textureRef,
|
||||
const Containers::String& texturePath = Containers::String());
|
||||
void SetTexturePath(const Containers::String& name, const Containers::String& texturePath);
|
||||
|
||||
float GetFloat(const Containers::String& name) const;
|
||||
Math::Vector2 GetFloat2(const Containers::String& name) const;
|
||||
Math::Vector3 GetFloat3(const Containers::String& name) const;
|
||||
Math::Vector4 GetFloat4(const Containers::String& name) const;
|
||||
Core::int32 GetInt(const Containers::String& name) const;
|
||||
bool GetBool(const Containers::String& name) const;
|
||||
ResourceHandle<Texture> GetTexture(const Containers::String& name) const;
|
||||
RHI::RHIBuffer* GetBuffer(const Containers::String& name) const;
|
||||
const MaterialBufferBinding* FindBufferBinding(const Containers::String& name) const;
|
||||
Core::uint32 GetTextureBindingCount() const { return static_cast<Core::uint32>(m_textureBindings.Size()); }
|
||||
Core::uint32 GetBufferBindingCount() const { return static_cast<Core::uint32>(m_bufferBindings.Size()); }
|
||||
Containers::String GetTextureBindingName(Core::uint32 index) const;
|
||||
AssetRef GetTextureBindingAssetRef(Core::uint32 index) const;
|
||||
Containers::String GetTextureBindingPath(Core::uint32 index) const;
|
||||
ResourceHandle<Texture> GetTextureBindingLoadedTexture(Core::uint32 index) const;
|
||||
ResourceHandle<Texture> GetTextureBindingTexture(Core::uint32 index) const;
|
||||
const Containers::Array<MaterialTextureBinding>& GetTextureBindings() const { return m_textureBindings; }
|
||||
const Containers::Array<MaterialBufferBinding>& GetBufferBindings() const { return m_bufferBindings; }
|
||||
std::vector<MaterialProperty> GetProperties() const;
|
||||
|
||||
const Containers::Array<Core::uint8>& GetConstantBufferData() const { return m_constantBufferData; }
|
||||
const Containers::Array<MaterialConstantFieldDesc>& GetConstantLayout() const { return m_constantLayout; }
|
||||
const MaterialConstantFieldDesc* FindConstantField(const Containers::String& name) const;
|
||||
void UpdateConstantBuffer();
|
||||
Core::uint64 GetChangeVersion() const { return m_changeVersion; }
|
||||
void RecalculateMemorySize();
|
||||
|
||||
bool HasProperty(const Containers::String& name) const;
|
||||
void RemoveProperty(const Containers::String& name);
|
||||
void ClearAllProperties();
|
||||
void RemoveBufferBinding(const Containers::String& name);
|
||||
void ClearBufferBindings();
|
||||
|
||||
private:
|
||||
const ShaderPropertyDesc* FindShaderPropertyDesc(const Containers::String& name) const;
|
||||
bool CanAssignPropertyType(const Containers::String& name, MaterialPropertyType type) const;
|
||||
bool ResetPropertyToShaderDefault(const Containers::String& name);
|
||||
void SyncShaderSchemaProperties(bool removeUnknownProperties);
|
||||
void SyncShaderRuntimeBufferBindings(bool removeUnknownBindings);
|
||||
void BeginAsyncTextureLoad(Core::uint32 index);
|
||||
void ResolvePendingTextureBinding(Core::uint32 index);
|
||||
void ResolvePendingTextureBindings();
|
||||
void MarkChanged(bool updateConstantBuffer);
|
||||
void UpdateMemorySize();
|
||||
void SyncShaderSchemaKeywords(bool removeUnknownKeywords);
|
||||
|
||||
ResourceHandle<class Shader> m_shader;
|
||||
Core::int32 m_renderQueue = static_cast<Core::int32>(MaterialRenderQueue::Geometry);
|
||||
MaterialRenderState m_renderState;
|
||||
bool m_hasRenderStateOverride = false;
|
||||
Containers::Array<MaterialTagEntry> m_tags;
|
||||
ShaderKeywordSet m_keywordSet;
|
||||
Containers::HashMap<Containers::String, MaterialProperty> m_properties;
|
||||
Containers::Array<MaterialConstantFieldDesc> m_constantLayout;
|
||||
Containers::Array<Core::uint8> m_constantBufferData;
|
||||
Containers::Array<MaterialTextureBinding> m_textureBindings;
|
||||
Containers::Array<MaterialBufferBinding> m_bufferBindings;
|
||||
Core::uint64 m_changeVersion = 1;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Core/Asset/ArtifactContainer.h>
|
||||
#include "MaterialLoader.h"
|
||||
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
|
||||
#include "engine/Runtime/Resources/Shader/ShaderLoader.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
26
engine/Runtime/Resources/Material/MaterialLoader.h
Normal file
26
engine/Runtime/Resources/Material/MaterialLoader.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
|
||||
#include "Material.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class MaterialLoader : public IResourceLoader {
|
||||
public:
|
||||
MaterialLoader();
|
||||
virtual ~MaterialLoader() override;
|
||||
|
||||
ResourceType GetResourceType() const override { return ResourceType::Material; }
|
||||
Containers::Array<Containers::String> GetSupportedExtensions() const override;
|
||||
bool CanLoad(const Containers::String& path) const override;
|
||||
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
|
||||
ImportSettings* GetDefaultSettings() const override;
|
||||
|
||||
private:
|
||||
bool ParseMaterialData(const Containers::Array<Core::uint8>& data, Material* material);
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
147
engine/Runtime/Resources/Material/MaterialRenderState.h
Normal file
147
engine/Runtime/Resources/Material/MaterialRenderState.h
Normal file
@@ -0,0 +1,147 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class MaterialCullMode : Core::uint8 {
|
||||
None = 0,
|
||||
Front = 1,
|
||||
Back = 2
|
||||
};
|
||||
|
||||
enum class MaterialComparisonFunc : Core::uint8 {
|
||||
Never = 0,
|
||||
Less = 1,
|
||||
Equal = 2,
|
||||
LessEqual = 3,
|
||||
Greater = 4,
|
||||
NotEqual = 5,
|
||||
GreaterEqual = 6,
|
||||
Always = 7
|
||||
};
|
||||
|
||||
enum class MaterialBlendOp : Core::uint8 {
|
||||
Add = 0,
|
||||
Subtract = 1,
|
||||
ReverseSubtract = 2,
|
||||
Min = 3,
|
||||
Max = 4
|
||||
};
|
||||
|
||||
enum class MaterialBlendFactor : Core::uint8 {
|
||||
Zero = 0,
|
||||
One = 1,
|
||||
SrcColor = 2,
|
||||
InvSrcColor = 3,
|
||||
SrcAlpha = 4,
|
||||
InvSrcAlpha = 5,
|
||||
DstAlpha = 6,
|
||||
InvDstAlpha = 7,
|
||||
DstColor = 8,
|
||||
InvDstColor = 9,
|
||||
SrcAlphaSat = 10,
|
||||
BlendFactor = 11,
|
||||
InvBlendFactor = 12,
|
||||
Src1Color = 13,
|
||||
InvSrc1Color = 14,
|
||||
Src1Alpha = 15,
|
||||
InvSrc1Alpha = 16
|
||||
};
|
||||
|
||||
enum class MaterialStencilOp : Core::uint8 {
|
||||
Keep = 0,
|
||||
Zero = 1,
|
||||
Replace = 2,
|
||||
IncrSat = 3,
|
||||
DecrSat = 4,
|
||||
Invert = 5,
|
||||
IncrWrap = 6,
|
||||
DecrWrap = 7
|
||||
};
|
||||
|
||||
struct MaterialStencilFaceState {
|
||||
MaterialStencilOp failOp = MaterialStencilOp::Keep;
|
||||
MaterialStencilOp passOp = MaterialStencilOp::Keep;
|
||||
MaterialStencilOp depthFailOp = MaterialStencilOp::Keep;
|
||||
MaterialComparisonFunc func = MaterialComparisonFunc::Always;
|
||||
|
||||
bool operator==(const MaterialStencilFaceState& other) const {
|
||||
return failOp == other.failOp &&
|
||||
passOp == other.passOp &&
|
||||
depthFailOp == other.depthFailOp &&
|
||||
func == other.func;
|
||||
}
|
||||
|
||||
bool operator!=(const MaterialStencilFaceState& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
struct MaterialStencilState {
|
||||
bool enabled = false;
|
||||
Core::uint8 readMask = 0xFF;
|
||||
Core::uint8 writeMask = 0xFF;
|
||||
Core::uint8 reference = 0;
|
||||
MaterialStencilFaceState front = {};
|
||||
MaterialStencilFaceState back = {};
|
||||
|
||||
bool operator==(const MaterialStencilState& other) const {
|
||||
return enabled == other.enabled &&
|
||||
readMask == other.readMask &&
|
||||
writeMask == other.writeMask &&
|
||||
reference == other.reference &&
|
||||
front == other.front &&
|
||||
back == other.back;
|
||||
}
|
||||
|
||||
bool operator!=(const MaterialStencilState& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
struct MaterialRenderState {
|
||||
bool blendEnable = false;
|
||||
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
|
||||
MaterialBlendFactor dstBlend = MaterialBlendFactor::Zero;
|
||||
MaterialBlendFactor srcBlendAlpha = MaterialBlendFactor::One;
|
||||
MaterialBlendFactor dstBlendAlpha = MaterialBlendFactor::Zero;
|
||||
MaterialBlendOp blendOp = MaterialBlendOp::Add;
|
||||
MaterialBlendOp blendOpAlpha = MaterialBlendOp::Add;
|
||||
Core::uint8 colorWriteMask = 0xF;
|
||||
|
||||
bool depthTestEnable = true;
|
||||
bool depthWriteEnable = true;
|
||||
MaterialComparisonFunc depthFunc = MaterialComparisonFunc::Less;
|
||||
|
||||
MaterialCullMode cullMode = MaterialCullMode::None;
|
||||
float depthBiasFactor = 0.0f;
|
||||
Core::int32 depthBiasUnits = 0;
|
||||
MaterialStencilState stencil = {};
|
||||
|
||||
bool operator==(const MaterialRenderState& other) const {
|
||||
return blendEnable == other.blendEnable &&
|
||||
srcBlend == other.srcBlend &&
|
||||
dstBlend == other.dstBlend &&
|
||||
srcBlendAlpha == other.srcBlendAlpha &&
|
||||
dstBlendAlpha == other.dstBlendAlpha &&
|
||||
blendOp == other.blendOp &&
|
||||
blendOpAlpha == other.blendOpAlpha &&
|
||||
colorWriteMask == other.colorWriteMask &&
|
||||
depthTestEnable == other.depthTestEnable &&
|
||||
depthWriteEnable == other.depthWriteEnable &&
|
||||
depthFunc == other.depthFunc &&
|
||||
cullMode == other.cullMode &&
|
||||
depthBiasFactor == other.depthBiasFactor &&
|
||||
depthBiasUnits == other.depthBiasUnits &&
|
||||
stencil == other.stencil;
|
||||
}
|
||||
|
||||
bool operator!=(const MaterialRenderState& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
#include "Mesh.h"
|
||||
#include "engine/Runtime/Resources/Material/Material.h"
|
||||
#include "engine/Runtime/Resources/Texture/Texture.h"
|
||||
#include <cstring>
|
||||
#include <unordered_set>
|
||||
|
||||
112
engine/Runtime/Resources/Mesh/Mesh.h
Normal file
112
engine/Runtime/Resources/Mesh/Mesh.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Math/Bounds.h>
|
||||
#include <XCEngine/Core/Math/Vector2.h>
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
#include <XCEngine/Core/Math/Vector4.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class Material;
|
||||
class Texture;
|
||||
|
||||
enum class VertexAttribute : Core::uint32 {
|
||||
Position = 1 << 0, Normal = 1 << 1, Tangent = 1 << 2,
|
||||
Color = 1 << 3, UV0 = 1 << 4, UV1 = 1 << 5,
|
||||
UV2 = 1 << 6, UV3 = 1 << 7, BoneWeights = 1 << 8, BoneIndices = 1 << 9,
|
||||
Bitangent = 1 << 10
|
||||
};
|
||||
|
||||
inline VertexAttribute operator|(VertexAttribute a, VertexAttribute b) {
|
||||
return static_cast<VertexAttribute>(static_cast<Core::uint32>(a) | static_cast<Core::uint32>(b));
|
||||
}
|
||||
|
||||
inline VertexAttribute operator&(VertexAttribute a, VertexAttribute b) {
|
||||
return static_cast<VertexAttribute>(static_cast<Core::uint32>(a) & static_cast<Core::uint32>(b));
|
||||
}
|
||||
|
||||
inline bool HasVertexAttribute(VertexAttribute attributes, VertexAttribute flag) {
|
||||
return (static_cast<Core::uint32>(attributes) & static_cast<Core::uint32>(flag)) != 0;
|
||||
}
|
||||
|
||||
struct StaticMeshVertex {
|
||||
Math::Vector3 position = Math::Vector3::Zero();
|
||||
Math::Vector3 normal = Math::Vector3::Zero();
|
||||
Math::Vector3 tangent = Math::Vector3::Zero();
|
||||
Math::Vector3 bitangent = Math::Vector3::Zero();
|
||||
Math::Vector2 uv0 = Math::Vector2::Zero();
|
||||
Math::Vector2 uv1 = Math::Vector2::Zero();
|
||||
Math::Vector4 color = Math::Vector4::One();
|
||||
};
|
||||
|
||||
struct MeshSection {
|
||||
Core::uint32 baseVertex;
|
||||
Core::uint32 vertexCount;
|
||||
Core::uint32 startIndex;
|
||||
Core::uint32 indexCount;
|
||||
Core::uint32 materialID;
|
||||
Math::Bounds bounds;
|
||||
};
|
||||
|
||||
class Mesh : public IResource {
|
||||
public:
|
||||
Mesh();
|
||||
virtual ~Mesh() override;
|
||||
|
||||
ResourceType GetType() const override { return ResourceType::Mesh; }
|
||||
const Containers::String& GetName() const override { return m_name; }
|
||||
const Containers::String& GetPath() const override { return m_path; }
|
||||
ResourceGUID GetGUID() const override { return m_guid; }
|
||||
bool IsValid() const override { return m_isValid; }
|
||||
size_t GetMemorySize() const override { return m_memorySize; }
|
||||
void Release() override;
|
||||
|
||||
void SetVertexData(const void* data, size_t size, Core::uint32 vertexCount,
|
||||
Core::uint32 vertexStride, VertexAttribute attributes);
|
||||
const void* GetVertexData() const { return m_vertexData.Data(); }
|
||||
size_t GetVertexDataSize() const { return m_vertexData.Size(); }
|
||||
Core::uint32 GetVertexCount() const { return m_vertexCount; }
|
||||
Core::uint32 GetVertexStride() const { return m_vertexStride; }
|
||||
VertexAttribute GetVertexAttributes() const { return m_attributes; }
|
||||
|
||||
void SetIndexData(const void* data, size_t size, Core::uint32 indexCount, bool use32Bit);
|
||||
const void* GetIndexData() const { return m_indexData.Data(); }
|
||||
size_t GetIndexDataSize() const { return m_indexData.Size(); }
|
||||
Core::uint32 GetIndexCount() const { return m_indexCount; }
|
||||
bool IsUse32BitIndex() const { return m_use32BitIndex; }
|
||||
|
||||
void SetBounds(const Math::Bounds& bounds) { m_bounds = bounds; }
|
||||
const Math::Bounds& GetBounds() const { return m_bounds; }
|
||||
|
||||
void AddSection(const MeshSection& section);
|
||||
void AddMaterial(Material* material);
|
||||
void AddTexture(Texture* texture);
|
||||
const Containers::Array<MeshSection>& GetSections() const { return m_sections; }
|
||||
const Containers::Array<Material*>& GetMaterials() const { return m_materials; }
|
||||
const Containers::Array<Texture*>& GetTextures() const { return m_textures; }
|
||||
Material* GetMaterial(Core::uint32 index) const;
|
||||
|
||||
private:
|
||||
void UpdateMemorySize();
|
||||
|
||||
Core::uint32 m_vertexCount = 0;
|
||||
Core::uint32 m_vertexStride = 0;
|
||||
VertexAttribute m_attributes = VertexAttribute::Position;
|
||||
|
||||
Core::uint32 m_indexCount = 0;
|
||||
bool m_use32BitIndex = false;
|
||||
|
||||
Containers::Array<Core::uint8> m_vertexData;
|
||||
Containers::Array<Core::uint8> m_indexData;
|
||||
Containers::Array<MeshSection> m_sections;
|
||||
Containers::Array<Material*> m_materials;
|
||||
Containers::Array<Texture*> m_textures;
|
||||
Math::Bounds m_bounds;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
|
||||
#include "MeshImportSettings.h"
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <sstream>
|
||||
85
engine/Runtime/Resources/Mesh/MeshImportSettings.h
Normal file
85
engine/Runtime/Resources/Mesh/MeshImportSettings.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/ImportSettings.h>
|
||||
#include "Mesh.h"
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class MeshImportFlags : Core::uint32 {
|
||||
None = 0,
|
||||
FlipUVs = 1 << 0,
|
||||
FlipWindingOrder = 1 << 1,
|
||||
GenerateNormals = 1 << 2,
|
||||
GenerateTangents = 1 << 3,
|
||||
OptimizeMesh = 1 << 4,
|
||||
ImportSkinning = 1 << 5,
|
||||
ImportAnimations = 1 << 6,
|
||||
ImportMaterials = 1 << 7,
|
||||
ImportCameras = 1 << 8,
|
||||
ImportLights = 1 << 9
|
||||
};
|
||||
|
||||
inline MeshImportFlags operator|(MeshImportFlags a, MeshImportFlags b) {
|
||||
return static_cast<MeshImportFlags>(static_cast<Core::uint32>(a) | static_cast<Core::uint32>(b));
|
||||
}
|
||||
|
||||
inline MeshImportFlags operator&(MeshImportFlags a, MeshImportFlags b) {
|
||||
return static_cast<MeshImportFlags>(static_cast<Core::uint32>(a) & static_cast<Core::uint32>(b));
|
||||
}
|
||||
|
||||
inline MeshImportFlags operator~(MeshImportFlags a) {
|
||||
return static_cast<MeshImportFlags>(~static_cast<Core::uint32>(a));
|
||||
}
|
||||
|
||||
class MeshImportSettings : public ImportSettings {
|
||||
public:
|
||||
MeshImportSettings();
|
||||
virtual ~MeshImportSettings() override;
|
||||
|
||||
Core::UniqueRef<ImportSettings> Clone() const override;
|
||||
bool LoadFromJSON(const Containers::String& json) override;
|
||||
Containers::String SaveToJSON() const override;
|
||||
|
||||
void SetImportFlags(MeshImportFlags flags) { m_importFlags = flags; }
|
||||
MeshImportFlags GetImportFlags() const { return m_importFlags; }
|
||||
|
||||
void AddImportFlag(MeshImportFlags flag) { m_importFlags = static_cast<MeshImportFlags>(static_cast<Core::uint32>(m_importFlags) | static_cast<Core::uint32>(flag)); }
|
||||
void RemoveImportFlag(MeshImportFlags flag) { m_importFlags = static_cast<MeshImportFlags>(static_cast<Core::uint32>(m_importFlags) & ~static_cast<Core::uint32>(flag)); }
|
||||
bool HasImportFlag(MeshImportFlags flag) const { return (static_cast<Core::uint32>(m_importFlags) & static_cast<Core::uint32>(flag)) != 0; }
|
||||
|
||||
void SetScale(float scale) { m_scale = scale; }
|
||||
float GetScale() const { return m_scale; }
|
||||
|
||||
void SetOffset(const Math::Vector3& offset) { m_offset = offset; }
|
||||
const Math::Vector3& GetOffset() const { return m_offset; }
|
||||
|
||||
void SetAxisConversion(bool convert) { m_axisConversion = convert; }
|
||||
bool GetAxisConversion() const { return m_axisConversion; }
|
||||
|
||||
void SetMergeMeshes(bool merge) { m_mergeMeshes = merge; }
|
||||
bool GetMergeMeshes() const { return m_mergeMeshes; }
|
||||
|
||||
void SetOptimizeThreshold(float threshold) { m_optimizeThreshold = threshold; }
|
||||
float GetOptimizeThreshold() const { return m_optimizeThreshold; }
|
||||
|
||||
void SetImportScale(float scale) { m_importScale = scale; }
|
||||
float GetImportScale() const { return m_importScale; }
|
||||
|
||||
void SetThreshold(float threshold) { m_threshold = threshold; }
|
||||
float GetThreshold() const { return m_threshold; }
|
||||
|
||||
private:
|
||||
MeshImportFlags m_importFlags = MeshImportFlags::ImportMaterials;
|
||||
float m_scale = 1.0f;
|
||||
Math::Vector3 m_offset = Math::Vector3::Zero();
|
||||
bool m_axisConversion = true;
|
||||
bool m_mergeMeshes = false;
|
||||
float m_optimizeThreshold = 0.3f;
|
||||
float m_importScale = 1.0f;
|
||||
float m_threshold = 0.0f;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,15 +1,15 @@
|
||||
#include <XCEngine/Resources/Mesh/MeshLoader.h>
|
||||
#include <XCEngine/Core/Asset/ArtifactContainer.h>
|
||||
#include "MeshLoader.h"
|
||||
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Material/MaterialLoader.h>
|
||||
#include <XCEngine/Resources/Mesh/MeshImportSettings.h>
|
||||
#include <XCEngine/Resources/Material/Material.h>
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
#include "engine/Runtime/Resources/Material/MaterialLoader.h"
|
||||
#include "MeshImportSettings.h"
|
||||
#include "engine/Runtime/Resources/Material/Material.h"
|
||||
#include "engine/Runtime/Resources/Texture/Texture.h"
|
||||
#include <XCEngine/Resources/Texture/TextureImportHeuristics.h>
|
||||
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
||||
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include "engine/Runtime/Resources/Texture/TextureLoader.h"
|
||||
#include "engine/Runtime/Resources/Texture/TextureImportSettings.h"
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
#include <XCEngine/Core/Math/Matrix4.h>
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/material.h>
|
||||
22
engine/Runtime/Resources/Mesh/MeshLoader.h
Normal file
22
engine/Runtime/Resources/Mesh/MeshLoader.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
#include "Mesh.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class MeshLoader : public IResourceLoader {
|
||||
public:
|
||||
MeshLoader();
|
||||
virtual ~MeshLoader() override;
|
||||
|
||||
ResourceType GetResourceType() const override { return ResourceType::Mesh; }
|
||||
Containers::Array<Containers::String> GetSupportedExtensions() const override;
|
||||
bool CanLoad(const Containers::String& path) const override;
|
||||
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
|
||||
ImportSettings* GetDefaultSettings() const override;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "ShaderFileUtils.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ArtifactContainer.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include "../Shader.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
#include "ShaderAuthoringInternal.h"
|
||||
#include "ShaderFileUtils.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
#include <XCEngine/Rendering/Builtin/BuiltinPassLayoutUtils.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
#include "../ShaderLoader.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
@@ -3,7 +3,8 @@
|
||||
#include "../ShaderIR.h"
|
||||
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
|
||||
#include "../Shader.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include "Shader.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
201
engine/Runtime/Resources/Shader/Shader.h
Normal file
201
engine/Runtime/Resources/Shader/Shader.h
Normal file
@@ -0,0 +1,201 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderKeywordTypes.h>
|
||||
|
||||
#include "engine/Runtime/Resources/Material/MaterialRenderState.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class ShaderType : Core::uint8 {
|
||||
Vertex,
|
||||
Fragment,
|
||||
Geometry,
|
||||
Compute,
|
||||
Hull,
|
||||
Domain
|
||||
};
|
||||
|
||||
enum class ShaderLanguage : Core::uint8 {
|
||||
GLSL,
|
||||
HLSL,
|
||||
SPIRV
|
||||
};
|
||||
|
||||
enum class ShaderBackend : Core::uint8 {
|
||||
Generic = 0,
|
||||
D3D12,
|
||||
OpenGL,
|
||||
Vulkan
|
||||
};
|
||||
|
||||
enum class ShaderPropertyType : Core::uint8 {
|
||||
Float = 0,
|
||||
Range,
|
||||
Int,
|
||||
Vector,
|
||||
Color,
|
||||
Texture2D,
|
||||
TextureCube
|
||||
};
|
||||
|
||||
enum class ShaderResourceType : Core::uint8 {
|
||||
ConstantBuffer = 0,
|
||||
Texture2D,
|
||||
TextureCube,
|
||||
Sampler,
|
||||
StructuredBuffer,
|
||||
RawBuffer,
|
||||
RWStructuredBuffer,
|
||||
RWRawBuffer
|
||||
};
|
||||
|
||||
struct ShaderUniform {
|
||||
Containers::String name;
|
||||
Core::uint32 location;
|
||||
Core::uint32 size;
|
||||
Core::uint32 type;
|
||||
};
|
||||
|
||||
struct ShaderAttribute {
|
||||
Containers::String name;
|
||||
Core::uint32 location;
|
||||
Core::uint32 size;
|
||||
Core::uint32 type;
|
||||
};
|
||||
|
||||
struct ShaderPassTagEntry {
|
||||
Containers::String name;
|
||||
Containers::String value;
|
||||
};
|
||||
|
||||
struct ShaderPropertyDesc {
|
||||
Containers::String name;
|
||||
Containers::String displayName;
|
||||
ShaderPropertyType type = ShaderPropertyType::Float;
|
||||
Containers::String defaultValue;
|
||||
Containers::String semantic;
|
||||
};
|
||||
|
||||
struct ShaderResourceBindingDesc {
|
||||
Containers::String name;
|
||||
ShaderResourceType type = ShaderResourceType::ConstantBuffer;
|
||||
Core::uint32 set = 0;
|
||||
Core::uint32 binding = 0;
|
||||
Containers::String semantic;
|
||||
};
|
||||
|
||||
struct ShaderBackendCompiledBinary {
|
||||
ShaderBackend backend = ShaderBackend::Generic;
|
||||
Containers::Array<Core::uint8> payload;
|
||||
};
|
||||
|
||||
struct ShaderStageVariant {
|
||||
ShaderType stage = ShaderType::Fragment;
|
||||
ShaderLanguage language = ShaderLanguage::GLSL;
|
||||
ShaderBackend backend = ShaderBackend::Generic;
|
||||
ShaderKeywordSet requiredKeywords;
|
||||
Containers::String entryPoint;
|
||||
Containers::String profile;
|
||||
Containers::String sourceCode;
|
||||
Containers::Array<Core::uint8> compiledBinary;
|
||||
Containers::Array<ShaderBackendCompiledBinary> backendCompiledBinaries;
|
||||
|
||||
const Containers::Array<Core::uint8>* GetCompiledBinaryForBackend(
|
||||
ShaderBackend targetBackend) const;
|
||||
void SetCompiledBinaryForBackend(
|
||||
ShaderBackend targetBackend,
|
||||
const Containers::Array<Core::uint8>& binary);
|
||||
void SetCompiledBinaryForBackend(
|
||||
ShaderBackend targetBackend,
|
||||
Containers::Array<Core::uint8>&& binary);
|
||||
};
|
||||
|
||||
struct ShaderPass {
|
||||
Containers::String name;
|
||||
bool hasFixedFunctionState = false;
|
||||
MaterialRenderState fixedFunctionState;
|
||||
Containers::Array<ShaderPassTagEntry> tags;
|
||||
Containers::Array<ShaderResourceBindingDesc> resources;
|
||||
Containers::Array<ShaderKeywordDeclaration> keywordDeclarations;
|
||||
Containers::Array<ShaderStageVariant> variants;
|
||||
};
|
||||
|
||||
class Shader : public IResource {
|
||||
public:
|
||||
Shader();
|
||||
virtual ~Shader() override;
|
||||
|
||||
ResourceType GetType() const override { return ResourceType::Shader; }
|
||||
const Containers::String& GetName() const override { return m_name; }
|
||||
const Containers::String& GetPath() const override { return m_path; }
|
||||
ResourceGUID GetGUID() const override { return m_guid; }
|
||||
bool IsValid() const override { return m_isValid; }
|
||||
size_t GetMemorySize() const override { return m_memorySize; }
|
||||
void Release() override;
|
||||
|
||||
void AddUniform(const ShaderUniform& uniform);
|
||||
const Containers::Array<ShaderUniform>& GetUniforms() const { return m_uniforms; }
|
||||
|
||||
void AddAttribute(const ShaderAttribute& attribute);
|
||||
const Containers::Array<ShaderAttribute>& GetAttributes() const { return m_attributes; }
|
||||
|
||||
void AddProperty(const ShaderPropertyDesc& property);
|
||||
void ClearProperties();
|
||||
const Containers::Array<ShaderPropertyDesc>& GetProperties() const { return m_properties; }
|
||||
const ShaderPropertyDesc* FindProperty(const Containers::String& propertyName) const;
|
||||
void SetFallback(const Containers::String& fallback);
|
||||
const Containers::String& GetFallback() const { return m_fallback; }
|
||||
|
||||
void AddPass(const ShaderPass& pass);
|
||||
void ClearPasses();
|
||||
Core::uint32 GetPassCount() const { return static_cast<Core::uint32>(m_passes.Size()); }
|
||||
const Containers::Array<ShaderPass>& GetPasses() const { return m_passes; }
|
||||
|
||||
void AddPassVariant(const Containers::String& passName, const ShaderStageVariant& variant);
|
||||
void SetPassTag(
|
||||
const Containers::String& passName,
|
||||
const Containers::String& tagName,
|
||||
const Containers::String& tagValue);
|
||||
void AddPassResourceBinding(
|
||||
const Containers::String& passName,
|
||||
const ShaderResourceBindingDesc& binding);
|
||||
void AddPassKeywordDeclaration(
|
||||
const Containers::String& passName,
|
||||
const ShaderKeywordDeclaration& declaration);
|
||||
bool HasPass(const Containers::String& passName) const;
|
||||
const ShaderPass* FindPass(const Containers::String& passName) const;
|
||||
ShaderPass* FindPass(const Containers::String& passName);
|
||||
bool PassDeclaresKeyword(
|
||||
const Containers::String& passName,
|
||||
const Containers::String& keyword) const;
|
||||
bool DeclaresKeyword(const Containers::String& keyword) const;
|
||||
const ShaderResourceBindingDesc* FindPassResourceBinding(
|
||||
const Containers::String& passName,
|
||||
const Containers::String& resourceName) const;
|
||||
const ShaderStageVariant* FindVariant(
|
||||
const Containers::String& passName,
|
||||
ShaderType stage,
|
||||
ShaderBackend backend = ShaderBackend::Generic,
|
||||
const ShaderKeywordSet& enabledKeywords = ShaderKeywordSet()) const;
|
||||
|
||||
class IRHIShader* GetRHIResource() const { return m_rhiResource; }
|
||||
void SetRHIResource(class IRHIShader* resource);
|
||||
|
||||
private:
|
||||
ShaderPass& GetOrCreatePass(const Containers::String& passName);
|
||||
|
||||
Containers::Array<ShaderUniform> m_uniforms;
|
||||
Containers::Array<ShaderAttribute> m_attributes;
|
||||
Containers::Array<ShaderPropertyDesc> m_properties;
|
||||
Containers::Array<ShaderPass> m_passes;
|
||||
Containers::String m_fallback;
|
||||
|
||||
class IRHIShader* m_rhiResource = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEngine/Resources/Shader/ShaderCompilationCache.h>
|
||||
#include "ShaderCompilationCache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
@@ -246,8 +246,9 @@ void ShaderCompilationCache::SaveDatabase() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderCompilationCache::Store(const ShaderCacheEntry& entry,
|
||||
Containers::String* outErrorMessage) {
|
||||
bool ShaderCompilationCache::Store(
|
||||
const ShaderCacheEntry& entry,
|
||||
Containers::String* outErrorMessage) {
|
||||
if (!IsInitialized()) {
|
||||
if (outErrorMessage != nullptr) {
|
||||
*outErrorMessage = MakeError("ShaderCompilationCache is not initialized.");
|
||||
@@ -319,9 +320,10 @@ bool ShaderCompilationCache::Store(const ShaderCacheEntry& entry,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShaderCompilationCache::TryLoad(const ShaderCompileKey& key,
|
||||
ShaderCacheEntry& outEntry,
|
||||
Containers::String* outErrorMessage) const {
|
||||
bool ShaderCompilationCache::TryLoad(
|
||||
const ShaderCompileKey& key,
|
||||
ShaderCacheEntry& outEntry,
|
||||
Containers::String* outErrorMessage) const {
|
||||
outEntry = {};
|
||||
if (!IsInitialized()) {
|
||||
if (outErrorMessage != nullptr) {
|
||||
92
engine/Runtime/Resources/Shader/ShaderCompilationCache.h
Normal file
92
engine/Runtime/Resources/Shader/ShaderCompilationCache.h
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/AssetGUID.h>
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
#include "Shader.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
constexpr Core::uint32 kShaderCompilationCacheSchemaVersion = 1;
|
||||
|
||||
enum class ShaderBytecodeFormat : Core::uint32 {
|
||||
Unknown = 0,
|
||||
DXIL,
|
||||
DXBC,
|
||||
SPIRV,
|
||||
GLSLSource,
|
||||
OpenGLProgramBinary
|
||||
};
|
||||
|
||||
struct ShaderCompileKey {
|
||||
Containers::String shaderPath;
|
||||
Containers::String sourceHash;
|
||||
Containers::String dependencyHash;
|
||||
Containers::String passName;
|
||||
Containers::String entryPoint;
|
||||
Containers::String profile;
|
||||
Containers::String compilerName;
|
||||
Containers::String compilerVersion;
|
||||
Containers::String optionsSignature;
|
||||
ShaderType stage = ShaderType::Fragment;
|
||||
ShaderLanguage sourceLanguage = ShaderLanguage::HLSL;
|
||||
ShaderBackend backend = ShaderBackend::Generic;
|
||||
Containers::Array<Containers::String> keywords;
|
||||
|
||||
void Normalize();
|
||||
Containers::String BuildSignature() const;
|
||||
Containers::String BuildCacheKey() const;
|
||||
};
|
||||
|
||||
struct ShaderCacheEntry {
|
||||
ShaderCompileKey key;
|
||||
ShaderBytecodeFormat format = ShaderBytecodeFormat::Unknown;
|
||||
Containers::Array<Core::uint8> payload;
|
||||
};
|
||||
|
||||
class ShaderCompilationCache {
|
||||
public:
|
||||
void Initialize(const Containers::String& libraryRoot);
|
||||
void Shutdown();
|
||||
|
||||
bool IsInitialized() const { return !m_libraryRoot.Empty(); }
|
||||
const Containers::String& GetLibraryRoot() const { return m_libraryRoot; }
|
||||
const Containers::String& GetDatabasePath() const { return m_databasePath; }
|
||||
Core::uint32 GetRecordCount() const { return static_cast<Core::uint32>(m_records.size()); }
|
||||
|
||||
Containers::String BuildCacheKey(const ShaderCompileKey& key) const;
|
||||
Containers::String BuildCacheRelativePath(const ShaderCompileKey& key) const;
|
||||
Containers::String BuildCacheAbsolutePath(const ShaderCompileKey& key) const;
|
||||
|
||||
bool Store(const ShaderCacheEntry& entry,
|
||||
Containers::String* outErrorMessage = nullptr);
|
||||
bool TryLoad(const ShaderCompileKey& key,
|
||||
ShaderCacheEntry& outEntry,
|
||||
Containers::String* outErrorMessage = nullptr) const;
|
||||
|
||||
private:
|
||||
struct ShaderCacheRecord {
|
||||
ShaderBackend backend = ShaderBackend::Generic;
|
||||
ShaderBytecodeFormat format = ShaderBytecodeFormat::Unknown;
|
||||
Containers::String relativePath;
|
||||
Core::uint64 payloadSize = 0;
|
||||
};
|
||||
|
||||
void LoadDatabase();
|
||||
void SaveDatabase() const;
|
||||
|
||||
static Containers::String BuildBackendDirectoryName(ShaderBackend backend);
|
||||
static AssetGUID ComputeKeyGuid(const Containers::String& cacheKey);
|
||||
|
||||
Containers::String m_libraryRoot;
|
||||
Containers::String m_databasePath;
|
||||
std::unordered_map<std::string, ShaderCacheRecord> m_records;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
#include <XCEngine/Core/Containers/Array.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
|
||||
#include "Shader.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
#include "ShaderLoader.h"
|
||||
|
||||
#include "Internal/ShaderArtifactLoader.h"
|
||||
#include "Internal/ShaderAuthoringLoader.h"
|
||||
#include "ShaderAuthoringParser.h"
|
||||
#include "Internal/ShaderFileUtils.h"
|
||||
#include "Internal/ShaderRuntimeBuildUtils.h"
|
||||
#include "ShaderAuthoringParser.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceTypes.h>
|
||||
#include "engine/Shared/Asset/ResourceType/ResourceTypes.h"
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
|
||||
#include <string>
|
||||
@@ -30,7 +30,7 @@ bool ShaderLoader::CanLoad(const Containers::String& path) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Containers::String ext = GetExtension(path).ToLower();
|
||||
const Containers::String ext = GetShaderPathExtension(path).ToLower();
|
||||
return ext == "shader" || ext == "xcshader";
|
||||
}
|
||||
|
||||
@@ -112,7 +112,9 @@ bool ShaderLoader::CollectSourceDependencies(
|
||||
return CollectShaderAuthoringDependencyPaths(path, sourceText, outDependencies, outError);
|
||||
}
|
||||
|
||||
ShaderType ShaderLoader::DetectShaderType(const Containers::String& path, const Containers::String& source) {
|
||||
ShaderType ShaderLoader::DetectShaderType(
|
||||
const Containers::String& path,
|
||||
const Containers::String& source) {
|
||||
(void)source;
|
||||
return DetectShaderTypeFromPath(path);
|
||||
}
|
||||
32
engine/Runtime/Resources/Shader/ShaderLoader.h
Normal file
32
engine/Runtime/Resources/Shader/ShaderLoader.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
|
||||
#include "Shader.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class ShaderLoader : public IResourceLoader {
|
||||
public:
|
||||
ShaderLoader();
|
||||
virtual ~ShaderLoader() override;
|
||||
|
||||
ResourceType GetResourceType() const override { return ResourceType::Shader; }
|
||||
Containers::Array<Containers::String> GetSupportedExtensions() const override;
|
||||
bool CanLoad(const Containers::String& path) const override;
|
||||
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
|
||||
ImportSettings* GetDefaultSettings() const override;
|
||||
bool CollectSourceDependencies(const Containers::String& path,
|
||||
Containers::Array<Containers::String>& outDependencies) const;
|
||||
bool CollectSourceDependencies(const Containers::String& path,
|
||||
Containers::Array<Containers::String>& outDependencies,
|
||||
Containers::String* outError) const;
|
||||
|
||||
private:
|
||||
ShaderType DetectShaderType(const Containers::String& path, const Containers::String& source);
|
||||
bool ParseShaderSource(const Containers::String& source, Shader* shader);
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "ShaderSourceUtils.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
@@ -1,7 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
|
||||
#include "Shader.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
#include "Texture.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace XCEngine {
|
||||
73
engine/Runtime/Resources/Texture/Texture.h
Normal file
73
engine/Runtime/Resources/Texture/Texture.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/IResource.h>
|
||||
#include <XCEngine/Core/Types.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class TextureType {
|
||||
Texture2D, Texture3D, TextureCube, Texture2DArray, TextureCubeArray
|
||||
};
|
||||
|
||||
enum class TextureFormat {
|
||||
Unknown, R8_UNORM, RG8_UNORM, RGBA8_UNORM, RGBA8_SRGB,
|
||||
R16_FLOAT, RG16_FLOAT, RGBA16_FLOAT,
|
||||
R32_FLOAT, RG32_FLOAT, RGBA32_FLOAT,
|
||||
D16_UNORM, D24_UNORM_S8_UINT, D32_FLOAT, D32_FLOAT_S8_X24_UINT,
|
||||
BC1_UNORM, BC1_UNORM_SRGB, BC2_UNORM, BC2_UNORM_SRGB,
|
||||
BC3_UNORM, BC3_UNORM_SRGB, BC4_UNORM, BC5_UNORM, BC6H_UF16,
|
||||
BC7_UNORM, BC7_UNORM_SRGB
|
||||
};
|
||||
|
||||
enum class TextureUsage : Core::uint8 {
|
||||
None = 0, ShaderResource = 1 << 0, RenderTarget = 1 << 1,
|
||||
DepthStencil = 1 << 2, UnorderedAccess = 1 << 3,
|
||||
TransferSrc = 1 << 4, TransferDst = 1 << 5
|
||||
};
|
||||
|
||||
class Texture : public IResource {
|
||||
public:
|
||||
Texture();
|
||||
virtual ~Texture() override;
|
||||
|
||||
ResourceType GetType() const override { return ResourceType::Texture; }
|
||||
const Containers::String& GetName() const override { return m_name; }
|
||||
const Containers::String& GetPath() const override { return m_path; }
|
||||
ResourceGUID GetGUID() const override { return m_guid; }
|
||||
bool IsValid() const override { return m_isValid; }
|
||||
size_t GetMemorySize() const override { return m_memorySize; }
|
||||
void Release() override;
|
||||
|
||||
Core::uint32 GetWidth() const { return m_width; }
|
||||
Core::uint32 GetHeight() const { return m_height; }
|
||||
Core::uint32 GetDepth() const { return m_depth; }
|
||||
Core::uint32 GetMipLevels() const { return m_mipLevels; }
|
||||
Core::uint32 GetArraySize() const { return m_arraySize; }
|
||||
TextureType GetTextureType() const { return m_textureType; }
|
||||
TextureFormat GetFormat() const { return m_format; }
|
||||
TextureUsage GetUsage() const { return m_usage; }
|
||||
|
||||
const void* GetPixelData() const { return m_pixelData.Data(); }
|
||||
size_t GetPixelDataSize() const { return m_pixelData.Size(); }
|
||||
|
||||
bool Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
|
||||
Core::uint32 mipLevels, TextureType type, TextureFormat format,
|
||||
const void* data, size_t dataSize, Core::uint32 arraySize = 1);
|
||||
bool GenerateMipmaps();
|
||||
|
||||
private:
|
||||
Core::uint32 m_width = 0;
|
||||
Core::uint32 m_height = 0;
|
||||
Core::uint32 m_depth = 1;
|
||||
Core::uint32 m_mipLevels = 1;
|
||||
Core::uint32 m_arraySize = 1;
|
||||
TextureType m_textureType = TextureType::Texture2D;
|
||||
TextureFormat m_format = TextureFormat::RGBA8_UNORM;
|
||||
TextureUsage m_usage = TextureUsage::ShaderResource;
|
||||
|
||||
Containers::Array<Core::uint8> m_pixelData;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
|
||||
#include "TextureImportSettings.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
91
engine/Runtime/Resources/Texture/TextureImportSettings.h
Normal file
91
engine/Runtime/Resources/Texture/TextureImportSettings.h
Normal file
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/ImportSettings.h>
|
||||
#include "Texture.h"
|
||||
#include <XCEngine/Core/Math/Vector3.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
enum class MipmapFilter {
|
||||
Box,
|
||||
Kaiser
|
||||
};
|
||||
|
||||
enum class CompressionQuality {
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
Ultra
|
||||
};
|
||||
|
||||
class TextureImportSettings : public ImportSettings {
|
||||
public:
|
||||
TextureImportSettings();
|
||||
virtual ~TextureImportSettings() override;
|
||||
|
||||
Core::UniqueRef<ImportSettings> Clone() const override;
|
||||
bool LoadFromJSON(const Containers::String& json) override;
|
||||
Containers::String SaveToJSON() const override;
|
||||
|
||||
void SetTextureType(TextureType type) { m_textureType = type; }
|
||||
TextureType GetTextureType() const { return m_textureType; }
|
||||
|
||||
void SetTargetFormat(TextureFormat format) { m_targetFormat = format; }
|
||||
TextureFormat GetTargetFormat() const { return m_targetFormat; }
|
||||
|
||||
void SetGenerateMipmaps(bool generate) { m_generateMipmaps = generate; }
|
||||
bool GetGenerateMipmaps() const { return m_generateMipmaps; }
|
||||
|
||||
void SetMipmapFilter(MipmapFilter filter) { m_mipmapFilter = filter; }
|
||||
MipmapFilter GetMipmapFilter() const { return m_mipmapFilter; }
|
||||
|
||||
void SetMaxAnisotropy(Core::uint32 anisotropy) { m_maxAnisotropy = anisotropy; }
|
||||
Core::uint32 GetMaxAnisotropy() const { return m_maxAnisotropy; }
|
||||
|
||||
void SetSRGB(bool srgb) { m_sRGB = srgb; }
|
||||
bool GetSRGB() const { return m_sRGB; }
|
||||
|
||||
void SetFlipVertical(bool flip) { m_flipVertical = flip; }
|
||||
bool GetFlipVertical() const { return m_flipVertical; }
|
||||
|
||||
void SetFlipHorizontal(bool flip) { m_flipHorizontal = flip; }
|
||||
bool GetFlipHorizontal() const { return m_flipHorizontal; }
|
||||
|
||||
void SetBorderColor(const Math::Vector3& color) { m_borderColor = color; }
|
||||
const Math::Vector3& GetBorderColor() const { return m_borderColor; }
|
||||
|
||||
void SetCompressionQuality(CompressionQuality quality) { m_compressionQuality = quality; }
|
||||
CompressionQuality GetCompressionQuality() const { return m_compressionQuality; }
|
||||
|
||||
void SetUseHardwareCompression(bool use) { m_useHardwareCompression = use; }
|
||||
bool GetUseHardwareCompression() const { return m_useHardwareCompression; }
|
||||
|
||||
void SetMaxSize(Core::uint32 size) { m_maxSize = size; }
|
||||
Core::uint32 GetMaxSize() const { return m_maxSize; }
|
||||
|
||||
void SetGenerateNormalMap(bool generate) { m_generateNormalMap = generate; }
|
||||
bool GetGenerateNormalMap() const { return m_generateNormalMap; }
|
||||
|
||||
void SetNormalMapStrength(float strength) { m_normalMapStrength = strength; }
|
||||
float GetNormalMapStrength() const { return m_normalMapStrength; }
|
||||
|
||||
private:
|
||||
TextureType m_textureType = TextureType::Texture2D;
|
||||
TextureFormat m_targetFormat = TextureFormat::Unknown;
|
||||
bool m_generateMipmaps = true;
|
||||
MipmapFilter m_mipmapFilter = MipmapFilter::Box;
|
||||
Core::uint32 m_maxAnisotropy = 16;
|
||||
bool m_sRGB = false;
|
||||
bool m_flipVertical = false;
|
||||
bool m_flipHorizontal = false;
|
||||
Math::Vector3 m_borderColor = Math::Vector3::Zero();
|
||||
CompressionQuality m_compressionQuality = CompressionQuality::High;
|
||||
bool m_useHardwareCompression = true;
|
||||
Core::uint32 m_maxSize = 0;
|
||||
bool m_generateNormalMap = false;
|
||||
float m_normalMapStrength = 1.0f;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
||||
#include <XCEngine/Core/Asset/ArtifactContainer.h>
|
||||
#include "TextureLoader.h"
|
||||
#include "engine/Shared/Asset/ArtifactContainer/ArtifactContainer.h"
|
||||
#include <XCEngine/Core/Asset/ArtifactFormats.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
#include "TextureImportSettings.h"
|
||||
#include <stb_image.h>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
28
engine/Runtime/Resources/Texture/TextureLoader.h
Normal file
28
engine/Runtime/Resources/Texture/TextureLoader.h
Normal file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/ImportSettings.h>
|
||||
#include <XCEngine/Core/IO/IResourceLoader.h>
|
||||
#include "Texture.h"
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
class TextureLoader : public IResourceLoader {
|
||||
public:
|
||||
TextureLoader();
|
||||
virtual ~TextureLoader() override;
|
||||
|
||||
ResourceType GetResourceType() const override { return ResourceType::Texture; }
|
||||
Containers::Array<Containers::String> GetSupportedExtensions() const override;
|
||||
bool CanLoad(const Containers::String& path) const override;
|
||||
LoadResult Load(const Containers::String& path, const ImportSettings* settings = nullptr) override;
|
||||
LoadResult LoadFromMemory(
|
||||
const Containers::String& path,
|
||||
const void* data,
|
||||
size_t dataSize,
|
||||
const ImportSettings* settings = nullptr) const;
|
||||
ImportSettings* GetDefaultSettings() const override;
|
||||
};
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
@@ -1,10 +1,11 @@
|
||||
#include <XCEngine/Scene/ModelSceneInstantiation.h>
|
||||
#include "ModelSceneInstantiation.h"
|
||||
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
#include <XCEngine/Components/MeshFilterComponent.h>
|
||||
#include <XCEngine/Components/MeshRendererComponent.h>
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include "Scene.h"
|
||||
#include "engine/Runtime/Asset/AssetManager/ResourceManager.h"
|
||||
|
||||
namespace XCEngine {
|
||||
|
||||
37
engine/Runtime/Scene/ModelSceneInstantiation.h
Normal file
37
engine/Runtime/Scene/ModelSceneInstantiation.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <XCEngine/Core/Asset/AssetRef.h>
|
||||
#include <XCEngine/Core/Containers/String.h>
|
||||
#include <XCEngine/Resources/Model/Model.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
|
||||
namespace Components {
|
||||
class GameObject;
|
||||
class Scene;
|
||||
}
|
||||
|
||||
struct ModelSceneInstantiationResult {
|
||||
Components::GameObject* rootObject = nullptr;
|
||||
std::vector<Components::GameObject*> nodeObjects;
|
||||
std::vector<Components::GameObject*> meshObjects;
|
||||
};
|
||||
|
||||
bool InstantiateModelHierarchy(
|
||||
Components::Scene& scene,
|
||||
const Resources::Model& model,
|
||||
const Resources::AssetRef& modelAssetRef,
|
||||
Components::GameObject* parent = nullptr,
|
||||
ModelSceneInstantiationResult* outResult = nullptr,
|
||||
Containers::String* outErrorMessage = nullptr);
|
||||
|
||||
bool InstantiateModelHierarchy(
|
||||
Components::Scene& scene,
|
||||
const Containers::String& modelPath,
|
||||
Components::GameObject* parent = nullptr,
|
||||
ModelSceneInstantiationResult* outResult = nullptr,
|
||||
Containers::String* outErrorMessage = nullptr);
|
||||
|
||||
} // namespace XCEngine
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Scene/RuntimeLoop.h"
|
||||
#include "RuntimeLoop.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
47
engine/Runtime/Scene/RuntimeLoop.h
Normal file
47
engine/Runtime/Scene/RuntimeLoop.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "SceneRuntime.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
|
||||
class RuntimeLoop {
|
||||
public:
|
||||
struct Settings {
|
||||
float fixedDeltaTime = 1.0f / 50.0f;
|
||||
float maxFrameDeltaTime = 0.1f;
|
||||
uint32_t maxFixedStepsPerFrame = 4;
|
||||
};
|
||||
|
||||
explicit RuntimeLoop(Settings settings = {});
|
||||
|
||||
void SetSettings(const Settings& settings);
|
||||
const Settings& GetSettings() const { return m_settings; }
|
||||
|
||||
void Start(Scene* scene);
|
||||
void Stop();
|
||||
void ReplaceScene(Scene* scene);
|
||||
|
||||
void Tick(float deltaTime);
|
||||
void Pause();
|
||||
void Resume();
|
||||
void StepFrame();
|
||||
|
||||
bool IsRunning() const { return m_sceneRuntime.IsRunning(); }
|
||||
bool IsPaused() const { return m_paused; }
|
||||
Scene* GetScene() const { return m_sceneRuntime.GetScene(); }
|
||||
Physics::PhysicsWorld* GetPhysicsWorld() const { return m_sceneRuntime.GetPhysicsWorld(); }
|
||||
float GetFixedAccumulator() const { return m_fixedAccumulator; }
|
||||
|
||||
private:
|
||||
SceneRuntime m_sceneRuntime;
|
||||
Settings m_settings = {};
|
||||
float m_fixedAccumulator = 0.0f;
|
||||
bool m_paused = false;
|
||||
bool m_stepRequested = false;
|
||||
};
|
||||
|
||||
} // namespace Components
|
||||
} // namespace XCEngine
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "Scene/Scene.h"
|
||||
#include "Scene.h"
|
||||
|
||||
#include "Components/ComponentFactoryRegistry.h"
|
||||
#include "Debug/Logger.h"
|
||||
#include "Components/GameObject.h"
|
||||
#include "Components/TransformComponent.h"
|
||||
#include <sstream>
|
||||
#include "Debug/Logger.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace XCEngine {
|
||||
@@ -18,10 +20,6 @@ bool ShouldTraceSceneComponentPayload(const std::string& payload) {
|
||||
payload.find("New Material.mat") != std::string::npos;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
||||
struct PendingComponentData {
|
||||
std::string type;
|
||||
std::string payload;
|
||||
@@ -66,9 +64,15 @@ std::string UnescapeString(const std::string& value) {
|
||||
if (value[i] == '\\' && i + 1 < value.size()) {
|
||||
++i;
|
||||
switch (value[i]) {
|
||||
case 'n': unescaped.push_back('\n'); break;
|
||||
case 'r': unescaped.push_back('\r'); break;
|
||||
default: unescaped.push_back(value[i]); break;
|
||||
case 'n':
|
||||
unescaped.push_back('\n');
|
||||
break;
|
||||
case 'r':
|
||||
unescaped.push_back('\r');
|
||||
break;
|
||||
default:
|
||||
unescaped.push_back(value[i]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
unescaped.push_back(value[i]);
|
||||
@@ -155,21 +159,21 @@ Scene::~Scene() {
|
||||
GameObject* Scene::CreateGameObject(const std::string& name, GameObject* parent) {
|
||||
auto gameObject = std::make_unique<GameObject>(name);
|
||||
GameObject* ptr = gameObject.get();
|
||||
|
||||
|
||||
GameObject::GetGlobalRegistry()[ptr->m_id] = ptr;
|
||||
m_gameObjectIDs.insert(ptr->m_id);
|
||||
m_gameObjects.emplace(ptr->m_id, std::move(gameObject));
|
||||
ptr->m_scene = this;
|
||||
|
||||
|
||||
if (parent) {
|
||||
ptr->SetParent(parent);
|
||||
} else {
|
||||
m_rootGameObjects.push_back(ptr->m_id);
|
||||
}
|
||||
ptr->Awake();
|
||||
|
||||
|
||||
m_onGameObjectCreated.Invoke(ptr);
|
||||
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -188,14 +192,13 @@ void Scene::DestroyGameObject(GameObject* gameObject) {
|
||||
} else {
|
||||
m_rootGameObjects.erase(
|
||||
std::remove(m_rootGameObjects.begin(), m_rootGameObjects.end(), gameObject->m_id),
|
||||
m_rootGameObjects.end()
|
||||
);
|
||||
m_rootGameObjects.end());
|
||||
}
|
||||
|
||||
m_onGameObjectDestroyed.Invoke(gameObject);
|
||||
|
||||
|
||||
gameObject->OnDestroy();
|
||||
|
||||
|
||||
m_gameObjectIDs.erase(gameObject->m_id);
|
||||
GameObject::GetGlobalRegistry().erase(gameObject->m_id);
|
||||
m_gameObjects.erase(gameObject->m_id);
|
||||
@@ -301,7 +304,9 @@ void Scene::DeserializeFromString(const std::string& data) {
|
||||
GameObject::ID maxId = 0;
|
||||
|
||||
while (std::getline(input, line)) {
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line == "gameobject_begin") {
|
||||
pendingObjects.emplace_back();
|
||||
@@ -385,16 +390,17 @@ void Scene::DeserializeFromString(const std::string& data) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[Scene] Deserialize component objectId=") +
|
||||
Containers::String(std::to_string(pending.id).c_str()) +
|
||||
" name=" +
|
||||
Containers::String(pending.name.c_str()) +
|
||||
" type=" +
|
||||
Containers::String(componentData.type.c_str()) +
|
||||
" payload=" +
|
||||
Containers::String(componentData.payload.c_str()));
|
||||
Containers::String(std::to_string(pending.id).c_str()) +
|
||||
" name=" +
|
||||
Containers::String(pending.name.c_str()) +
|
||||
" type=" +
|
||||
Containers::String(componentData.type.c_str()) +
|
||||
" payload=" +
|
||||
Containers::String(componentData.payload.c_str()));
|
||||
}
|
||||
|
||||
if (Component* component = ComponentFactoryRegistry::Get().CreateComponent(go.get(), componentData.type)) {
|
||||
if (Component* component =
|
||||
ComponentFactoryRegistry::Get().CreateComponent(go.get(), componentData.type)) {
|
||||
if (!componentData.payload.empty()) {
|
||||
std::istringstream componentStream(componentData.payload);
|
||||
component->Deserialize(componentStream);
|
||||
@@ -467,9 +473,9 @@ void Scene::Load(const std::string& filePath) {
|
||||
Debug::Logger::Get().Info(
|
||||
Debug::LogCategory::FileSystem,
|
||||
Containers::String("[Scene] Load bytes=") +
|
||||
Containers::String(std::to_string(buffer.str().size()).c_str()) +
|
||||
" file=" +
|
||||
Containers::String(filePath.c_str()));
|
||||
Containers::String(std::to_string(buffer.str().size()).c_str()) +
|
||||
" file=" +
|
||||
Containers::String(filePath.c_str()));
|
||||
DeserializeFromString(buffer.str());
|
||||
}
|
||||
|
||||
122
engine/Runtime/Scene/Scene.h
Normal file
122
engine/Runtime/Scene/Scene.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
|
||||
#include <XCEngine/Core/Event.h>
|
||||
#include <XCEngine/Components/GameObject.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
|
||||
class Scene {
|
||||
public:
|
||||
using GameObjectID = uint64_t;
|
||||
static constexpr GameObjectID INVALID_GAMEOBJECT_ID = 0;
|
||||
|
||||
Scene();
|
||||
explicit Scene(const std::string& name);
|
||||
~Scene();
|
||||
|
||||
const std::string& GetName() const { return m_name; }
|
||||
void SetName(const std::string& name) { m_name = name; }
|
||||
|
||||
bool IsActive() const { return m_active; }
|
||||
void SetActive(bool active) { m_active = active; }
|
||||
|
||||
GameObject* CreateGameObject(const std::string& name, GameObject* parent = nullptr);
|
||||
void DestroyGameObject(GameObject* gameObject);
|
||||
|
||||
GameObject* Find(const std::string& name) const;
|
||||
GameObject* FindByID(GameObjectID id) const;
|
||||
GameObject* FindGameObjectWithTag(const std::string& tag) const;
|
||||
|
||||
template<typename T>
|
||||
T* FindObjectOfType() const {
|
||||
for (auto* go : GetRootGameObjects()) {
|
||||
if (T* comp = go->GetComponent<T>()) {
|
||||
return comp;
|
||||
}
|
||||
if (T* comp = FindInChildren<T>(go)) {
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T*> FindObjectsOfType() const {
|
||||
std::vector<T*> results;
|
||||
for (auto* go : GetRootGameObjects()) {
|
||||
if (T* comp = go->GetComponent<T>()) {
|
||||
results.push_back(comp);
|
||||
}
|
||||
FindInChildren<T>(go, results);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<GameObject*> GetRootGameObjects() const;
|
||||
|
||||
void Update(float deltaTime);
|
||||
void FixedUpdate(float fixedDeltaTime);
|
||||
void LateUpdate(float deltaTime);
|
||||
|
||||
void Save(const std::string& filePath);
|
||||
void Load(const std::string& filePath);
|
||||
std::string SerializeToString() const;
|
||||
void DeserializeFromString(const std::string& data);
|
||||
|
||||
Core::Event<GameObject*>& OnGameObjectCreated() { return m_onGameObjectCreated; }
|
||||
Core::Event<GameObject*>& OnGameObjectDestroyed() { return m_onGameObjectDestroyed; }
|
||||
Core::Event<GameObject*, Component*>& OnComponentAdded() { return m_onComponentAdded; }
|
||||
Core::Event<GameObject*, Component*>& OnComponentRemoved() { return m_onComponentRemoved; }
|
||||
|
||||
private:
|
||||
GameObject* FindInChildren(GameObject* parent, const std::string& name) const;
|
||||
|
||||
template<typename T>
|
||||
T* FindInChildren(GameObject* parent) const {
|
||||
for (size_t i = 0; i < parent->GetChildCount(); ++i) {
|
||||
GameObject* child = parent->GetChild(i);
|
||||
if (T* comp = child->GetComponent<T>()) {
|
||||
return comp;
|
||||
}
|
||||
if (T* comp = FindInChildren<T>(child)) {
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void FindInChildren(GameObject* parent, std::vector<T*>& results) const {
|
||||
for (size_t i = 0; i < parent->GetChildCount(); ++i) {
|
||||
GameObject* child = parent->GetChild(i);
|
||||
if (T* comp = child->GetComponent<T>()) {
|
||||
results.push_back(comp);
|
||||
}
|
||||
FindInChildren<T>(child, results);
|
||||
}
|
||||
}
|
||||
|
||||
std::string m_name;
|
||||
bool m_active = true;
|
||||
std::unordered_map<GameObjectID, std::unique_ptr<GameObject>> m_gameObjects;
|
||||
std::vector<GameObjectID> m_rootGameObjects;
|
||||
std::unordered_set<GameObjectID> m_gameObjectIDs;
|
||||
|
||||
Core::Event<GameObject*> m_onGameObjectCreated;
|
||||
Core::Event<GameObject*> m_onGameObjectDestroyed;
|
||||
Core::Event<GameObject*, Component*> m_onComponentAdded;
|
||||
Core::Event<GameObject*, Component*> m_onComponentRemoved;
|
||||
|
||||
friend class GameObject;
|
||||
friend class SceneManager;
|
||||
};
|
||||
|
||||
} // namespace Components
|
||||
} // namespace XCEngine
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user