Formalize scene viewport interaction resolver

This commit is contained in:
2026-04-03 17:16:16 +08:00
parent 27014e613e
commit 1ac2afb0bb
7 changed files with 345 additions and 156 deletions

View File

@@ -0,0 +1,167 @@
#include "SceneViewportInteractionResolver.h"
namespace XCEngine {
namespace Editor {
namespace {
struct SceneViewportInteractionCandidate {
SceneViewportInteractionResult interaction = {};
int priority = 0;
int secondaryPriority = 0;
float distanceSq = Math::FLOAT_MAX;
float depth = Math::FLOAT_MAX;
bool HasHit() const {
return interaction.HasHit();
}
};
bool IsBetterSceneViewportInteractionCandidate(
const SceneViewportInteractionCandidate& candidate,
const SceneViewportInteractionCandidate& current) {
constexpr float kMetricEpsilon = 0.001f;
if (!candidate.HasHit()) {
return false;
}
if (!current.HasHit()) {
return true;
}
if (candidate.priority != current.priority) {
return candidate.priority > current.priority;
}
if (candidate.distanceSq + kMetricEpsilon < current.distanceSq) {
return true;
}
if (current.distanceSq + kMetricEpsilon < candidate.distanceSq) {
return false;
}
if (candidate.depth + kMetricEpsilon < current.depth) {
return true;
}
if (current.depth + kMetricEpsilon < candidate.depth) {
return false;
}
return candidate.secondaryPriority > current.secondaryPriority;
}
void AccumulateSceneViewportInteractionCandidate(
const SceneViewportInteractionCandidate& candidate,
SceneViewportInteractionCandidate& bestCandidate) {
if (IsBetterSceneViewportInteractionCandidate(candidate, bestCandidate)) {
bestCandidate = candidate;
}
}
SceneViewportInteractionCandidate BuildHudOverlayInteractionCandidate(
const SceneViewportHudOverlayHitResult& hitResult) {
SceneViewportInteractionCandidate candidate = {};
switch (hitResult.kind) {
case SceneViewportHudOverlayHitKind::OrientationAxis:
if (hitResult.orientationAxis == SceneViewportOrientationAxis::None) {
return candidate;
}
candidate.interaction.kind = SceneViewportInteractionKind::OrientationGizmo;
candidate.interaction.orientationAxis = hitResult.orientationAxis;
candidate.priority = 200;
candidate.distanceSq = 0.0f;
candidate.depth = 0.0f;
return candidate;
case SceneViewportHudOverlayHitKind::None:
default:
return candidate;
}
}
SceneViewportInteractionCandidate BuildOverlayHandleInteractionCandidate(
const SceneViewportOverlayHandleHitResult& hitResult) {
SceneViewportInteractionCandidate candidate = {};
if (!hitResult.HasHit()) {
return candidate;
}
candidate.priority = hitResult.priority;
candidate.distanceSq = hitResult.distanceSq;
candidate.depth = hitResult.depth;
candidate.interaction.entityId = hitResult.entityId;
switch (hitResult.kind) {
case SceneViewportOverlayHandleKind::SceneIcon:
candidate.interaction.kind = SceneViewportInteractionKind::SceneIcon;
return candidate;
case SceneViewportOverlayHandleKind::MoveAxis:
candidate.interaction.kind = SceneViewportInteractionKind::MoveGizmo;
candidate.interaction.moveAxis = static_cast<SceneViewportGizmoAxis>(hitResult.handleId);
return candidate;
case SceneViewportOverlayHandleKind::MovePlane:
candidate.interaction.kind = SceneViewportInteractionKind::MoveGizmo;
candidate.interaction.movePlane = static_cast<SceneViewportGizmoPlane>(hitResult.handleId);
return candidate;
case SceneViewportOverlayHandleKind::RotateAxis:
candidate.interaction.kind = SceneViewportInteractionKind::RotateGizmo;
candidate.interaction.rotateAxis = static_cast<SceneViewportRotateGizmoAxis>(hitResult.handleId);
return candidate;
case SceneViewportOverlayHandleKind::ScaleAxis:
case SceneViewportOverlayHandleKind::ScaleUniform:
candidate.interaction.kind = SceneViewportInteractionKind::ScaleGizmo;
candidate.interaction.scaleHandle = static_cast<SceneViewportScaleGizmoHandle>(hitResult.handleId);
return candidate;
case SceneViewportOverlayHandleKind::None:
default:
return candidate;
}
}
SceneViewportOverlayHandleHitResult HitTestSceneViewportOverlayInteractionHandles(
const SceneViewportInteractionResolveRequest& request) {
if (request.overlayFrameData == nullptr) {
return {};
}
return HitTestSceneViewportOverlayHandles(
*request.overlayFrameData,
request.viewportSize,
request.localMousePosition);
}
SceneViewportHudOverlayHitResult HitTestSceneViewportHudInteraction(
const SceneViewportInteractionResolveRequest& request) {
if (request.hudOverlay == nullptr) {
return {};
}
return HitTestSceneViewportHudOverlay(
*request.hudOverlay,
request.viewportMin,
request.viewportMax,
request.absoluteMousePosition);
}
} // namespace
SceneViewportInteractionResult ResolveSceneViewportInteraction(
const SceneViewportOverlayHandleHitResult& overlayHandleHit,
const SceneViewportHudOverlayHitResult& hudOverlayHit) {
SceneViewportInteractionCandidate bestCandidate = {};
AccumulateSceneViewportInteractionCandidate(
BuildOverlayHandleInteractionCandidate(overlayHandleHit),
bestCandidate);
AccumulateSceneViewportInteractionCandidate(
BuildHudOverlayInteractionCandidate(hudOverlayHit),
bestCandidate);
return bestCandidate.interaction;
}
SceneViewportInteractionResult ResolveSceneViewportInteraction(
const SceneViewportInteractionResolveRequest& request) {
return ResolveSceneViewportInteraction(
HitTestSceneViewportOverlayInteractionHandles(request),
HitTestSceneViewportHudInteraction(request));
}
} // namespace Editor
} // namespace XCEngine

