#include "Features/Scene/SceneViewportSceneOverlay.h" #include "Features/Scene/SceneViewportTransformGizmoSupport.h" #include "Rendering/Assets/BuiltInIcons.h" #include "Scene/EditorSceneRuntime.h" #include #include #include #include #include #include #include namespace XCEngine::UI::Editor::App { namespace { using ::XCEngine::Components::CameraComponent; using ::XCEngine::Components::GameObject; using ::XCEngine::Components::LightComponent; using ::XCEngine::Components::Scene; using ::XCEngine::Components::TransformComponent; 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 CanBuildSceneIcon(const GameObject* gameObject) { return gameObject != nullptr && gameObject->GetTransform() != nullptr && gameObject->IsActiveInHierarchy(); } 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 CameraComponent* camera = sceneRuntime.GetSceneViewCamera(); if (camera == nullptr || camera->GetGameObject() == nullptr) { return overlay; } const TransformComponent* transform = camera->GetGameObject()->GetTransform(); if (transform == nullptr) { return overlay; } overlay.valid = true; overlay.cameraPosition = transform->GetPosition(); overlay.cameraForward = transform->GetForward(); overlay.cameraRight = transform->GetRight(); overlay.cameraUp = transform->GetUp(); overlay.verticalFovDegrees = camera->GetFieldOfView(); overlay.nearClipPlane = camera->GetNearClipPlane(); overlay.farClipPlane = camera->GetFarClipPlane(); overlay.orbitDistance = sceneRuntime.GetSceneViewOrbitDistance(); return overlay; } ::XCEngine::UI::UITextureHandle ResolveCameraIcon(const BuiltInIcons* icons) { return icons != nullptr ? icons->Resolve(BuiltInIconKind::CameraGizmo) : ::XCEngine::UI::UITextureHandle(); } ::XCEngine::UI::UITextureHandle ResolveLightIcon( const BuiltInIcons* icons, const LightComponent& light) { if (icons == nullptr) { return {}; } switch (light.GetLightType()) { case ::XCEngine::Components::LightType::Directional: return icons->Resolve(BuiltInIconKind::DirectionalLightGizmo); case ::XCEngine::Components::LightType::Point: return icons->Resolve(BuiltInIconKind::PointLightGizmo); case ::XCEngine::Components::LightType::Spot: return icons->Resolve(BuiltInIconKind::SpotLightGizmo); default: return {}; } } } // namespace void SceneViewportSceneOverlay::SetBuiltInIcons(const BuiltInIcons* 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; } Scene* scene = sceneRuntime.GetActiveScene(); if (scene == nullptr) { return; } const SceneViewportGizmoSupport::SceneViewportOverlayData overlay = BuildOverlayData(sceneRuntime); if (!overlay.valid) { return; } std::vector icons = {}; const auto tryAppendIcon = [&](const GameObject& gameObject, const Vector2& iconSize, const ::XCEngine::UI::UITextureHandle& texture) { if (!texture.IsValid()) { return; } const TransformComponent* transform = gameObject.GetTransform(); if (transform == nullptr) { return; } const SceneViewportGizmoSupport::SceneViewportProjectedPoint projectedPoint = SceneViewportGizmoSupport::ProjectSceneViewportWorldPoint( overlay, viewportRect.width, viewportRect.height, transform->GetPosition()); if (!projectedPoint.visible) { return; } IconFrame icon = {}; icon.entityId = gameObject.GetID(); 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)); }; const ::XCEngine::UI::UITextureHandle cameraIcon = ResolveCameraIcon(m_icons); for (CameraComponent* camera : scene->FindObjectsOfType()) { if (camera == nullptr || !camera->IsEnabled()) { continue; } GameObject* gameObject = camera->GetGameObject(); if (!CanBuildSceneIcon(gameObject)) { continue; } tryAppendIcon(*gameObject, kCameraIconSize, cameraIcon); } for (LightComponent* light : scene->FindObjectsOfType()) { if (light == nullptr || !light->IsEnabled()) { continue; } GameObject* gameObject = light->GetGameObject(); if (!CanBuildSceneIcon(gameObject)) { continue; } tryAppendIcon( *gameObject, kLightIconSize, ResolveLightIcon(m_icons, *light)); } 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