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

@@ -20,6 +20,10 @@ bool ShouldTraceMeshPath(const std::string& path) {
path.find("backpack") != std::string::npos;
}
bool HasVirtualPathScheme(const std::string& path) {
return path.find("://") != std::string::npos;
}
std::string EncodeAssetRef(const Resources::AssetRef& assetRef) {
if (!assetRef.IsValid()) {
return std::string();
@@ -67,17 +71,20 @@ struct MeshFilterComponent::PendingMeshLoadState {
};
Resources::Mesh* MeshFilterComponent::GetMesh() const {
const_cast<MeshFilterComponent*>(this)->EnsureDeferredAsyncMeshLoadStarted();
const_cast<MeshFilterComponent*>(this)->ResolvePendingMesh();
return m_mesh.Get();
}
const Resources::ResourceHandle<Resources::Mesh>& MeshFilterComponent::GetMeshHandle() const {
const_cast<MeshFilterComponent*>(this)->EnsureDeferredAsyncMeshLoadStarted();
const_cast<MeshFilterComponent*>(this)->ResolvePendingMesh();
return m_mesh;
}
void MeshFilterComponent::SetMeshPath(const std::string& meshPath) {
m_pendingMeshLoad.reset();
m_asyncMeshLoadRequested = false;
m_meshPath = meshPath;
if (m_meshPath.empty()) {
m_mesh.Reset();
@@ -101,6 +108,7 @@ void MeshFilterComponent::SetMeshPath(const std::string& meshPath) {
void MeshFilterComponent::SetMesh(const Resources::ResourceHandle<Resources::Mesh>& mesh) {
m_pendingMeshLoad.reset();
m_asyncMeshLoadRequested = false;
m_mesh = mesh;
m_meshPath = mesh.Get() != nullptr ? ToStdString(mesh->GetPath()) : std::string();
if (m_meshPath.empty()) {
@@ -116,18 +124,29 @@ void MeshFilterComponent::SetMesh(Resources::Mesh* mesh) {
void MeshFilterComponent::ClearMesh() {
m_pendingMeshLoad.reset();
m_asyncMeshLoadRequested = false;
m_mesh.Reset();
m_meshPath.clear();
m_meshRef.Reset();
}
void MeshFilterComponent::Serialize(std::ostream& os) const {
os << "mesh=" << m_meshPath << ";";
os << "meshRef=" << EncodeAssetRef(m_meshRef) << ";";
Resources::AssetRef meshRef = m_meshRef;
if (!meshRef.IsValid() &&
!m_meshPath.empty() &&
!HasVirtualPathScheme(m_meshPath) &&
Resources::ResourceManager::Get().TryGetAssetRef(m_meshPath.c_str(), Resources::ResourceType::Mesh, meshRef)) {
}
os << "meshRef=" << EncodeAssetRef(meshRef) << ";";
if (!meshRef.IsValid() && !m_meshPath.empty()) {
os << "meshPath=" << m_meshPath << ";";
}
}
void MeshFilterComponent::Deserialize(std::istream& is) {
m_pendingMeshLoad.reset();
m_asyncMeshLoadRequested = false;
m_mesh.Reset();
m_meshPath.clear();
m_meshRef.Reset();
@@ -148,7 +167,7 @@ void MeshFilterComponent::Deserialize(std::istream& is) {
const std::string key = token.substr(0, eqPos);
const std::string value = token.substr(eqPos + 1);
if (key == "mesh") {
if (key == "mesh" || key == "meshPath") {
pendingMeshPath = value;
} else if (key == "meshRef") {
TryDecodeAssetRef(value, pendingMeshRef);
@@ -172,7 +191,6 @@ void MeshFilterComponent::Deserialize(std::istream& is) {
if (ShouldTraceMeshPath(m_meshPath)) {
TraceMeshFilter(*this, std::string("Resolved meshRef to path=") + m_meshPath);
}
BeginAsyncMeshLoad(m_meshPath);
return;
}
@@ -191,7 +209,6 @@ void MeshFilterComponent::Deserialize(std::istream& is) {
if (!Resources::ResourceManager::Get().TryGetAssetRef(m_meshPath.c_str(), Resources::ResourceType::Mesh, m_meshRef)) {
m_meshRef.Reset();
}
BeginAsyncMeshLoad(m_meshPath);
return;
}
@@ -202,10 +219,12 @@ void MeshFilterComponent::Deserialize(std::istream& is) {
void MeshFilterComponent::BeginAsyncMeshLoad(const std::string& meshPath) {
if (meshPath.empty()) {
m_pendingMeshLoad.reset();
m_asyncMeshLoadRequested = false;
m_mesh.Reset();
return;
}
m_asyncMeshLoadRequested = true;
m_mesh.Reset();
m_pendingMeshLoad = std::make_shared<PendingMeshLoadState>();
if (ShouldTraceMeshPath(meshPath)) {
@@ -219,7 +238,15 @@ void MeshFilterComponent::BeginAsyncMeshLoad(const std::string& meshPath) {
state->result = std::move(result);
state->completed = true;
}
});
});
}
void MeshFilterComponent::EnsureDeferredAsyncMeshLoadStarted() {
if (m_asyncMeshLoadRequested || m_mesh.Get() != nullptr || m_meshPath.empty()) {
return;
}
BeginAsyncMeshLoad(m_meshPath);
}
void MeshFilterComponent::ResolvePendingMesh() {

View File

@@ -4,6 +4,7 @@
#include "Core/Asset/ResourceManager.h"
#include "Debug/Logger.h"
#include <algorithm>
#include <sstream>
namespace XCEngine {
@@ -21,6 +22,10 @@ bool ShouldTraceMaterialPath(const std::string& path) {
path.find("New Material.mat") != std::string::npos;
}
bool HasVirtualPathScheme(const std::string& path) {
return path.find("://") != std::string::npos;
}
std::string EncodeAssetRef(const Resources::AssetRef& assetRef) {
if (!assetRef.IsValid()) {
return std::string();
@@ -145,11 +150,13 @@ struct MeshRendererComponent::PendingMaterialLoadState {
};
Resources::Material* MeshRendererComponent::GetMaterial(size_t index) const {
const_cast<MeshRendererComponent*>(this)->EnsureDeferredAsyncMaterialLoadStarted(index);
const_cast<MeshRendererComponent*>(this)->ResolvePendingMaterials();
return index < m_materials.size() ? m_materials[index].Get() : nullptr;
}
const Resources::ResourceHandle<Resources::Material>& MeshRendererComponent::GetMaterialHandle(size_t index) const {
const_cast<MeshRendererComponent*>(this)->EnsureDeferredAsyncMaterialLoadStarted(index);
const_cast<MeshRendererComponent*>(this)->ResolvePendingMaterials();
static const Resources::ResourceHandle<Resources::Material> kNullHandle;
return index < m_materials.size() ? m_materials[index] : kNullHandle;
@@ -163,6 +170,7 @@ const std::string& MeshRendererComponent::GetMaterialPath(size_t index) const {
void MeshRendererComponent::SetMaterialPath(size_t index, const std::string& materialPath) {
EnsureMaterialSlot(index);
m_pendingMaterialLoads[index].reset();
m_asyncMaterialLoadRequested[index] = false;
m_materialPaths[index] = materialPath;
if (materialPath.empty()) {
m_materials[index].Reset();
@@ -188,6 +196,7 @@ void MeshRendererComponent::SetMaterialPath(size_t index, const std::string& mat
void MeshRendererComponent::SetMaterial(size_t index, const Resources::ResourceHandle<Resources::Material>& material) {
EnsureMaterialSlot(index);
m_pendingMaterialLoads[index].reset();
m_asyncMaterialLoadRequested[index] = false;
m_materials[index] = material;
m_materialPaths[index] = MaterialPathFromHandle(material);
if (m_materialPaths[index].empty() ||
@@ -206,6 +215,8 @@ void MeshRendererComponent::SetMaterials(const std::vector<Resources::ResourceHa
m_materialRefs.resize(materials.size());
m_pendingMaterialLoads.clear();
m_pendingMaterialLoads.resize(materials.size());
m_asyncMaterialLoadRequested.clear();
m_asyncMaterialLoadRequested.resize(materials.size(), false);
for (size_t i = 0; i < materials.size(); ++i) {
m_materialPaths[i] = MaterialPathFromHandle(materials[i]);
if (m_materialPaths[i].empty() ||
@@ -220,23 +231,45 @@ void MeshRendererComponent::ClearMaterials() {
m_materialPaths.clear();
m_materialRefs.clear();
m_pendingMaterialLoads.clear();
m_asyncMaterialLoadRequested.clear();
}
void MeshRendererComponent::Serialize(std::ostream& os) const {
os << "materials=";
for (size_t i = 0; i < m_materialPaths.size(); ++i) {
const size_t slotCount = std::max(m_materialPaths.size(), m_materialRefs.size());
std::vector<Resources::AssetRef> serializedRefs = m_materialRefs;
serializedRefs.resize(slotCount);
std::vector<std::string> serializedPaths = m_materialPaths;
serializedPaths.resize(slotCount);
std::vector<std::string> fallbackPaths(slotCount);
for (size_t i = 0; i < slotCount; ++i) {
if (!serializedRefs[i].IsValid() &&
!serializedPaths[i].empty() &&
!HasVirtualPathScheme(serializedPaths[i]) &&
Resources::ResourceManager::Get().TryGetAssetRef(
serializedPaths[i].c_str(),
Resources::ResourceType::Material,
serializedRefs[i])) {
}
if (!serializedRefs[i].IsValid()) {
fallbackPaths[i] = serializedPaths[i];
}
}
os << "materialPaths=";
for (size_t i = 0; i < slotCount; ++i) {
if (i > 0) {
os << "|";
}
os << m_materialPaths[i];
os << fallbackPaths[i];
}
os << ";";
os << "materialRefs=";
for (size_t i = 0; i < m_materialRefs.size(); ++i) {
for (size_t i = 0; i < serializedRefs.size(); ++i) {
if (i > 0) {
os << "|";
}
os << EncodeAssetRef(m_materialRefs[i]);
os << EncodeAssetRef(serializedRefs[i]);
}
os << ";";
os << "castShadows=" << (m_castShadows ? 1 : 0) << ";";
@@ -265,11 +298,12 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
const std::string key = token.substr(0, eqPos);
const std::string value = token.substr(eqPos + 1);
if (key == "materials") {
if (key == "materials" || key == "materialPaths") {
m_materialPaths = SplitMaterialPaths(value);
m_materials.resize(m_materialPaths.size());
m_materialRefs.resize(m_materialPaths.size());
m_pendingMaterialLoads.resize(m_materialPaths.size());
m_asyncMaterialLoadRequested.resize(m_materialPaths.size(), false);
} else if (key == "materialRefs") {
pendingMaterialRefs = SplitMaterialRefs(value);
} else if (key == "castShadows") {
@@ -286,6 +320,7 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
m_materials.resize(pendingMaterialRefs.size());
m_materialRefs.resize(pendingMaterialRefs.size());
m_pendingMaterialLoads.resize(pendingMaterialRefs.size());
m_asyncMaterialLoadRequested.resize(pendingMaterialRefs.size(), false);
}
if (ShouldTraceAnyMaterialPath(m_materialPaths)) {
@@ -310,7 +345,6 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
std::string("Resolved materialRef slot=") + std::to_string(i) +
" path=" + m_materialPaths[i]);
}
BeginAsyncMaterialLoad(i, m_materialPaths[i]);
restoredOrQueued = true;
}
}
@@ -336,7 +370,6 @@ void MeshRendererComponent::Deserialize(std::istream& is) {
m_materialRefs[i])) {
m_materialRefs[i].Reset();
}
BeginAsyncMaterialLoad(i, m_materialPaths[i]);
}
continue;
}
@@ -349,10 +382,12 @@ void MeshRendererComponent::BeginAsyncMaterialLoad(size_t index, const std::stri
EnsureMaterialSlot(index);
if (materialPath.empty()) {
m_pendingMaterialLoads[index].reset();
m_asyncMaterialLoadRequested[index] = false;
m_materials[index].Reset();
return;
}
m_asyncMaterialLoadRequested[index] = true;
m_materials[index].Reset();
m_pendingMaterialLoads[index] = std::make_shared<PendingMaterialLoadState>();
if (ShouldTraceMaterialPath(materialPath)) {
@@ -369,7 +404,22 @@ void MeshRendererComponent::BeginAsyncMaterialLoad(size_t index, const std::stri
state->result = std::move(result);
state->completed = true;
}
});
});
}
void MeshRendererComponent::EnsureDeferredAsyncMaterialLoadStarted(size_t index) {
if (index >= m_materialPaths.size()) {
return;
}
EnsureMaterialSlot(index);
if (m_asyncMaterialLoadRequested[index] ||
m_materials[index].Get() != nullptr ||
m_materialPaths[index].empty()) {
return;
}
BeginAsyncMaterialLoad(index, m_materialPaths[index]);
}
void MeshRendererComponent::ResolvePendingMaterials() {
@@ -428,6 +478,9 @@ void MeshRendererComponent::EnsureMaterialSlot(size_t index) {
if (index >= m_pendingMaterialLoads.size()) {
m_pendingMaterialLoads.resize(index + 1);
}
if (index >= m_asyncMaterialLoadRequested.size()) {
m_asyncMaterialLoadRequested.resize(index + 1, false);
}
}
std::string MeshRendererComponent::MaterialPathFromHandle(const Resources::ResourceHandle<Resources::Material>& material) {