Files
XCEngine/editor/app/Features/Scene/SceneViewportSceneOverlay.cpp

202 lines
5.9 KiB
C++
Raw Normal View History

2026-04-27 19:16:08 +08:00
#include "Scene/SceneViewportSceneOverlay.h"
2026-04-27 19:16:08 +08:00
#include "Scene/SceneViewportTransformGizmoSupport.h"
2026-04-28 02:57:49 +08:00
#include "Assets/EditorIconService.h"
#include "Scene/EditorSceneRuntime.h"
#include "Scene/SceneViewportSession.h"
#include <algorithm>
#include <utility>
namespace XCEngine::UI::Editor::App {
namespace {
using ::XCEngine::Math::Vector2;
using ::XCEngine::UI::UIPoint;
using ::XCEngine::UI::UIRect;
namespace SceneViewportGizmoSupport = ::XCEngine::UI::Editor::App::SceneViewportGizmoSupport;
constexpr Vector2 kCameraIconSize(90.0f, 90.0f);
constexpr Vector2 kLightIconSize(100.0f, 100.0f);
bool ContainsPoint(const UIRect& rect, const UIPoint& point) {
return point.x >= rect.x &&
point.x <= rect.x + rect.width &&
point.y >= rect.y &&
point.y <= rect.y + rect.height;
}
SceneViewportGizmoSupport::SceneViewportOverlayData BuildOverlayData(
const SceneViewportSession& session) {
SceneViewportGizmoSupport::SceneViewportOverlayData overlay = {};
const EditorSceneCameraSnapshot snapshot = session.BuildCameraSnapshot();
2026-04-29 01:24:21 +08:00
if (!snapshot.valid) {
return overlay;
}
overlay.valid = true;
2026-04-29 01:24:21 +08:00
overlay.cameraPosition = snapshot.position;
overlay.cameraForward = snapshot.forward;
overlay.cameraRight = snapshot.right;
overlay.cameraUp = snapshot.up;
overlay.verticalFovDegrees = snapshot.verticalFovDegrees;
overlay.nearClipPlane = snapshot.nearClipPlane;
overlay.farClipPlane = snapshot.farClipPlane;
overlay.orbitDistance = snapshot.orbitDistance;
return overlay;
}
2026-04-29 01:24:21 +08:00
::XCEngine::UI::UITextureHandle ResolveViewportIcon(
2026-04-28 02:57:49 +08:00
const EditorIconService* icons,
2026-04-29 01:24:21 +08:00
EditorSceneViewportIconKind kind) {
if (icons == nullptr) {
return {};
}
2026-04-29 01:24:21 +08:00
switch (kind) {
case EditorSceneViewportIconKind::Camera:
return icons->Resolve(BuiltInIconKind::CameraGizmo);
case EditorSceneViewportIconKind::DirectionalLight:
return icons->Resolve(BuiltInIconKind::DirectionalLightGizmo);
2026-04-29 01:24:21 +08:00
case EditorSceneViewportIconKind::PointLight:
return icons->Resolve(BuiltInIconKind::PointLightGizmo);
2026-04-29 01:24:21 +08:00
case EditorSceneViewportIconKind::SpotLight:
return icons->Resolve(BuiltInIconKind::SpotLightGizmo);
default:
return {};
}
}
2026-04-29 01:24:21 +08:00
Vector2 ResolveViewportIconSize(EditorSceneViewportIconKind kind) {
return kind == EditorSceneViewportIconKind::Camera
? kCameraIconSize
: kLightIconSize;
}
} // namespace
2026-04-28 02:57:49 +08:00
void SceneViewportSceneOverlay::SetIconService(const EditorIconService* icons) {
m_icons = icons;
if (m_icons == nullptr) {
ResetFrame();
}
}
void SceneViewportSceneOverlay::ResetFrame() {
m_frame = {};
}
void SceneViewportSceneOverlay::Refresh(
const EditorSceneRuntime& sceneRuntime,
const SceneViewportSession& session,
const UIRect& viewportRect) {
m_frame = {};
m_frame.clipRect = viewportRect;
if (m_icons == nullptr ||
viewportRect.width <= 1.0f ||
viewportRect.height <= 1.0f) {
return;
}
const SceneViewportGizmoSupport::SceneViewportOverlayData overlay =
BuildOverlayData(session);
if (!overlay.valid) {
return;
}
std::vector<IconFrame> icons = {};
const auto tryAppendIcon =
2026-04-29 01:24:21 +08:00
[&](const EditorSceneViewportIconSnapshot& snapshot) {
const ::XCEngine::UI::UITextureHandle texture =
ResolveViewportIcon(m_icons, snapshot.kind);
if (!texture.IsValid()) {
return;
}
2026-04-29 01:24:21 +08:00
const Vector2 iconSize = ResolveViewportIconSize(snapshot.kind);
const SceneViewportGizmoSupport::SceneViewportProjectedPoint projectedPoint =
SceneViewportGizmoSupport::ProjectSceneViewportWorldPoint(
overlay,
viewportRect.width,
viewportRect.height,
2026-04-29 01:24:21 +08:00
snapshot.worldPosition);
if (!projectedPoint.visible) {
return;
}
IconFrame icon = {};
2026-04-29 01:24:21 +08:00
icon.entityId = snapshot.entityId;
icon.rect = UIRect(
viewportRect.x + projectedPoint.screenPosition.x - iconSize.x * 0.5f,
viewportRect.y + projectedPoint.screenPosition.y - iconSize.y * 0.5f,
iconSize.x,
iconSize.y);
icon.texture = texture;
icon.sortDepth = projectedPoint.ndcDepth;
icons.push_back(std::move(icon));
};
2026-04-29 01:24:21 +08:00
for (const EditorSceneViewportIconSnapshot& snapshot :
sceneRuntime.BuildSceneViewportIconSnapshots()) {
if (!snapshot.IsValid()) {
continue;
}
2026-04-29 01:24:21 +08:00
tryAppendIcon(snapshot);
}
if (icons.empty()) {
return;
}
std::sort(
icons.begin(),
icons.end(),
[](const IconFrame& lhs, const IconFrame& rhs) {
return lhs.sortDepth > rhs.sortDepth;
});
m_frame.visible = true;
m_frame.icons = std::move(icons);
}
SceneViewportSceneOverlay::HitResult SceneViewportSceneOverlay::HitTest(
const UIPoint& point) const {
HitResult result = {};
if (!m_frame.visible) {
return result;
}
constexpr float kMetricEpsilon = 0.001f;
for (const IconFrame& icon : m_frame.icons) {
if (icon.entityId == 0 ||
!icon.texture.IsValid() ||
!ContainsPoint(icon.rect, point)) {
continue;
}
if (!result.HasHit() ||
icon.sortDepth + kMetricEpsilon < result.sortDepth) {
result.entityId = icon.entityId;
result.sortDepth = icon.sortDepth;
}
}
return result;
}
void SceneViewportSceneOverlay::Append(::XCEngine::UI::UIDrawList& drawList) const {
if (!m_frame.visible || m_frame.icons.empty()) {
return;
}
drawList.PushClipRect(m_frame.clipRect);
for (const IconFrame& icon : m_frame.icons) {
drawList.AddImage(icon.rect, icon.texture);
}
drawList.PopClipRect();
}
} // namespace XCEngine::UI::Editor::App