Tighten editor scene backend write boundary

This commit is contained in:
2026-04-28 18:32:07 +08:00
parent b67af931de
commit d1a717091d
5 changed files with 453 additions and 196 deletions

View File

@@ -11,11 +11,13 @@
#include <XCEngine/Scene/SceneManager.h>
#include <algorithm>
#include <charconv>
#include <filesystem>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -29,11 +31,15 @@ using ::XCEngine::Components::GameObject;
using ::XCEngine::Components::Scene;
using ::XCEngine::Components::SceneManager;
using ::XCEngine::Components::TransformComponent;
using ::XCEngine::Math::Quaternion;
using ::XCEngine::Math::Vector3;
using ::XCEngine::Rendering::RenderObjectId;
using ::XCEngine::Rendering::RenderObjectIdRegistry;
using ::XCEngine::Resources::ResourceManager;
using ::XCEngine::Resources::Shader;
constexpr char kComponentIdSeparator = '#';
void TraceSceneStartup(std::string message) {
::XCEngine::UI::Editor::AppendUIEditorRuntimeTrace("startup", std::move(message));
}
@@ -207,6 +213,82 @@ EditorSceneHierarchyNode BuildHierarchySnapshotNodeRecursive(
return node;
}
std::string BuildEditorComponentId(
std::string_view typeName,
std::size_t ordinal) {
std::string componentId(typeName);
componentId.push_back(kComponentIdSeparator);
componentId += std::to_string(ordinal);
return componentId;
}
bool ParseEditorComponentId(
std::string_view componentId,
std::string& outTypeName,
std::size_t& outOrdinal) {
const std::size_t separatorIndex = componentId.find(kComponentIdSeparator);
if (separatorIndex == std::string_view::npos ||
separatorIndex == 0u ||
separatorIndex + 1u >= componentId.size()) {
return false;
}
outTypeName = std::string(componentId.substr(0u, separatorIndex));
std::size_t ordinal = 0u;
const std::string_view ordinalText =
componentId.substr(separatorIndex + 1u);
const char* first = ordinalText.data();
const char* last = ordinalText.data() + ordinalText.size();
const std::from_chars_result result =
std::from_chars(first, last, ordinal);
if (result.ec != std::errc() || result.ptr != last) {
outTypeName.clear();
return false;
}
outOrdinal = ordinal;
return true;
}
EditorSceneComponentDescriptor BuildComponentDescriptor(
const Component& component,
std::size_t ordinal) {
EditorSceneComponentDescriptor descriptor = {};
descriptor.typeName = component.GetName();
descriptor.componentId =
BuildEditorComponentId(descriptor.typeName, ordinal);
descriptor.component = &component;
descriptor.removable =
component.GetGameObject() != nullptr &&
component.GetGameObject()->GetTransform() != &component;
return descriptor;
}
EditorSceneComponentDescriptor ResolveComponentDescriptor(
const GameObject& gameObject,
std::string_view componentId) {
std::string typeName = {};
std::size_t ordinal = 0u;
if (!ParseEditorComponentId(componentId, typeName, ordinal)) {
return {};
}
std::size_t currentOrdinal = 0u;
for (const Component* component : gameObject.GetComponents<Component>()) {
if (component == nullptr || component->GetName() != typeName) {
continue;
}
if (currentOrdinal == ordinal) {
return BuildComponentDescriptor(*component, currentOrdinal);
}
++currentOrdinal;
}
return {};
}
bool MoveGameObjectRelativeToTarget(
SceneManager& sceneManager,
std::string_view itemId,
@@ -403,6 +485,179 @@ public:
return addedComponent != nullptr;
}
std::vector<EditorSceneComponentDescriptor> GetComponents(
std::string_view itemId) const override {
std::vector<EditorSceneComponentDescriptor> descriptors = {};
const GameObject* gameObject = FindGameObject(itemId);
if (gameObject == nullptr) {
return descriptors;
}
const std::vector<const Component*> components =
gameObject->GetComponents<Component>();
descriptors.reserve(components.size());
std::unordered_map<std::string, std::size_t> ordinalsByType = {};
for (const Component* component : components) {
if (component == nullptr) {
continue;
}
const std::string typeName = component->GetName();
const std::size_t ordinal = ordinalsByType[typeName];
descriptors.push_back(BuildComponentDescriptor(*component, ordinal));
ordinalsByType[typeName] = ordinal + 1u;
}
return descriptors;
}
bool RemoveComponent(
std::string_view itemId,
std::string_view componentId) override {
GameObject* gameObject = FindGameObject(itemId);
if (gameObject == nullptr) {
return false;
}
const EditorSceneComponentDescriptor descriptor =
ResolveComponentDescriptor(*gameObject, componentId);
Component* component =
const_cast<Component*>(descriptor.component);
if (!descriptor.IsValid() ||
!descriptor.removable ||
component == nullptr) {
return false;
}
return gameObject->RemoveComponent(component);
}
bool SetTransformLocalPosition(
std::string_view itemId,
std::string_view componentId,
const Vector3& position) override {
TransformComponent* transform =
ResolveTransformComponent(itemId, componentId);
if (transform == nullptr) {
return false;
}
transform->SetLocalPosition(position);
return true;
}
bool SetTransformLocalEulerAngles(
std::string_view itemId,
std::string_view componentId,
const Vector3& eulerAngles) override {
TransformComponent* transform =
ResolveTransformComponent(itemId, componentId);
if (transform == nullptr) {
return false;
}
transform->SetLocalEulerAngles(eulerAngles);
return true;
}
bool SetTransformLocalScale(
std::string_view itemId,
std::string_view componentId,
const Vector3& scale) override {
TransformComponent* transform =
ResolveTransformComponent(itemId, componentId);
if (transform == nullptr) {
return false;
}
transform->SetLocalScale(scale);
return true;
}
bool MutateComponent(
std::string_view itemId,
std::string_view componentId,
const std::function<bool(Component&)>& mutation) override {
if (!mutation) {
return false;
}
GameObject* gameObject = FindGameObject(itemId);
if (gameObject == nullptr) {
return false;
}
const EditorSceneComponentDescriptor descriptor =
ResolveComponentDescriptor(*gameObject, componentId);
Component* component =
const_cast<Component*>(descriptor.component);
return descriptor.IsValid() &&
component != nullptr &&
mutation(*component);
}
bool QueryWorldTransform(
EditorSceneObjectId objectId,
Vector3& outPosition,
Quaternion& outRotation,
Vector3& outScale) const override {
Scene* scene = ResolvePrimaryScene(m_sceneManager);
GameObject* gameObject =
scene != nullptr ? scene->FindByID(objectId) : nullptr;
const TransformComponent* transform =
gameObject != nullptr ? gameObject->GetTransform() : nullptr;
if (transform == nullptr) {
return false;
}
outPosition = transform->GetPosition();
outRotation = transform->GetRotation();
outScale = transform->GetScale();
return true;
}
bool SetWorldTransform(
EditorSceneObjectId objectId,
const Vector3& position,
const Quaternion& rotation,
const Vector3& scale) override {
TransformComponent* transform = ResolveTransformComponent(objectId);
if (transform == nullptr) {
return false;
}
transform->SetPosition(position);
transform->SetRotation(rotation);
transform->SetScale(scale);
return true;
}
bool SetWorldPositionRotation(
EditorSceneObjectId objectId,
const Vector3& position,
const Quaternion& rotation) override {
TransformComponent* transform = ResolveTransformComponent(objectId);
if (transform == nullptr) {
return false;
}
transform->SetPosition(position);
transform->SetRotation(rotation);
return true;
}
bool SetObjectLocalScale(
EditorSceneObjectId objectId,
const Vector3& localScale) override {
TransformComponent* transform = ResolveTransformComponent(objectId);
if (transform == nullptr) {
return false;
}
transform->SetLocalScale(localScale);
return true;
}
bool RenameGameObject(
std::string_view itemId,
std::string_view newName) override {
@@ -488,6 +743,27 @@ public:
}
private:
TransformComponent* ResolveTransformComponent(EditorSceneObjectId objectId) const {
Scene* scene = ResolvePrimaryScene(m_sceneManager);
GameObject* gameObject =
scene != nullptr ? scene->FindByID(objectId) : nullptr;
return gameObject != nullptr ? gameObject->GetTransform() : nullptr;
}
TransformComponent* ResolveTransformComponent(
std::string_view itemId,
std::string_view componentId) const {
GameObject* gameObject = FindGameObject(itemId);
if (gameObject == nullptr) {
return nullptr;
}
const EditorSceneComponentDescriptor descriptor =
ResolveComponentDescriptor(*gameObject, componentId);
return const_cast<TransformComponent*>(
dynamic_cast<const TransformComponent*>(descriptor.component));
}
SceneManager& m_sceneManager;
ResourceManager& m_resourceManager;
};