feat: expand editor scripting asset and viewport flow

This commit is contained in:
2026-04-03 13:22:30 +08:00
parent ed8c27fde2
commit a05d0b80a2
124 changed files with 10397 additions and 1737 deletions

View File

@@ -3,6 +3,7 @@
#include <XCEngine/Components/Component.h>
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ResourceHandle.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/Resources/Mesh/Mesh.h>
#include <memory>
@@ -32,12 +33,14 @@ private:
struct PendingMeshLoadState;
void BeginAsyncMeshLoad(const std::string& meshPath);
void EnsureDeferredAsyncMeshLoadStarted();
void ResolvePendingMesh();
Resources::ResourceHandle<Resources::Mesh> m_mesh;
std::string m_meshPath;
Resources::AssetRef m_meshRef;
std::shared_ptr<PendingMeshLoadState> m_pendingMeshLoad;
bool m_asyncMeshLoadRequested = false;
};
} // namespace Components

View File

@@ -46,6 +46,7 @@ private:
struct PendingMaterialLoadState;
void BeginAsyncMaterialLoad(size_t index, const std::string& materialPath);
void EnsureDeferredAsyncMaterialLoadStarted(size_t index);
void ResolvePendingMaterials();
void EnsureMaterialSlot(size_t index);
static std::string MaterialPathFromHandle(const Resources::ResourceHandle<Resources::Material>& material);
@@ -54,6 +55,7 @@ private:
std::vector<std::string> m_materialPaths;
std::vector<Resources::AssetRef> m_materialRefs;
std::vector<std::shared_ptr<PendingMaterialLoadState>> m_pendingMaterialLoads;
std::vector<bool> m_asyncMaterialLoadRequested;
bool m_castShadows = true;
bool m_receiveShadows = true;
uint32_t m_renderLayer = 0;

View File

