相机更新
This commit is contained in:
parent
bcec8b6a8d
commit
97b704428a
@ -4350,6 +4350,7 @@ public:
|
||||
[[nodiscard]] bool OpenProject(const std::filesystem::path& projectRoot) override;
|
||||
[[nodiscard]] bool RenamePath(const std::filesystem::path& relativePath, const std::filesystem::path& newName) override;
|
||||
[[nodiscard]] bool MovePath(const std::filesystem::path& relativePath, const std::filesystem::path& targetDirectory) override;
|
||||
[[nodiscard]] bool MovePathTo(const std::filesystem::path& relativePath, const std::filesystem::path& targetRelativePath) override;
|
||||
[[nodiscard]] bool DeletePath(const std::filesystem::path& relativePath) override;
|
||||
bool ApplyExternalFileChanges(const std::vector<MetaCoreAssetChangeEvent>& changes) override;
|
||||
[[nodiscard]] const std::vector<MetaCoreAssetRecord>& GetAssetRegistry() const override { return AssetRecords_; }
|
||||
@ -4403,6 +4404,8 @@ public:
|
||||
|
||||
bool Refresh() override;
|
||||
bool CreateFolder(const std::filesystem::path& relativeDirectory) override;
|
||||
bool CreateMaterialAsset(const std::filesystem::path& relativeMaterialPath) override;
|
||||
bool CreateEmptySceneAsset(const std::filesystem::path& relativeScenePath, bool makeStartupScene) override;
|
||||
bool RegisterScenePath(const std::filesystem::path& relativeScenePath, bool makeStartupScene) override;
|
||||
bool SetStartupScenePath(const std::filesystem::path& relativeScenePath) override;
|
||||
|
||||
@ -4570,49 +4573,7 @@ bool MetaCoreBuiltinAssetDatabaseService::RenamePath(
|
||||
|
||||
const std::filesystem::path normalizedPath = relativePath.lexically_normal();
|
||||
const std::filesystem::path targetPath = (normalizedPath.parent_path() / newName).lexically_normal();
|
||||
if (MetaCoreIsUnsafeRelativePath(targetPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path absoluteSourcePath = Project_.RootPath / normalizedPath;
|
||||
const std::filesystem::path absoluteTargetPath = Project_.RootPath / targetPath;
|
||||
if (!std::filesystem::exists(absoluteSourcePath) || std::filesystem::exists(absoluteTargetPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
std::filesystem::rename(absoluteSourcePath, absoluteTargetPath, errorCode);
|
||||
if (errorCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MetaCoreMoveSidecarIfPresent(Project_.RootPath, normalizedPath, targetPath);
|
||||
MetaCoreUpdateMovedMetadata(Project_.RootPath, normalizedPath, targetPath);
|
||||
|
||||
const auto remapPath = [&](std::filesystem::path& pathValue) {
|
||||
if (pathValue == normalizedPath) {
|
||||
pathValue = targetPath;
|
||||
} else {
|
||||
const auto normalizedValue = pathValue.lexically_normal();
|
||||
const auto normalizedString = normalizedValue.generic_string();
|
||||
const auto sourcePrefix = normalizedPath.generic_string() + "/";
|
||||
if (normalizedString.rfind(sourcePrefix, 0) == 0) {
|
||||
pathValue = (targetPath / normalizedValue.lexically_relative(normalizedPath)).lexically_normal();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
remapPath(Project_.StartupScenePath);
|
||||
for (auto& scenePath : Project_.ScenePaths) {
|
||||
remapPath(scenePath);
|
||||
}
|
||||
std::sort(Project_.ScenePaths.begin(), Project_.ScenePaths.end());
|
||||
Project_.ScenePaths.erase(std::unique(Project_.ScenePaths.begin(), Project_.ScenePaths.end()), Project_.ScenePaths.end());
|
||||
SaveProjectDescriptor();
|
||||
|
||||
const bool refreshed = Refresh();
|
||||
RescanHotReloadBaseline();
|
||||
return refreshed;
|
||||
return MovePathTo(normalizedPath, targetPath);
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetDatabaseService::MovePath(
|
||||
@ -4633,42 +4594,65 @@ bool MetaCoreBuiltinAssetDatabaseService::MovePath(
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::filesystem::path absoluteSourcePath = Project_.RootPath / normalizedSourcePath;
|
||||
const std::filesystem::path absoluteTargetDirectory = Project_.RootPath / normalizedTargetDirectory;
|
||||
if (!std::filesystem::exists(absoluteSourcePath) || !std::filesystem::exists(absoluteTargetDirectory)) {
|
||||
if (!std::filesystem::exists(absoluteTargetDirectory) || !std::filesystem::is_directory(absoluteTargetDirectory)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path targetPath = (normalizedTargetDirectory / normalizedSourcePath.filename()).lexically_normal();
|
||||
if (MetaCoreIsUnsafeRelativePath(targetPath) || std::filesystem::exists(Project_.RootPath / targetPath)) {
|
||||
return MovePathTo(normalizedSourcePath, targetPath);
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetDatabaseService::MovePathTo(
|
||||
const std::filesystem::path& relativePath,
|
||||
const std::filesystem::path& targetRelativePath
|
||||
) {
|
||||
if (!HasProject() ||
|
||||
MetaCoreIsUnsafeRelativePath(relativePath) ||
|
||||
MetaCoreIsUnsafeRelativePath(targetRelativePath) ||
|
||||
relativePath.empty() ||
|
||||
targetRelativePath.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto remapPath = [&](std::filesystem::path& pathValue) {
|
||||
if (pathValue == normalizedSourcePath) {
|
||||
pathValue = targetPath;
|
||||
} else {
|
||||
const auto normalizedValue = pathValue.lexically_normal();
|
||||
const auto normalizedString = normalizedValue.generic_string();
|
||||
const auto sourcePrefix = normalizedSourcePath.generic_string() + "/";
|
||||
if (normalizedString.rfind(sourcePrefix, 0) == 0) {
|
||||
pathValue = (targetPath / normalizedValue.lexically_relative(normalizedSourcePath)).lexically_normal();
|
||||
}
|
||||
const std::filesystem::path normalizedSourcePath = relativePath.lexically_normal();
|
||||
const std::filesystem::path normalizedTargetPath = targetRelativePath.lexically_normal();
|
||||
if (normalizedSourcePath == normalizedTargetPath) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::filesystem::path absoluteSourcePath = Project_.RootPath / normalizedSourcePath;
|
||||
const std::filesystem::path absoluteTargetPath = Project_.RootPath / normalizedTargetPath;
|
||||
if (!std::filesystem::exists(absoluteSourcePath) || std::filesystem::exists(absoluteTargetPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::filesystem::is_directory(absoluteSourcePath)) {
|
||||
const std::string sourcePrefix = normalizedSourcePath.generic_string() + "/";
|
||||
const std::string targetString = normalizedTargetPath.generic_string() + "/";
|
||||
if (targetString.rfind(sourcePrefix, 0) == 0) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
std::filesystem::rename(absoluteSourcePath, Project_.RootPath / targetPath, errorCode);
|
||||
std::filesystem::create_directories(absoluteTargetPath.parent_path(), errorCode);
|
||||
if (errorCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MetaCoreMoveSidecarIfPresent(Project_.RootPath, normalizedSourcePath, targetPath);
|
||||
MetaCoreUpdateMovedMetadata(Project_.RootPath, normalizedSourcePath, targetPath);
|
||||
errorCode.clear();
|
||||
std::filesystem::rename(absoluteSourcePath, absoluteTargetPath, errorCode);
|
||||
if (errorCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
remapPath(Project_.StartupScenePath);
|
||||
MetaCoreMoveSidecarIfPresent(Project_.RootPath, normalizedSourcePath, normalizedTargetPath);
|
||||
MetaCoreUpdateMovedMetadata(Project_.RootPath, normalizedSourcePath, normalizedTargetPath);
|
||||
|
||||
Project_.StartupScenePath = MetaCoreRemapRelativePath(Project_.StartupScenePath, normalizedSourcePath, normalizedTargetPath);
|
||||
for (auto& scenePath : Project_.ScenePaths) {
|
||||
remapPath(scenePath);
|
||||
scenePath = MetaCoreRemapRelativePath(scenePath, normalizedSourcePath, normalizedTargetPath);
|
||||
}
|
||||
std::sort(Project_.ScenePaths.begin(), Project_.ScenePaths.end());
|
||||
Project_.ScenePaths.erase(std::unique(Project_.ScenePaths.begin(), Project_.ScenePaths.end()), Project_.ScenePaths.end());
|
||||
@ -5031,6 +5015,108 @@ bool MetaCoreBuiltinAssetDatabaseService::CreateFolder(const std::filesystem::pa
|
||||
return std::filesystem::create_directories(Project_.RootPath / relativeDirectory.lexically_normal());
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetDatabaseService::CreateMaterialAsset(const std::filesystem::path& relativeMaterialPath) {
|
||||
if (!HasProject() || ReflectionRegistry_ == nullptr || MetaCoreIsUnsafeRelativePath(relativeMaterialPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path normalizedPath = relativeMaterialPath.lexically_normal();
|
||||
if (!MetaCoreIsMaterialPath(normalizedPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path absolutePath = Project_.RootPath / normalizedPath;
|
||||
if (std::filesystem::exists(absolutePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
std::filesystem::create_directories(absolutePath.parent_path(), errorCode);
|
||||
if (errorCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MetaCoreMaterialAssetDocument materialDocument;
|
||||
materialDocument.AssetGuid = MetaCoreAssetGuid::Generate();
|
||||
materialDocument.Name = normalizedPath.stem().stem().string();
|
||||
|
||||
if (!MetaCoreSceneSerializer::SaveMaterialToJson(absolutePath, materialDocument, ReflectionRegistry_->GetTypeRegistry())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MetaCoreAssetMetadataDocument metadata;
|
||||
metadata.AssetGuid = materialDocument.AssetGuid;
|
||||
metadata.AssetType = "material";
|
||||
metadata.ImporterId = "MaterialImporter";
|
||||
metadata.SourcePath = normalizedPath.generic_string();
|
||||
metadata.PackagePath = normalizedPath.generic_string();
|
||||
metadata.SourceHash = MetaCoreHashFile(absolutePath).value_or(0);
|
||||
if (!MetaCoreWriteMetaJson(MetaCoreBuildMetaPath(absolutePath), metadata)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool refreshed = Refresh();
|
||||
RescanHotReloadBaseline();
|
||||
return refreshed;
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetDatabaseService::CreateEmptySceneAsset(
|
||||
const std::filesystem::path& relativeScenePath,
|
||||
bool makeStartupScene
|
||||
) {
|
||||
if (!HasProject() || ReflectionRegistry_ == nullptr || MetaCoreIsUnsafeRelativePath(relativeScenePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path normalizedPath = relativeScenePath.lexically_normal();
|
||||
if (!MetaCoreIsScenePath(normalizedPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path absolutePath = Project_.RootPath / normalizedPath;
|
||||
if (std::filesystem::exists(absolutePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code errorCode;
|
||||
std::filesystem::create_directories(absolutePath.parent_path(), errorCode);
|
||||
if (errorCode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const MetaCoreAssetGuid sceneGuid = MetaCoreAssetGuid::Generate();
|
||||
MetaCoreSceneDocument sceneDocument;
|
||||
sceneDocument.Name = MetaCoreSceneDisplayNameFromPath(normalizedPath);
|
||||
sceneDocument.GameObjects = {};
|
||||
if (!MetaCoreSceneSerializer::SaveSceneToJson(absolutePath, sceneDocument, ReflectionRegistry_->GetTypeRegistry())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MetaCoreAssetMetadataDocument metadata;
|
||||
metadata.AssetGuid = sceneGuid;
|
||||
metadata.AssetType = "scene";
|
||||
metadata.ImporterId = "SceneImporter";
|
||||
metadata.SourcePath = normalizedPath.generic_string();
|
||||
metadata.PackagePath = normalizedPath.generic_string();
|
||||
metadata.SourceHash = MetaCoreHashFile(absolutePath).value_or(0);
|
||||
if (!MetaCoreWriteMetaJson(MetaCoreBuildMetaPath(absolutePath), metadata)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::find(Project_.ScenePaths.begin(), Project_.ScenePaths.end(), normalizedPath) == Project_.ScenePaths.end()) {
|
||||
Project_.ScenePaths.push_back(normalizedPath);
|
||||
std::sort(Project_.ScenePaths.begin(), Project_.ScenePaths.end());
|
||||
}
|
||||
if (makeStartupScene) {
|
||||
Project_.StartupScenePath = normalizedPath;
|
||||
}
|
||||
SaveProjectDescriptor();
|
||||
|
||||
const bool refreshed = Refresh();
|
||||
RescanHotReloadBaseline();
|
||||
return refreshed;
|
||||
}
|
||||
|
||||
bool MetaCoreBuiltinAssetDatabaseService::RegisterScenePath(
|
||||
const std::filesystem::path& relativeScenePath,
|
||||
bool makeStartupScene
|
||||
@ -6227,6 +6313,11 @@ public:
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const std::filesystem::path& relativePrefabPath
|
||||
) override;
|
||||
[[nodiscard]] std::optional<std::filesystem::path> CreatePrefabFromObject(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreId objectId,
|
||||
const std::filesystem::path& relativePrefabPath
|
||||
) override;
|
||||
[[nodiscard]] std::optional<MetaCoreId> InstantiatePrefab(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreAssetGuid& prefabAssetGuid,
|
||||
@ -7490,6 +7581,14 @@ std::optional<MetaCorePrefabDocument> MetaCoreBuiltinPrefabService::LoadPrefabDo
|
||||
std::optional<std::filesystem::path> MetaCoreBuiltinPrefabService::CreatePrefabFromSelection(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const std::filesystem::path& relativePrefabPath
|
||||
) {
|
||||
return CreatePrefabFromObject(editorContext, editorContext.GetActiveObjectId(), relativePrefabPath);
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> MetaCoreBuiltinPrefabService::CreatePrefabFromObject(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreId objectId,
|
||||
const std::filesystem::path& relativePrefabPath
|
||||
) {
|
||||
if (AssetDatabaseService_ == nullptr ||
|
||||
ReflectionRegistry_ == nullptr ||
|
||||
@ -7498,19 +7597,18 @@ std::optional<std::filesystem::path> MetaCoreBuiltinPrefabService::CreatePrefabF
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const MetaCoreId activeObjectId = editorContext.GetActiveObjectId();
|
||||
if (activeObjectId == 0 || !editorContext.GetScene().FindGameObject(activeObjectId)) {
|
||||
if (objectId == 0 || !editorContext.GetScene().FindGameObject(objectId)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<MetaCoreGameObject> prefabObjects = MetaCoreCaptureSubtree(editorContext.GetScene(), activeObjectId);
|
||||
std::vector<MetaCoreGameObject> prefabObjects = MetaCoreCaptureSubtree(editorContext.GetScene(), objectId);
|
||||
if (prefabObjects.empty()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MetaCoreNormalizePrefabSubtree(prefabObjects, activeObjectId);
|
||||
MetaCoreNormalizePrefabSubtree(prefabObjects, objectId);
|
||||
|
||||
const MetaCoreGameObject rootObject = editorContext.GetScene().FindGameObject(activeObjectId);
|
||||
const MetaCoreGameObject rootObject = editorContext.GetScene().FindGameObject(objectId);
|
||||
const std::filesystem::path normalizedPrefabPath = relativePrefabPath.lexically_normal();
|
||||
const std::filesystem::path absolutePrefabPath = AssetDatabaseService_->GetProjectDescriptor().RootPath / normalizedPrefabPath;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -483,7 +483,7 @@ void MetaCoreDrawInfernuxStatusBar(MetaCoreEditorContext& editorContext, const I
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreProjectAssetDragDropPayload& payload
|
||||
) {
|
||||
if (payload.StorageKind != MetaCoreAssetStorageKind::SourcePackage) {
|
||||
if (payload.IsDirectory || !payload.AssetGuid.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -525,8 +525,18 @@ void MetaCoreDrawSceneViewportDropTarget(
|
||||
const ImVec2& viewportSize
|
||||
) {
|
||||
const ImGuiPayload* dragDropPayload = ImGui::GetDragDropPayload();
|
||||
const bool projectAssetDragActive =
|
||||
dragDropPayload != nullptr && dragDropPayload->IsDataType(MetaCoreProjectAssetDragDropPayloadType);
|
||||
bool projectAssetDragActive =
|
||||
dragDropPayload != nullptr &&
|
||||
dragDropPayload->IsDataType(MetaCoreProjectAssetDragDropPayloadType) &&
|
||||
dragDropPayload->DataSize == sizeof(MetaCoreProjectAssetDragDropPayload);
|
||||
if (projectAssetDragActive) {
|
||||
const auto* projectAssetPayload = static_cast<const MetaCoreProjectAssetDragDropPayload*>(dragDropPayload->Data);
|
||||
const std::string assetType = projectAssetPayload == nullptr ? std::string{} : std::string(projectAssetPayload->AssetType);
|
||||
projectAssetDragActive =
|
||||
projectAssetPayload != nullptr &&
|
||||
!projectAssetPayload->IsDirectory &&
|
||||
(assetType == "model" || assetType == "prefab");
|
||||
}
|
||||
if (!projectAssetDragActive) {
|
||||
return;
|
||||
}
|
||||
@ -1130,57 +1140,7 @@ bool MetaCoreEditorApp::Initialize(int argc, char* argv[]) {
|
||||
|
||||
MetaCoreTraceStartup("metacore.app: initialize message handler");
|
||||
#if defined(_WIN32)
|
||||
DragAcceptFiles(static_cast<HWND>(Window_.GetNativeWindowHandle()), TRUE);
|
||||
|
||||
// 允许低权限的 Windows 资源管理器 (Explorer) 向高权限的编辑器进程发送拖放消息,彻底解决拖拽禁止图标的系统底层限制
|
||||
#ifndef MSGFLT_ADD
|
||||
#define MSGFLT_ADD 1
|
||||
#endif
|
||||
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
|
||||
ChangeWindowMessageFilter(0x0049, MSGFLT_ADD); // WM_COPYGLOBALMEM
|
||||
|
||||
Window_.SetNativeWindowMessageHandler([this](void* nativeWindowHandle, unsigned int message, std::uintptr_t wparam, std::intptr_t lparam) {
|
||||
if (message == WM_DROPFILES) {
|
||||
HDROP hDrop = reinterpret_cast<HDROP>(wparam);
|
||||
UINT fileCount = DragQueryFileW(hDrop, 0xFFFFFFFF, nullptr, 0);
|
||||
const auto assetDatabaseService = ModuleRegistry_.ResolveService<MetaCoreIAssetDatabaseService>();
|
||||
const auto importPipelineService = ModuleRegistry_.ResolveService<MetaCoreIImportPipelineService>();
|
||||
if (assetDatabaseService != nullptr && assetDatabaseService->HasProject() && importPipelineService != nullptr) {
|
||||
const std::filesystem::path assetsPath = assetDatabaseService->GetProjectDescriptor().AssetsPath;
|
||||
|
||||
// 【修复外部拖入目录问题】:如果当前编辑器选中了特定子文件夹目录(例如 Assets/Models),则应当拷贝入该子目录中而不是根目录
|
||||
std::filesystem::path targetFolder = assetsPath;
|
||||
if (EditorContext_ != nullptr) {
|
||||
const std::filesystem::path selectedDir = EditorContext_->GetSelectedProjectDirectory();
|
||||
if (!selectedDir.empty()) {
|
||||
const std::filesystem::path candidate = assetDatabaseService->GetProjectDescriptor().RootPath / selectedDir;
|
||||
if (std::filesystem::exists(candidate)) {
|
||||
targetFolder = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool importedAny = false;
|
||||
for (UINT i = 0; i < fileCount; ++i) {
|
||||
wchar_t filePath[MAX_PATH];
|
||||
if (DragQueryFileW(hDrop, i, filePath, MAX_PATH)) {
|
||||
std::filesystem::path sourcePath(filePath);
|
||||
std::filesystem::path targetPath = targetFolder / sourcePath.filename();
|
||||
try {
|
||||
// 使用 overwrite_existing 允许外部重构模型直接拖入刷新,触发引擎自动重导入并更新引用
|
||||
std::filesystem::copy_file(sourcePath, targetPath, std::filesystem::copy_options::overwrite_existing);
|
||||
importedAny = true;
|
||||
} catch (...) { }
|
||||
}
|
||||
}
|
||||
if (importedAny) {
|
||||
(void)importPipelineService->RefreshImports();
|
||||
(void)assetDatabaseService->Refresh();
|
||||
}
|
||||
}
|
||||
DragFinish(hDrop);
|
||||
return true;
|
||||
}
|
||||
Window_.SetNativeWindowMessageHandler([](void* nativeWindowHandle, unsigned int message, std::uintptr_t wparam, std::intptr_t lparam) {
|
||||
return ImGui_ImplWin32_WndProcHandler(static_cast<HWND>(nativeWindowHandle), message, static_cast<WPARAM>(wparam), static_cast<LPARAM>(lparam)) != 0;
|
||||
});
|
||||
#endif
|
||||
|
||||
@ -70,7 +70,7 @@ struct MetaCoreSelectedAssetState {
|
||||
MetaCoreAssetStorageKind StorageKind = MetaCoreAssetStorageKind::SourcePackage;
|
||||
|
||||
[[nodiscard]] bool IsValid() const {
|
||||
return Guid.IsValid();
|
||||
return Guid.IsValid() || !RelativePath.empty();
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
@ -87,6 +87,8 @@ struct MetaCoreProjectAssetDragDropPayload {
|
||||
MetaCoreAssetGuid AssetGuid{};
|
||||
MetaCoreAssetStorageKind StorageKind = MetaCoreAssetStorageKind::SourcePackage;
|
||||
char AssetType[32]{};
|
||||
char RelativePath[512]{};
|
||||
bool IsDirectory = false;
|
||||
};
|
||||
|
||||
inline constexpr const char* MetaCoreGeneratedMaterialDragDropPayloadType = "MC_GENERATED_MATERIAL_ASSET";
|
||||
|
||||
@ -142,6 +142,7 @@ public:
|
||||
[[nodiscard]] virtual bool OpenProject(const std::filesystem::path& projectRoot) = 0;
|
||||
[[nodiscard]] virtual bool RenamePath(const std::filesystem::path& relativePath, const std::filesystem::path& newName) = 0;
|
||||
[[nodiscard]] virtual bool MovePath(const std::filesystem::path& relativePath, const std::filesystem::path& targetDirectory) = 0;
|
||||
[[nodiscard]] virtual bool MovePathTo(const std::filesystem::path& relativePath, const std::filesystem::path& targetRelativePath) = 0;
|
||||
[[nodiscard]] virtual bool DeletePath(const std::filesystem::path& relativePath) = 0;
|
||||
[[nodiscard]] virtual std::vector<std::filesystem::path> GetDirectoriesUnder(const std::filesystem::path& relativeDirectory) const = 0;
|
||||
[[nodiscard]] virtual std::vector<MetaCoreAssetRecord> GetAssetsUnder(const std::filesystem::path& relativeDirectory) const = 0;
|
||||
@ -149,6 +150,8 @@ public:
|
||||
virtual bool ApplyExternalFileChanges(const std::vector<MetaCoreAssetChangeEvent>& changes) = 0;
|
||||
virtual bool Refresh() = 0;
|
||||
virtual bool CreateFolder(const std::filesystem::path& relativeDirectory) = 0;
|
||||
virtual bool CreateMaterialAsset(const std::filesystem::path& relativeMaterialPath) = 0;
|
||||
virtual bool CreateEmptySceneAsset(const std::filesystem::path& relativeScenePath, bool makeStartupScene) = 0;
|
||||
virtual bool RegisterScenePath(const std::filesystem::path& relativeScenePath, bool makeStartupScene) = 0;
|
||||
virtual bool SetStartupScenePath(const std::filesystem::path& relativeScenePath) = 0;
|
||||
};
|
||||
@ -280,6 +283,11 @@ public:
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const std::filesystem::path& relativePrefabPath
|
||||
) = 0;
|
||||
[[nodiscard]] virtual std::optional<std::filesystem::path> CreatePrefabFromObject(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
MetaCoreId objectId,
|
||||
const std::filesystem::path& relativePrefabPath
|
||||
) = 0;
|
||||
[[nodiscard]] virtual std::optional<MetaCoreId> InstantiatePrefab(
|
||||
MetaCoreEditorContext& editorContext,
|
||||
const MetaCoreAssetGuid& prefabAssetGuid,
|
||||
|
||||
@ -4,16 +4,19 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
@ -92,6 +95,7 @@ public:
|
||||
HWND NativeHandle = nullptr;
|
||||
MetaCoreInput Input{};
|
||||
MetaCoreNativeWindowMessageHandler MessageHandler{};
|
||||
std::vector<std::filesystem::path> DroppedFiles{};
|
||||
WNDPROC OriginalWindowProc = nullptr;
|
||||
std::chrono::steady_clock::time_point LastFrameTime = std::chrono::steady_clock::now();
|
||||
float DeltaSeconds = 1.0F / 60.0F;
|
||||
@ -107,6 +111,25 @@ LRESULT CALLBACK MetaCoreWindowSubclassProc(HWND hwnd, UINT msg, WPARAM wparam,
|
||||
}
|
||||
|
||||
MetaCoreWindow::MetaCoreWindowImpl& owner = *iterator->second;
|
||||
|
||||
if (msg == WM_DROPFILES) {
|
||||
HDROP dropHandle = reinterpret_cast<HDROP>(wparam);
|
||||
const UINT fileCount = DragQueryFileW(dropHandle, 0xFFFFFFFF, nullptr, 0);
|
||||
for (UINT index = 0; index < fileCount; ++index) {
|
||||
const UINT length = DragQueryFileW(dropHandle, index, nullptr, 0);
|
||||
if (length == 0) {
|
||||
continue;
|
||||
}
|
||||
std::wstring buffer(static_cast<std::size_t>(length) + 1, L'\0');
|
||||
if (DragQueryFileW(dropHandle, index, buffer.data(), length + 1) != 0) {
|
||||
buffer.resize(length);
|
||||
owner.DroppedFiles.emplace_back(buffer);
|
||||
}
|
||||
}
|
||||
DragFinish(dropHandle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool handledByEditor = false;
|
||||
if (owner.MessageHandler) {
|
||||
handledByEditor = owner.MessageHandler(hwnd, msg, static_cast<std::uintptr_t>(wparam), static_cast<std::intptr_t>(lparam));
|
||||
@ -186,6 +209,12 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
|
||||
MetaCoreWindowTrace("metacore.window: window created");
|
||||
|
||||
MetaCoreGetWindowMap()[Impl_->NativeHandle] = Impl_.get();
|
||||
DragAcceptFiles(Impl_->NativeHandle, TRUE);
|
||||
#ifndef MSGFLT_ADD
|
||||
#define MSGFLT_ADD 1
|
||||
#endif
|
||||
ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
|
||||
ChangeWindowMessageFilter(0x0049, MSGFLT_ADD);
|
||||
|
||||
Impl_->CloseRequested = false;
|
||||
Impl_->LastFrameTime = std::chrono::steady_clock::now();
|
||||
@ -306,6 +335,12 @@ std::pair<int, int> MetaCoreWindow::GetFramebufferSize() const {
|
||||
return GetWindowSize();
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> MetaCoreWindow::ConsumeDroppedFiles() {
|
||||
std::vector<std::filesystem::path> droppedFiles;
|
||||
droppedFiles.swap(Impl_->DroppedFiles);
|
||||
return droppedFiles;
|
||||
}
|
||||
|
||||
void MetaCoreWindow::SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler) {
|
||||
Impl_->MessageHandler = std::move(handler);
|
||||
}
|
||||
|
||||
@ -5,9 +5,11 @@
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
@ -59,6 +61,7 @@ public:
|
||||
GLFWwindow* NativeHandle = nullptr;
|
||||
MetaCoreInput Input{};
|
||||
MetaCoreNativeWindowMessageHandler MessageHandler{};
|
||||
std::vector<std::filesystem::path> DroppedFiles{};
|
||||
std::chrono::steady_clock::time_point LastFrameTime = std::chrono::steady_clock::now();
|
||||
float DeltaSeconds = 1.0F / 60.0F;
|
||||
bool Initialized = false;
|
||||
@ -84,6 +87,16 @@ void MetaCoreGlfwScrollCallback(GLFWwindow* window, double, double yOffset) {
|
||||
}
|
||||
}
|
||||
|
||||
void MetaCoreGlfwDropCallback(GLFWwindow* window, int count, const char** paths) {
|
||||
if (auto* owner = MetaCoreGetWindowImpl(window); owner != nullptr) {
|
||||
for (int index = 0; index < count; ++index) {
|
||||
if (paths[index] != nullptr && paths[index][0] != '\0') {
|
||||
owner->DroppedFiles.emplace_back(paths[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MetaCoreWindow::MetaCoreWindow()
|
||||
@ -118,6 +131,7 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
|
||||
glfwSetWindowUserPointer(Impl_->NativeHandle, Impl_.get());
|
||||
glfwSetWindowCloseCallback(Impl_->NativeHandle, MetaCoreGlfwWindowCloseCallback);
|
||||
glfwSetScrollCallback(Impl_->NativeHandle, MetaCoreGlfwScrollCallback);
|
||||
glfwSetDropCallback(Impl_->NativeHandle, MetaCoreGlfwDropCallback);
|
||||
Impl_->CloseRequested = false;
|
||||
Impl_->LastFrameTime = std::chrono::steady_clock::now();
|
||||
Impl_->Initialized = true;
|
||||
@ -139,6 +153,7 @@ bool MetaCoreWindow::Initialize(int width, int height, const std::string& title)
|
||||
|
||||
void MetaCoreWindow::Shutdown() {
|
||||
if (Impl_->NativeHandle != nullptr) {
|
||||
glfwSetDropCallback(Impl_->NativeHandle, nullptr);
|
||||
glfwSetWindowUserPointer(Impl_->NativeHandle, nullptr);
|
||||
glfwDestroyWindow(Impl_->NativeHandle);
|
||||
Impl_->NativeHandle = nullptr;
|
||||
@ -250,6 +265,12 @@ std::pair<int, int> MetaCoreWindow::GetFramebufferSize() const {
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> MetaCoreWindow::ConsumeDroppedFiles() {
|
||||
std::vector<std::filesystem::path> droppedFiles;
|
||||
droppedFiles.swap(Impl_->DroppedFiles);
|
||||
return droppedFiles;
|
||||
}
|
||||
|
||||
void MetaCoreWindow::SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler) {
|
||||
Impl_->MessageHandler = std::move(handler);
|
||||
}
|
||||
|
||||
@ -3,10 +3,12 @@
|
||||
#include "MetaCorePlatform/MetaCoreInput.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace MetaCore {
|
||||
|
||||
@ -36,6 +38,7 @@ public:
|
||||
[[nodiscard]] float GetDeltaSeconds() const;
|
||||
[[nodiscard]] std::pair<int, int> GetWindowSize() const;
|
||||
[[nodiscard]] std::pair<int, int> GetFramebufferSize() const;
|
||||
[[nodiscard]] std::vector<std::filesystem::path> ConsumeDroppedFiles();
|
||||
|
||||
void SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler);
|
||||
|
||||
|
||||
@ -601,6 +601,83 @@ void MetaCoreTestAssetDatabaseMovePathUpdatesProjectDescriptor() {
|
||||
std::filesystem::remove_all(tempProjectRoot);
|
||||
}
|
||||
|
||||
void MetaCoreTestProjectAssetDatabaseOperations() {
|
||||
const std::filesystem::path tempProjectRoot =
|
||||
std::filesystem::temp_directory_path() / "MetaCoreProjectAssetDatabaseOperations";
|
||||
std::filesystem::remove_all(tempProjectRoot);
|
||||
std::filesystem::create_directories(tempProjectRoot / "Assets");
|
||||
std::filesystem::create_directories(tempProjectRoot / "Scenes");
|
||||
std::filesystem::create_directories(tempProjectRoot / "Library");
|
||||
|
||||
{
|
||||
std::ofstream projectFile(tempProjectRoot / "MetaCore.project.json", std::ios::trunc);
|
||||
projectFile << "{\n"
|
||||
<< " \"name\": \"ProjectAssetOps\",\n"
|
||||
<< " \"version\": \"0.1.0\",\n"
|
||||
<< " \"scenes\": [],\n"
|
||||
<< " \"startup_scene\": \"\"\n"
|
||||
<< "}\n";
|
||||
}
|
||||
{
|
||||
std::ofstream sourceFile(tempProjectRoot / "Assets" / "Source.dat", std::ios::trunc);
|
||||
sourceFile << "asset";
|
||||
}
|
||||
|
||||
_putenv_s("METACORE_PROJECT_PATH", tempProjectRoot.string().c_str());
|
||||
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
auto coreServicesModule = MetaCore::MetaCoreCreateBuiltinCoreServicesModule();
|
||||
coreServicesModule->Startup(moduleRegistry);
|
||||
|
||||
const auto assetDatabase = moduleRegistry.ResolveService<MetaCore::MetaCoreIAssetDatabaseService>();
|
||||
MetaCoreExpect(assetDatabase != nullptr, "应解析到 AssetDatabaseService");
|
||||
MetaCoreExpect(assetDatabase->HasProject(), "项目应已加载");
|
||||
MetaCoreExpect(assetDatabase->Refresh(), "应能刷新资产数据库");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / "Assets" / "Source.dat.mcmeta"), "Refresh 应生成源资产 mcmeta");
|
||||
|
||||
const std::filesystem::path movedPath = std::filesystem::path("Assets") / "Moved" / "SourceMoved.dat";
|
||||
MetaCoreExpect(assetDatabase->MovePathTo(std::filesystem::path("Assets") / "Source.dat", movedPath), "MovePathTo 应能精确移动文件");
|
||||
MetaCoreExpect(!std::filesystem::exists(tempProjectRoot / "Assets" / "Source.dat"), "移动后旧源文件应消失");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / movedPath), "移动后目标文件应存在");
|
||||
MetaCoreExpect(!std::filesystem::exists(tempProjectRoot / "Assets" / "Source.dat.mcmeta"), "移动后旧 mcmeta 应消失");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / "Assets" / "Moved" / "SourceMoved.dat.mcmeta"), "移动后新 mcmeta 应存在");
|
||||
MetaCoreExpect(!assetDatabase->FindAssetByRelativePath(std::filesystem::path("Assets") / "Source.dat").has_value(), "移动后旧路径资产记录应失效");
|
||||
MetaCoreExpect(assetDatabase->FindAssetByRelativePath(movedPath).has_value(), "移动后新路径资产记录应存在");
|
||||
|
||||
const std::filesystem::path materialPath = std::filesystem::path("Assets") / "Materials" / "PanelMaterial.mcmaterial.json";
|
||||
MetaCoreExpect(assetDatabase->CreateMaterialAsset(materialPath), "应能创建材质资源");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / materialPath), "材质源文件应存在");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / (materialPath.generic_string() + ".mcmeta")), "材质 mcmeta 应存在");
|
||||
const auto materialRecord = assetDatabase->FindAssetByRelativePath(materialPath);
|
||||
MetaCoreExpect(materialRecord.has_value(), "材质应进入资产数据库");
|
||||
MetaCoreExpect(materialRecord->Guid.IsValid(), "材质 GUID 应有效");
|
||||
MetaCoreExpect(materialRecord->Type == "material", "材质资产类型应正确");
|
||||
|
||||
const std::filesystem::path scenePath = std::filesystem::path("Scenes") / "PanelScene.mcscene.json";
|
||||
MetaCoreExpect(assetDatabase->CreateEmptySceneAsset(scenePath, true), "应能创建空场景资源");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / scenePath), "场景源文件应存在");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / (scenePath.generic_string() + ".mcmeta")), "场景 mcmeta 应存在");
|
||||
MetaCoreExpect(assetDatabase->GetProjectDescriptor().StartupScenePath == scenePath, "创建场景可设置为启动场景");
|
||||
MetaCoreExpect(
|
||||
std::find(
|
||||
assetDatabase->GetProjectDescriptor().ScenePaths.begin(),
|
||||
assetDatabase->GetProjectDescriptor().ScenePaths.end(),
|
||||
scenePath
|
||||
) != assetDatabase->GetProjectDescriptor().ScenePaths.end(),
|
||||
"创建场景后应注册到项目场景列表"
|
||||
);
|
||||
|
||||
MetaCoreExpect(assetDatabase->DeletePath(scenePath), "应能删除场景资源");
|
||||
MetaCoreExpect(!std::filesystem::exists(tempProjectRoot / scenePath), "删除后场景源文件应消失");
|
||||
MetaCoreExpect(!std::filesystem::exists(tempProjectRoot / (scenePath.generic_string() + ".mcmeta")), "删除后场景 mcmeta 应消失");
|
||||
MetaCoreExpect(assetDatabase->GetProjectDescriptor().StartupScenePath.empty(), "删除启动场景后 startup scene 应清空");
|
||||
|
||||
coreServicesModule->Shutdown(moduleRegistry);
|
||||
moduleRegistry.ShutdownServices();
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
std::filesystem::remove_all(tempProjectRoot);
|
||||
}
|
||||
|
||||
void MetaCoreTestComponentRegistryDescriptors() {
|
||||
MetaCore::MetaCoreEditorModuleRegistry moduleRegistry;
|
||||
auto coreServicesModule = MetaCore::MetaCoreCreateBuiltinCoreServicesModule();
|
||||
@ -2482,6 +2559,25 @@ void MetaCoreTestPrefabWorkflow() {
|
||||
MetaCoreExpect(revertedRoot, "还原后应重新选中实例根");
|
||||
MetaCoreExpectVec3Near(revertedRoot.GetComponent<MetaCore::MetaCoreTransformComponent>().Position, glm::vec3(8.0F, 9.0F, 10.0F), "Revert 后实例应恢复为 prefab 内容");
|
||||
|
||||
MetaCore::MetaCoreGameObject draggedSource = scene.CreateGameObject("DraggedPrefabSource");
|
||||
draggedSource.GetComponent<MetaCore::MetaCoreTransformComponent>().Position = glm::vec3(4.0F, 5.0F, 6.0F);
|
||||
const std::filesystem::path draggedPrefabPath = std::filesystem::path("Assets") / "Prefabs" / "DraggedPrefabSource.mcprefab.json";
|
||||
const auto draggedCreatedPath = prefabService->CreatePrefabFromObject(editorContext, draggedSource.GetId(), draggedPrefabPath);
|
||||
MetaCoreExpect(draggedCreatedPath.has_value(), "应能从指定对象创建 prefab");
|
||||
MetaCoreExpect(std::filesystem::exists(tempProjectRoot / draggedPrefabPath), "指定对象 prefab 文件应存在");
|
||||
MetaCoreExpect(assetDatabaseService->Refresh(), "指定对象 prefab 创建后应能刷新资产数据库");
|
||||
const auto draggedPrefabRecord = assetDatabaseService->FindAssetByRelativePath(draggedPrefabPath);
|
||||
MetaCoreExpect(draggedPrefabRecord.has_value(), "指定对象 prefab 应进入资产数据库");
|
||||
const auto draggedPrefabDocument = MetaCore::MetaCoreSceneSerializer::LoadPrefabFromJson(tempProjectRoot / draggedPrefabPath, reflectionRegistry->GetTypeRegistry());
|
||||
MetaCoreExpect(draggedPrefabDocument.has_value(), "指定对象 prefab 应能被 JSON 解析");
|
||||
MetaCoreExpect(!draggedPrefabDocument->GameObjects.empty(), "指定对象 prefab 应包含对象数据");
|
||||
MetaCoreExpect(draggedPrefabDocument->GameObjects.front().Name == "DraggedPrefabSource", "指定对象 prefab 应使用目标对象数据");
|
||||
MetaCoreExpectVec3Near(
|
||||
draggedPrefabDocument->GameObjects.front().Transform.Position,
|
||||
glm::vec3(4.0F, 5.0F, 6.0F),
|
||||
"指定对象 prefab 应保留目标对象 transform"
|
||||
);
|
||||
|
||||
coreServicesModule->Shutdown(moduleRegistry);
|
||||
moduleRegistry.ShutdownServices();
|
||||
_putenv_s("METACORE_PROJECT_PATH", "");
|
||||
@ -4217,6 +4313,8 @@ int main() {
|
||||
MetaCoreTestAssetDatabaseRenamePathUpdatesProjectDescriptor();
|
||||
std::cout << "[RUN] MetaCoreTestAssetDatabaseMovePathUpdatesProjectDescriptor..." << std::endl;
|
||||
MetaCoreTestAssetDatabaseMovePathUpdatesProjectDescriptor();
|
||||
std::cout << "[RUN] MetaCoreTestProjectAssetDatabaseOperations..." << std::endl;
|
||||
MetaCoreTestProjectAssetDatabaseOperations();
|
||||
std::cout << "[RUN] MetaCoreTestComponentRegistryDescriptors..." << std::endl;
|
||||
MetaCoreTestComponentRegistryDescriptors();
|
||||
std::cout << "[RUN] MetaCoreTestSceneEditingServiceCreateCamera..." << std::endl;
|
||||
@ -4249,6 +4347,8 @@ int main() {
|
||||
MetaCoreTestInstantiateImportedModelAssetIntoScene();
|
||||
std::cout << "[RUN] MetaCoreTestInstantiateMultiNodeModelAssetIntoScene..." << std::endl;
|
||||
MetaCoreTestInstantiateMultiNodeModelAssetIntoScene();
|
||||
std::cout << "[RUN] MetaCoreTestPrefabWorkflow..." << std::endl;
|
||||
MetaCoreTestPrefabWorkflow();
|
||||
std::cout << "[RUN] MetaCoreTestDeleteModelAssetCorrectlyClearsFromViewport..." << std::endl;
|
||||
MetaCoreTestDeleteModelAssetCorrectlyClearsFromViewport();
|
||||
std::cout << "[RUN] MetaCoreTestDeleteModelSubChildCorrectlyClearsFromViewport..." << std::endl;
|
||||
@ -4257,8 +4357,6 @@ int main() {
|
||||
MetaCoreTestModelReimportKeepsGeneratedAssetReferencesStable();
|
||||
std::cout << "[RUN] MetaCoreTestBootstrapStartupSceneCreation..." << std::endl;
|
||||
MetaCoreTestBootstrapStartupSceneCreation();
|
||||
std::cout << "[RUN] MetaCoreTestPrefabWorkflow..." << std::endl;
|
||||
MetaCoreTestPrefabWorkflow();
|
||||
std::cout << "[RUN] MetaCoreTestCookPipelineCooksJsonScenePrefabMaterialUi..." << std::endl;
|
||||
MetaCoreTestCookPipelineCooksJsonScenePrefabMaterialUi();
|
||||
std::cout << "[RUN] MetaCoreTestComponentRegistryOperations..." << std::endl;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user