#include "Scene/SceneViewportSceneOverlay.h" #include "Scene/SceneViewportTransformGizmoSupport.h" #include "Assets/EditorIconService.h" #include "Scene/EditorSceneRuntime.h" #include #include 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 EditorSceneRuntime& sceneRuntime) { SceneViewportGizmoSupport::SceneViewportOverlayData overlay = {}; const EditorSceneCameraSnapshot snapshot = sceneRuntime.BuildSceneViewCameraSnapshot(); if (!snapshot.valid) { return overlay; } overlay.valid = true; 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; } ::XCEngine::UI::UITextureHandle ResolveViewportIcon( const EditorIconService* icons, EditorSceneViewportIconKind kind) { if (icons == nullptr) { return {}; } switch (kind) { case EditorSceneViewportIconKind::Camera: return icons->Resolve(BuiltInIconKind::CameraGizmo); case EditorSceneViewportIconKind::DirectionalLight: return icons->Resolve(BuiltInIconKind::DirectionalLightGizmo); case EditorSceneViewportIconKind::PointLight: return icons->Resolve(BuiltInIconKind::PointLightGizmo); case EditorSceneViewportIconKind::SpotLight: return icons->Resolve(BuiltInIconKind::SpotLightGizmo); default: return {}; } } Vector2 ResolveViewportIconSize(EditorSceneViewportIconKind kind) { return kind == EditorSceneViewportIconKind::Camera ? kCameraIconSize : kLightIconSize; } } // namespace void SceneViewportSceneOverlay::SetIconService(const EditorIconService* icons) { m_icons = icons; if (m_icons == nullptr) { ResetFrame(); } } void SceneViewportSceneOverlay::ResetFrame() { m_frame = {}; } void SceneViewportSceneOverlay::Refresh( EditorSceneRuntime& sceneRuntime, 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(sceneRuntime); if (!overlay.valid) { return; } std::vector icons = {}; const auto tryAppendIcon = [&](const EditorSceneViewportIconSnapshot& snapshot) { const ::XCEngine::UI::UITextureHandle texture = ResolveViewportIcon(m_icons, snapshot.kind); if (!texture.IsValid()) { return; } const Vector2 iconSize = ResolveViewportIconSize(snapshot.kind); const SceneViewportGizmoSupport::SceneViewportProjectedPoint projectedPoint = SceneViewportGizmoSupport::ProjectSceneViewportWorldPoint( overlay, viewportRect.width, viewportRect.height, snapshot.worldPosition); if (!projectedPoint.visible) { return; } IconFrame icon = {}; 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)); }; for (const EditorSceneViewportIconSnapshot& snapshot : sceneRuntime.BuildSceneViewportIconSnapshots()) { if (!snapshot.IsValid()) { continue; } 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