@@ -6,12 +6,23 @@
#include <filesystem>
#include <unordered_map>
#include <vector>
namespace XCEngine {
namespace Resources {
class Mesh;
class Material;
class AssetDatabase {
public:
struct ArtifactDependencyRecord {
Containers::String path;
Containers::String hash;
Core::uint64 fileSize = 0;
Core::uint64 writeTime = 0;
};
struct SourceAssetRecord {
AssetGUID guid;
Containers::String relativePath;
@@ -39,6 +50,7 @@ public:
Core::uint64 sourceFileSize = 0;
Core::uint64 sourceWriteTime = 0;
LocalID mainLocalID = kMainAssetLocalID;
std::vector<ArtifactDependencyRecord> dependencies;
};
struct ResolvedAsset {
@@ -66,13 +78,15 @@ public:
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; }
private:
static constexpr Core::uint32 kCurrentImporterVersion = 2;
static constexpr Core::uint32 kCurrentImporterVersion = 3;
void EnsureProjectLayout();
void LoadSourceAssetDB();
@@ -110,12 +124,24 @@ private:
bool ImportModelAsset(const SourceAssetRecord& sourceRecord,
ArtifactRecord& outRecord);
Containers::String BuildArtifactKey(const SourceAssetRecord& sourceRecord) const;
Containers::String BuildArtifactKey(
const SourceAssetRecord& sourceRecord,
const std::vector<ArtifactDependencyRecord>& dependencies = {}) const;
Containers::String BuildArtifactDirectory(const Containers::String& artifactKey) 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 Mesh& mesh,
std::vector<ArtifactDependencyRecord>& outDependencies) const;
bool CollectMaterialDependencies(const Material& material,
std::vector<ArtifactDependencyRecord>& outDependencies) const;
Containers::String m_projectRoot;
Containers::String m_assetsRoot;

View File

@@ -0,0 +1,38 @@
#pragma once
#include "AssetDatabase.h"
#include <mutex>
#include <unordered_map>
namespace XCEngine {
namespace Resources {
class AssetImportService {
public:
void Initialize();
void Shutdown();
void SetProjectRoot(const Containers::String& projectRoot);
Containers::String GetProjectRoot() const;
void Refresh();
bool EnsureArtifact(const Containers::String& requestPath,
ResourceType requestedType,
AssetDatabase::ResolvedAsset& outAsset);
bool TryGetAssetRef(const Containers::String& requestPath,
ResourceType resourceType,
AssetRef& outRef) const;
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;
private:
mutable std::recursive_mutex m_mutex;
Containers::String m_projectRoot;
AssetDatabase m_assetDatabase;
};
} // namespace Resources
} // namespace XCEngine

View File

@@ -0,0 +1,37 @@
#pragma once
#include <XCEngine/Core/Asset/AssetRef.h>
#include <XCEngine/Core/Asset/ResourceTypes.h>
#include <XCEngine/Core/Containers/String.h>
#include <shared_mutex>
#include <unordered_map>
namespace XCEngine {
namespace Resources {
class AssetImportService;
class ProjectAssetIndex {
public:
void ResetProjectRoot(const Containers::String& projectRoot = Containers::String());
void RefreshFrom(const AssetImportService& importService);
bool TryGetAssetRef(AssetImportService& importService,
const Containers::String& path,
ResourceType resourceType,
AssetRef& outRef) const;
bool TryResolveAssetPath(const AssetImportService& importService,
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

View File

@@ -1,7 +1,8 @@
#pragma once
#include <XCEngine/Core/IO/IResourceLoader.h>
#include "AssetDatabase.h"
#include "AssetImportService.h"
#include "ProjectAssetIndex.h"
#include "ResourceCache.h"
#include "AsyncLoader.h"
#include "ResourceHandle.h"
@@ -165,12 +166,12 @@ private:
size_t m_memoryUsage = 0;
size_t m_memoryBudget = 512 * 1024 * 1024;
AssetDatabase m_assetDatabase;
mutable AssetImportService m_assetImportService;
mutable ProjectAssetIndex m_projectAssetIndex;
ResourceCache m_cache;
Core::UniqueRef<AsyncLoader> m_asyncLoader;
Threading::Mutex m_mutex;
std::mutex m_initializeMutex;
mutable std::recursive_mutex m_ioMutex;
std::mutex m_inFlightLoadsMutex;
std::unordered_map<InFlightLoadKey, std::shared_ptr<InFlightLoadState>, InFlightLoadKeyHasher> m_inFlightLoads;
std::atomic<Core::uint32> m_deferredSceneLoadDepth{0};

View File

@@ -21,6 +21,7 @@ public:
bool IsKeyDown(KeyCode key) const;
bool IsKeyUp(KeyCode key) const;
bool IsKeyPressed(KeyCode key) const;
bool IsKeyReleased(KeyCode key) const;
Math::Vector2 GetMousePosition() const;
Math::Vector2 GetMouseDelta() const;
@@ -28,6 +29,7 @@ public:
bool IsMouseButtonDown(MouseButton button) const;
bool IsMouseButtonUp(MouseButton button) const;
bool IsMouseButtonClicked(MouseButton button) const;
bool IsMouseButtonReleased(MouseButton button) const;
int GetTouchCount() const;
TouchState GetTouch(int index) const;
@@ -38,6 +40,8 @@ public:
bool GetButton(const Containers::String& buttonName) const;
bool GetButtonDown(const Containers::String& buttonName) const;
bool GetButtonUp(const Containers::String& buttonName) const;
bool IsAnyKeyDown() const;
bool IsAnyKeyPressed() const;
void RegisterAxis(const InputAxis& axis);
void RegisterButton(const Containers::String& name, KeyCode key);
@@ -68,6 +72,7 @@ private:
std::vector<bool> m_keyDownThisFrame;
std::vector<bool> m_keyDownLastFrame;
std::vector<bool> m_keyUpThisFrame;
std::vector<bool> m_keyDown;
Math::Vector2 m_mousePosition;
@@ -75,6 +80,7 @@ private:
float m_mouseScrollDelta = 0.0f;
std::vector<bool> m_mouseButtonDownThisFrame;
std::vector<bool> m_mouseButtonDownLastFrame;
std::vector<bool> m_mouseButtonUpThisFrame;
std::vector<bool> m_mouseButtonDown;
std::vector<TouchState> m_touches;

View File

@@ -57,6 +57,7 @@ struct CameraRenderRequest {
Math::Color clearColorOverride = Math::Color::Black();
RenderPassSequence* preScenePasses = nullptr;
RenderPassSequence* postScenePasses = nullptr;
RenderPassSequence* overlayPasses = nullptr;
bool IsValid() const {
return scene != nullptr &&

View File

@@ -28,6 +28,26 @@ enum class ScriptLifecycleMethod {
OnDestroy
};
struct ScriptClassDescriptor {
std::string assemblyName;
std::string namespaceName;
std::string className;
std::string GetFullName() const {
return namespaceName.empty() ? className : namespaceName + "." + className;
}
bool operator==(const ScriptClassDescriptor& other) const {
return assemblyName == other.assemblyName
&& namespaceName == other.namespaceName
&& className == other.className;
}
bool operator!=(const ScriptClassDescriptor& other) const {
return !(*this == other);
}
};
struct ScriptRuntimeContext {
Components::Scene* scene = nullptr;
Components::GameObject* gameObject = nullptr;
@@ -43,6 +63,9 @@ public:
virtual void OnRuntimeStart(Components::Scene* scene) = 0;
virtual void OnRuntimeStop(Components::Scene* scene) = 0;
virtual bool TryGetAvailableScriptClasses(
std::vector<ScriptClassDescriptor>& outClasses) const = 0;
virtual bool TryGetClassFieldMetadata(
const std::string& assemblyName,
const std::string& namespaceName,

View File

@@ -50,6 +50,8 @@ public:
const std::string& namespaceName,
const std::string& className) const;
std::vector<std::string> GetScriptClassNames(const std::string& assemblyName = std::string()) const;
bool TryGetAvailableScriptClasses(
std::vector<ScriptClassDescriptor>& outClasses) const override;
bool TryGetClassFieldMetadata(
const std::string& assemblyName,
const std::string& namespaceName,

View File

@@ -9,6 +9,8 @@ class NullScriptRuntime : public IScriptRuntime {
public:
void OnRuntimeStart(Components::Scene* scene) override;
void OnRuntimeStop(Components::Scene* scene) override;
bool TryGetAvailableScriptClasses(
std::vector<ScriptClassDescriptor>& outClasses) const override;
bool TryGetClassFieldMetadata(
const std::string& assemblyName,
const std::string& namespaceName,

View File

@@ -28,6 +28,7 @@ public:
void SetScriptClass(const std::string& namespaceName, const std::string& className);
void SetScriptClass(const std::string& assemblyName, const std::string& namespaceName, const std::string& className);
void ClearScriptClass();
bool HasScriptClass() const { return !m_className.empty(); }
std::string GetFullClassName() const;

View File

@@ -19,9 +19,12 @@ class ScriptComponent;
class ScriptEngine {
public:
static ScriptEngine& Get();
static constexpr float DefaultFixedDeltaTime = 1.0f / 50.0f;
void SetRuntime(IScriptRuntime* runtime);
IScriptRuntime* GetRuntime() const { return m_runtime; }
void SetRuntimeFixedDeltaTime(float fixedDeltaTime);
float GetRuntimeFixedDeltaTime() const { return m_runtimeFixedDeltaTime; }
void OnRuntimeStart(Components::Scene* scene);
void OnRuntimeStop();
@@ -33,6 +36,7 @@ public:
void OnScriptComponentEnabled(ScriptComponent* component);
void OnScriptComponentDisabled(ScriptComponent* component);
void OnScriptComponentDestroyed(ScriptComponent* component);
void OnScriptComponentClassChanged(ScriptComponent* component);
bool IsRuntimeRunning() const { return m_runtimeRunning; }
Components::Scene* GetRuntimeScene() const { return m_runtimeScene; }
@@ -40,6 +44,9 @@ public:
bool HasTrackedScriptComponent(const ScriptComponent* component) const;
bool HasRuntimeInstance(const ScriptComponent* component) const;
size_t GetTrackedScriptCount() const { return m_scriptOrder.size(); }
bool TryGetAvailableScriptClasses(
std::vector<ScriptClassDescriptor>& outClasses,
const std::string& assemblyName = std::string()) const;
bool TrySetScriptFieldValue(
ScriptComponent* component,
const std::string& fieldName,
@@ -138,6 +145,7 @@ private:
IScriptRuntime* m_runtime = &m_nullRuntime;
Components::Scene* m_runtimeScene = nullptr;
bool m_runtimeRunning = false;
float m_runtimeFixedDeltaTime = DefaultFixedDeltaTime;
uint64_t m_runtimeSceneCreatedSubscription = 0;
std::unordered_map<ScriptInstanceKey, ScriptInstanceState, ScriptInstanceKeyHasher> m_scriptStates;