Formalize scene viewport interaction resolver
This commit is contained in:
167
editor/src/Viewport/SceneViewportInteractionResolver.cpp
Normal file
167
editor/src/Viewport/SceneViewportInteractionResolver.cpp
Normal 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
|
||||
57
editor/src/Viewport/SceneViewportInteractionResolver.h
Normal file
57
editor/src/Viewport/SceneViewportInteractionResolver.h
Normal 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
|
||||
Reference in New Issue
Block a user