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

@@ -9,6 +9,7 @@
#include <cstring>
#include <cstdlib>
#include <filesystem>
#include <functional>
#include <fstream>
#include <string>
@@ -64,6 +65,56 @@ Containers::String NormalizePathString(const std::filesystem::path& path) {
return Containers::String(path.lexically_normal().generic_string().c_str());
}
bool IsProjectRelativePath(const std::filesystem::path& path) {
const std::string generic = path.generic_string();
return !generic.empty() &&
generic != "." &&
generic != ".." &&
generic.rfind("../", 0) != 0;
}
Containers::String ToProjectRelativeIfPossible(const std::filesystem::path& path) {
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
const std::filesystem::path normalizedPath = path.lexically_normal();
if (!resourceRoot.Empty() && normalizedPath.is_absolute()) {
std::error_code ec;
const std::filesystem::path relativePath =
std::filesystem::relative(normalizedPath, std::filesystem::path(resourceRoot.CStr()), ec);
if (!ec && IsProjectRelativePath(relativePath)) {
return NormalizePathString(relativePath);
}
}
return NormalizePathString(normalizedPath);
}
Containers::String ResolveSourceDependencyPath(const Containers::String& dependencyPath,
const Containers::String& sourcePath) {
if (dependencyPath.Empty()) {
return dependencyPath;
}
std::filesystem::path dependencyFsPath(dependencyPath.CStr());
if (dependencyFsPath.is_absolute()) {
return NormalizePathString(dependencyFsPath);
}
const std::filesystem::path sourceFsPath(sourcePath.CStr());
if (sourceFsPath.is_absolute()) {
return ToProjectRelativeIfPossible(sourceFsPath.parent_path() / dependencyFsPath);
}
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
if (!resourceRoot.Empty()) {
return ToProjectRelativeIfPossible(
std::filesystem::path(resourceRoot.CStr()) /
sourceFsPath.parent_path() /
dependencyFsPath);
}
return NormalizePathString(sourceFsPath.parent_path() / dependencyFsPath);
}
Containers::String ResolveArtifactDependencyPath(const Containers::String& dependencyPath,
const Containers::String& ownerArtifactPath) {
if (dependencyPath.Empty()) {
@@ -358,6 +409,125 @@ bool TryParseTagMap(const std::string& objectText, Material* material) {
return false;
}
bool TryParseStringMapObject(
const std::string& objectText,
const std::function<void(const Containers::String&, const Containers::String&)>& onEntry) {
if (!onEntry || objectText.empty() || objectText.front() != '{' || objectText.back() != '}') {
return false;
}
size_t pos = 1;
while (pos < objectText.size()) {
pos = SkipWhitespace(objectText, pos);
if (pos >= objectText.size()) {
return false;
}
if (objectText[pos] == '}') {
return true;
}
Containers::String key;
if (!ParseQuotedString(objectText, pos, key, &pos)) {
return false;
}
pos = SkipWhitespace(objectText, pos);
if (pos >= objectText.size() || objectText[pos] != ':') {
return false;
}
pos = SkipWhitespace(objectText, pos + 1);
Containers::String value;
if (!ParseQuotedString(objectText, pos, value, &pos)) {
return false;
}
onEntry(key, value);
pos = SkipWhitespace(objectText, pos);
if (pos >= objectText.size()) {
return false;
}
if (objectText[pos] == ',') {
++pos;
continue;
}
if (objectText[pos] == '}') {
return true;
}
return false;
}
return false;
}
bool TryApplyTexturePath(Material* material,
const Containers::String& textureName,
const Containers::String& texturePath) {
if (material == nullptr || textureName.Empty() || texturePath.Empty()) {
return false;
}
material->SetTexturePath(
textureName,
ResolveSourceDependencyPath(texturePath, material->GetPath()));
return true;
}
bool TryParseMaterialTextureBindings(const std::string& jsonText, Material* material) {
if (material == nullptr) {
return false;
}
static const char* const kKnownTextureKeys[] = {
"baseColorTexture",
"_BaseColorTexture",
"_MainTex",
"normalTexture",
"_BumpMap",
"specularTexture",
"emissiveTexture",
"metallicTexture",
"roughnessTexture",
"occlusionTexture",
"opacityTexture"
};
for (const char* key : kKnownTextureKeys) {
if (!HasKey(jsonText, key)) {
continue;
}
Containers::String texturePath;
if (!TryParseStringValue(jsonText, key, texturePath)) {
return false;
}
TryApplyTexturePath(material, Containers::String(key), texturePath);
}
if (HasKey(jsonText, "textures")) {
std::string texturesObject;
if (!TryExtractObject(jsonText, "textures", texturesObject)) {
return false;
}
if (!TryParseStringMapObject(
texturesObject,
[material](const Containers::String& name, const Containers::String& value) {
TryApplyTexturePath(material, name, value);
})) {
return false;
}
}
return true;
}
bool TryParseCullMode(const Containers::String& value, MaterialCullMode& outMode) {
const Containers::String normalized = value.Trim().ToLower();
if (normalized == "none" || normalized == "off") {
@@ -953,6 +1123,10 @@ bool MaterialLoader::ParseMaterialData(const Containers::Array<Core::uint8>& dat
}
}
if (!TryParseMaterialTextureBindings(jsonText, material)) {
return false;
}
return true;
}