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

@@ -4,7 +4,11 @@
#include "Core/ISceneManager.h"
#include "Core/ISelectionManager.h"
#include "IViewportHostService.h"
#include "Passes/SceneViewportEditorOverlayPass.h"
#include "SceneViewportCameraController.h"
#include "SceneViewportEditorOverlayData.h"
#include "SceneViewportOverlayHandleBuilder.h"
#include "SceneViewportOverlayBuilder.h"
#include "ViewportHostRenderFlowUtils.h"
#include "ViewportHostRenderTargets.h"
#include "ViewportObjectIdPicker.h"
@@ -12,6 +16,7 @@
#include <XCEngine/Components/CameraComponent.h>
#include <XCEngine/Components/GameObject.h>
#include <XCEngine/Components/LightComponent.h>
#include <XCEngine/Core/Asset/ResourceManager.h>
#include <XCEngine/RHI/RHIDevice.h>
#include <XCEngine/RHI/RHIEnums.h>
@@ -24,7 +29,9 @@
#include <XCEngine/Scene/Scene.h>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
@@ -35,6 +42,133 @@ namespace Editor {
namespace {
constexpr bool kDebugSceneSelectionMask = false;
constexpr uint64_t kSceneViewportOverlaySignatureOffsetBasis = 14695981039346656037ull;
constexpr uint64_t kSceneViewportOverlaySignaturePrime = 1099511628211ull;
void HashSceneViewportOverlayBytes(uint64_t& hash, const void* data, size_t size) {
const auto* bytes = static_cast<const uint8_t*>(data);
for (size_t index = 0; index < size; ++index) {
hash ^= static_cast<uint64_t>(bytes[index]);
hash *= kSceneViewportOverlaySignaturePrime;
}
}
template <typename TValue>
void HashSceneViewportOverlayValue(uint64_t& hash, const TValue& value) {
HashSceneViewportOverlayBytes(hash, &value, sizeof(TValue));
}
void HashSceneViewportOverlayFloat(uint64_t& hash, float value) {
uint32_t bits = 0u;
std::memcpy(&bits, &value, sizeof(bits));
HashSceneViewportOverlayValue(hash, bits);
}
void HashSceneViewportOverlayVector3(uint64_t& hash, const Math::Vector3& value) {
HashSceneViewportOverlayFloat(hash, value.x);
HashSceneViewportOverlayFloat(hash, value.y);
HashSceneViewportOverlayFloat(hash, value.z);
}
void HashSceneViewportOverlayQuaternion(uint64_t& hash, const Math::Quaternion& value) {
HashSceneViewportOverlayFloat(hash, value.x);
HashSceneViewportOverlayFloat(hash, value.y);
HashSceneViewportOverlayFloat(hash, value.z);
HashSceneViewportOverlayFloat(hash, value.w);
}
void HashSceneViewportOverlayRect(uint64_t& hash, const Math::Rect& value) {
HashSceneViewportOverlayFloat(hash, value.x);
HashSceneViewportOverlayFloat(hash, value.y);
HashSceneViewportOverlayFloat(hash, value.width);
HashSceneViewportOverlayFloat(hash, value.height);
}
void HashSceneViewportOverlayTransform(uint64_t& hash, const Components::TransformComponent& transform) {
HashSceneViewportOverlayVector3(hash, transform.GetPosition());
HashSceneViewportOverlayQuaternion(hash, transform.GetRotation());
HashSceneViewportOverlayVector3(hash, transform.GetScale());
}
uint64_t BuildSceneViewEditorOverlayContentSignature(
const Components::Scene* scene,
const std::vector<uint64_t>& selectedObjectIds) {
uint64_t hash = kSceneViewportOverlaySignatureOffsetBasis;
HashSceneViewportOverlayValue(hash, static_cast<uint64_t>(selectedObjectIds.size()));
for (uint64_t entityId : selectedObjectIds) {
HashSceneViewportOverlayValue(hash, entityId);
}
if (scene == nullptr) {
return hash;
}
for (Components::CameraComponent* camera : scene->FindObjectsOfType<Components::CameraComponent>()) {
Components::GameObject* gameObject = camera != nullptr ? camera->GetGameObject() : nullptr;
HashSceneViewportOverlayValue(hash, static_cast<uint8_t>(1u));
HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull);
HashSceneViewportOverlayValue(hash, camera != nullptr && camera->IsEnabled());
HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy());
if (camera == nullptr ||
gameObject == nullptr ||
!camera->IsEnabled() ||
!gameObject->IsActiveInHierarchy() ||
gameObject->GetTransform() == nullptr) {
continue;
}
HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform());
HashSceneViewportOverlayValue(hash, static_cast<uint32_t>(camera->GetProjectionType()));
HashSceneViewportOverlayFloat(hash, camera->GetFieldOfView());
HashSceneViewportOverlayFloat(hash, camera->GetOrthographicSize());
HashSceneViewportOverlayFloat(hash, camera->GetNearClipPlane());
HashSceneViewportOverlayFloat(hash, camera->GetFarClipPlane());
HashSceneViewportOverlayRect(hash, camera->GetViewportRect());
}
for (Components::LightComponent* light : scene->FindObjectsOfType<Components::LightComponent>()) {
Components::GameObject* gameObject = light != nullptr ? light->GetGameObject() : nullptr;
HashSceneViewportOverlayValue(hash, static_cast<uint8_t>(2u));
HashSceneViewportOverlayValue(hash, gameObject != nullptr ? gameObject->GetID() : 0ull);
HashSceneViewportOverlayValue(hash, light != nullptr && light->IsEnabled());
HashSceneViewportOverlayValue(hash, gameObject != nullptr && gameObject->IsActiveInHierarchy());
if (light == nullptr ||
gameObject == nullptr ||
!light->IsEnabled() ||
!gameObject->IsActiveInHierarchy() ||
gameObject->GetTransform() == nullptr) {
continue;
}
HashSceneViewportOverlayTransform(hash, *gameObject->GetTransform());
HashSceneViewportOverlayValue(hash, static_cast<uint32_t>(light->GetLightType()));
}
return hash;
}
bool AreEqualSceneViewportVector3(const Math::Vector3& lhs, const Math::Vector3& rhs) {
constexpr float kEpsilon = 1e-4f;
return std::abs(lhs.x - rhs.x) <= kEpsilon &&
std::abs(lhs.y - rhs.y) <= kEpsilon &&
std::abs(lhs.z - rhs.z) <= kEpsilon;
}
bool AreEqualSceneViewportOverlayData(
const SceneViewportOverlayData& lhs,
const SceneViewportOverlayData& rhs) {
constexpr float kEpsilon = 1e-4f;
return lhs.valid == rhs.valid &&
AreEqualSceneViewportVector3(lhs.cameraPosition, rhs.cameraPosition) &&
AreEqualSceneViewportVector3(lhs.cameraForward, rhs.cameraForward) &&
AreEqualSceneViewportVector3(lhs.cameraRight, rhs.cameraRight) &&
AreEqualSceneViewportVector3(lhs.cameraUp, rhs.cameraUp) &&
std::abs(lhs.verticalFovDegrees - rhs.verticalFovDegrees) <= kEpsilon &&
std::abs(lhs.nearClipPlane - rhs.nearClipPlane) <= kEpsilon &&
std::abs(lhs.farClipPlane - rhs.farClipPlane) <= kEpsilon &&
std::abs(lhs.orbitDistance - rhs.orbitDistance) <= kEpsilon;
}
Math::Vector3 GetSceneViewportOrientationAxisVector(SceneViewportOrientationAxis axis) {
switch (axis) {
@@ -71,7 +205,9 @@ public:
entry = {};
}
m_sceneViewportEditorOverlayRenderer.Shutdown();
m_sceneViewCamera = {};
ResetSceneViewEditorOverlayFrameData();
m_sceneViewLastRenderContext = {};
m_device = nullptr;
m_backend = nullptr;
@@ -84,6 +220,8 @@ public:
entry.requestedWidth = 0;
entry.requestedHeight = 0;
}
m_sceneViewTransientTransformGizmoOverlay = {};
m_sceneViewTransientTransformGizmoInputs = {};
}
EditorViewportFrame RequestViewport(EditorViewportKind kind, const ImVec2& requestedSize) override {
@@ -212,6 +350,30 @@ public:
return data;
}
const SceneViewportOverlayFrameData& GetSceneViewEditorOverlayFrameData(IEditorContext& context) override {
EnsureSceneViewEditorOverlayFrameData(context);
return m_sceneViewEditorOverlayFrameData;
}
const SceneViewportOverlayFrameData& GetSceneViewInteractionOverlayFrameData(
IEditorContext& context,
const SceneViewportOverlayData& overlay,
const SceneViewportTransformGizmoHandleBuildInputs& inputs) override {
EnsureSceneViewEditorOverlayFrameData(context);
m_sceneViewInteractionOverlayFrameData = m_sceneViewEditorOverlayFrameData;
AppendSceneViewportOverlayFrameData(
m_sceneViewInteractionOverlayFrameData,
BuildSceneViewportTransformGizmoOverlayFrameData(overlay, inputs));
return m_sceneViewInteractionOverlayFrameData;
}
void SetSceneViewTransientTransformGizmoOverlayData(
const SceneViewportOverlayData& overlay,
const SceneViewportTransformGizmoHandleBuildInputs& inputs) override {
m_sceneViewTransientTransformGizmoOverlay = overlay;
m_sceneViewTransientTransformGizmoInputs = inputs;
}
void RenderRequestedViewports(
IEditorContext& context,
const Rendering::RenderContext& renderContext) override {
@@ -256,6 +418,7 @@ private:
struct SceneViewportRenderState {
SceneViewportOverlayData overlay = {};
Rendering::BuiltinPostProcessRequest builtinPostProcess = {};
SceneViewportOverlayFrameData editorOverlayFrameData = {};
std::vector<uint64_t> selectedObjectIds;
};
@@ -365,6 +528,86 @@ private:
return BuildViewportColorSurface(entry.renderTargets);
}
void ResetSceneViewEditorOverlayFrameData() {
m_sceneViewEditorOverlayFrameData = {};
m_sceneViewInteractionOverlayFrameData = {};
m_sceneViewEditorOverlayScene = nullptr;
m_sceneViewEditorOverlaySelectedObjectIds.clear();
m_sceneViewEditorOverlayViewportWidth = 0u;
m_sceneViewEditorOverlayViewportHeight = 0u;
m_sceneViewEditorOverlayContentSignature = 0u;
m_sceneViewEditorOverlayCached = false;
}
void ResolveSceneViewEditorOverlayViewportSize(
const ViewportEntry& entry,
uint32_t& outWidth,
uint32_t& outHeight) const {
outWidth = entry.requestedWidth > 0u ? entry.requestedWidth : entry.renderTargets.width;
outHeight = entry.requestedHeight > 0u ? entry.requestedHeight : entry.renderTargets.height;
}
bool ShouldRebuildSceneViewEditorOverlayFrameData(
const Components::Scene* scene,
const SceneViewportOverlayData& overlay,
uint32_t viewportWidth,
uint32_t viewportHeight,
const std::vector<uint64_t>& selectedObjectIds,
uint64_t contentSignature) const {
return !m_sceneViewEditorOverlayCached ||
m_sceneViewEditorOverlayScene != scene ||
m_sceneViewEditorOverlayViewportWidth != viewportWidth ||
m_sceneViewEditorOverlayViewportHeight != viewportHeight ||
m_sceneViewEditorOverlaySelectedObjectIds != selectedObjectIds ||
m_sceneViewEditorOverlayContentSignature != contentSignature ||
!AreEqualSceneViewportOverlayData(m_sceneViewEditorOverlayFrameData.overlay, overlay);
}
void EnsureSceneViewEditorOverlayFrameData(IEditorContext& context) {
if (!EnsureSceneViewCamera()) {
ResetSceneViewEditorOverlayFrameData();
return;
}
const ViewportEntry& entry = GetEntry(EditorViewportKind::Scene);
uint32_t viewportWidth = 0u;
uint32_t viewportHeight = 0u;
ResolveSceneViewEditorOverlayViewportSize(entry, viewportWidth, viewportHeight);
const Components::Scene* scene = context.GetSceneManager().GetScene();
const SceneViewportOverlayData overlay = GetSceneViewOverlayData();
const std::vector<uint64_t> selectedObjectIds = context.GetSelectionManager().GetSelectedEntities();
const uint64_t contentSignature =
BuildSceneViewEditorOverlayContentSignature(scene, selectedObjectIds);
if (!ShouldRebuildSceneViewEditorOverlayFrameData(
scene,
overlay,
viewportWidth,
viewportHeight,
selectedObjectIds,
contentSignature)) {
return;
}
m_sceneViewEditorOverlayFrameData = {};
m_sceneViewEditorOverlayFrameData.overlay = overlay;
if (scene != nullptr && overlay.valid && viewportWidth > 0u && viewportHeight > 0u) {
m_sceneViewEditorOverlayFrameData = SceneViewportOverlayBuilder::Build(
context,
overlay,
viewportWidth,
viewportHeight,
selectedObjectIds);
}
m_sceneViewEditorOverlayScene = scene;
m_sceneViewEditorOverlaySelectedObjectIds = selectedObjectIds;
m_sceneViewEditorOverlayViewportWidth = viewportWidth;
m_sceneViewEditorOverlayViewportHeight = viewportHeight;
m_sceneViewEditorOverlayContentSignature = contentSignature;
m_sceneViewEditorOverlayCached = true;
}
void ApplyViewportRenderFailure(
ViewportEntry& entry,
const Rendering::RenderContext& renderContext,
@@ -399,6 +642,7 @@ private:
}
outState.selectedObjectIds = context.GetSelectionManager().GetSelectedEntities();
outState.editorOverlayFrameData = GetSceneViewEditorOverlayFrameData(context);
const SceneViewportBuiltinPostProcessBuildResult builtinPostProcess =
BuildSceneViewportBuiltinPostProcess(
outState.overlay,
@@ -456,6 +700,18 @@ private:
&sceneState.builtinPostProcess,
nullptr,
requests[0]);
SceneViewportOverlayFrameData renderOverlayFrameData = sceneState.editorOverlayFrameData;
AppendSceneViewportOverlayFrameData(
renderOverlayFrameData,
BuildSceneViewTransientTransformGizmoOverlayFrameData());
Rendering::RenderPassSequence overlayPassSequence = {};
if (renderOverlayFrameData.HasOverlayPrimitives()) {
overlayPassSequence.AddPass(
CreateSceneViewportEditorOverlayPass(
m_sceneViewportEditorOverlayRenderer,
renderOverlayFrameData));
requests[0].overlayPasses = &overlayPassSequence;
}
requests[0].hasClearColorOverride = true;
requests[0].clearColorOverride = Math::Color(0.27f, 0.27f, 0.27f, 1.0f);
@@ -600,12 +856,29 @@ private:
});
}
SceneViewportOverlayFrameData BuildSceneViewTransientTransformGizmoOverlayFrameData() const {
return BuildSceneViewportTransformGizmoOverlayFrameData(
m_sceneViewTransientTransformGizmoOverlay,
m_sceneViewTransientTransformGizmoInputs);
}
UI::ImGuiBackendBridge* m_backend = nullptr;
RHI::RHIDevice* m_device = nullptr;
std::unique_ptr<Rendering::SceneRenderer> m_sceneRenderer;
Rendering::RenderContext m_sceneViewLastRenderContext = {};
std::array<ViewportEntry, 2> m_entries = {};
SceneViewCameraState m_sceneViewCamera;
SceneViewportOverlayFrameData m_sceneViewEditorOverlayFrameData = {};
SceneViewportOverlayFrameData m_sceneViewInteractionOverlayFrameData = {};
SceneViewportOverlayData m_sceneViewTransientTransformGizmoOverlay = {};
SceneViewportTransformGizmoHandleBuildInputs m_sceneViewTransientTransformGizmoInputs = {};
const Components::Scene* m_sceneViewEditorOverlayScene = nullptr;
std::vector<uint64_t> m_sceneViewEditorOverlaySelectedObjectIds = {};
uint32_t m_sceneViewEditorOverlayViewportWidth = 0u;
uint32_t m_sceneViewEditorOverlayViewportHeight = 0u;
uint64_t m_sceneViewEditorOverlayContentSignature = 0u;
bool m_sceneViewEditorOverlayCached = false;
SceneViewportEditorOverlayPassRenderer m_sceneViewportEditorOverlayRenderer;
};
} // namespace Editor