View File

@@ -0,0 +1,57 @@
#pragma once
#include "SceneViewportHudOverlay.h"
#include "SceneViewportMoveGizmo.h"
#include "SceneViewportOverlayHitTester.h"
#include "SceneViewportRotateGizmo.h"
#include "SceneViewportScaleGizmo.h"
#include <imgui.h>
#include <cstdint>
namespace XCEngine {
namespace Editor {
enum class SceneViewportInteractionKind : uint8_t {
None = 0,
MoveGizmo,
RotateGizmo,
ScaleGizmo,
OrientationGizmo,
SceneIcon
};
struct SceneViewportInteractionResult {
SceneViewportInteractionKind kind = SceneViewportInteractionKind::None;
uint64_t entityId = 0;
SceneViewportGizmoAxis moveAxis = SceneViewportGizmoAxis::None;
SceneViewportGizmoPlane movePlane = SceneViewportGizmoPlane::None;
SceneViewportRotateGizmoAxis rotateAxis = SceneViewportRotateGizmoAxis::None;
SceneViewportScaleGizmoHandle scaleHandle = SceneViewportScaleGizmoHandle::None;
SceneViewportOrientationAxis orientationAxis = SceneViewportOrientationAxis::None;
bool HasHit() const {
return kind != SceneViewportInteractionKind::None;
}
};
struct SceneViewportInteractionResolveRequest {
const SceneViewportOverlayFrameData* overlayFrameData = nullptr;
Math::Vector2 viewportSize = Math::Vector2::Zero();
Math::Vector2 localMousePosition = Math::Vector2::Zero();
const SceneViewportHudOverlayData* hudOverlay = nullptr;
ImVec2 viewportMin = ImVec2(0.0f, 0.0f);
ImVec2 viewportMax = ImVec2(0.0f, 0.0f);
ImVec2 absoluteMousePosition = ImVec2(0.0f, 0.0f);
};
SceneViewportInteractionResult ResolveSceneViewportInteraction(
const SceneViewportOverlayHandleHitResult& overlayHandleHit,
const SceneViewportHudOverlayHitResult& hudOverlayHit);
SceneViewportInteractionResult ResolveSceneViewportInteraction(
const SceneViewportInteractionResolveRequest& request);
} // namespace Editor
} // namespace XCEngine