Tighten editor scene backend write boundary
This commit is contained in:
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user