Add scene viewport move gizmo workflow
This commit is contained in:
@@ -3,10 +3,19 @@
|
||||
#include "IViewportHostService.h"
|
||||
|
||||
#include <XCEngine/Core/Math/Matrix4.h>
|
||||
#include <XCEngine/Core/Math/Plane.h>
|
||||
#include <XCEngine/Core/Math/Vector2.h>
|
||||
#include <XCEngine/Core/Math/Vector4.h>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Editor {
|
||||
|
||||
struct SceneViewportProjectedPoint {
|
||||
Math::Vector2 screenPosition = Math::Vector2::Zero();
|
||||
float ndcDepth = 0.0f;
|
||||
bool visible = false;
|
||||
};
|
||||
|
||||
inline Math::Matrix4x4 BuildSceneViewportViewMatrix(const SceneViewportOverlayData& overlay) {
|
||||
const Math::Vector3 right = overlay.cameraRight.Normalized();
|
||||
const Math::Vector3 up = overlay.cameraUp.Normalized();
|
||||
@@ -44,5 +53,122 @@ inline Math::Matrix4x4 BuildSceneViewportProjectionMatrix(
|
||||
overlay.farClipPlane);
|
||||
}
|
||||
|
||||
inline Math::Matrix4x4 BuildSceneViewportViewProjectionMatrix(
|
||||
const SceneViewportOverlayData& overlay,
|
||||
float viewportWidth,
|
||||
float viewportHeight) {
|
||||
return BuildSceneViewportProjectionMatrix(overlay, viewportWidth, viewportHeight) *
|
||||
BuildSceneViewportViewMatrix(overlay);
|
||||
}
|
||||
|
||||
inline SceneViewportProjectedPoint ProjectSceneViewportWorldPoint(
|
||||
const SceneViewportOverlayData& overlay,
|
||||
float viewportWidth,
|
||||
float viewportHeight,
|
||||
const Math::Vector3& worldPoint) {
|
||||
SceneViewportProjectedPoint result = {};
|
||||
if (!overlay.valid || viewportWidth <= 1.0f || viewportHeight <= 1.0f) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const Math::Vector4 clipPoint =
|
||||
BuildSceneViewportViewProjectionMatrix(overlay, viewportWidth, viewportHeight) *
|
||||
Math::Vector4(worldPoint, 1.0f);
|
||||
if (clipPoint.w <= Math::EPSILON) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const Math::Vector3 ndcPoint = clipPoint.ToVector3() / clipPoint.w;
|
||||
result.screenPosition.x = (ndcPoint.x * 0.5f + 0.5f) * viewportWidth;
|
||||
result.screenPosition.y = (1.0f - (ndcPoint.y * 0.5f + 0.5f)) * viewportHeight;
|
||||
result.ndcDepth = ndcPoint.z;
|
||||
result.visible =
|
||||
ndcPoint.x >= -1.0f && ndcPoint.x <= 1.0f &&
|
||||
ndcPoint.y >= -1.0f && ndcPoint.y <= 1.0f &&
|
||||
ndcPoint.z >= 0.0f && ndcPoint.z <= 1.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool ProjectSceneViewportAxisDirection(
|
||||
const SceneViewportOverlayData& overlay,
|
||||
const Math::Vector3& worldAxis,
|
||||
Math::Vector2& outScreenDirection) {
|
||||
if (!overlay.valid) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Math::Vector3 viewAxis = BuildSceneViewportViewMatrix(overlay).MultiplyVector(worldAxis.Normalized());
|
||||
const Math::Vector2 screenDirection(viewAxis.x, -viewAxis.y);
|
||||
if (screenDirection.SqrMagnitude() <= Math::EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outScreenDirection = screenDirection.Normalized();
|
||||
return true;
|
||||
}
|
||||
|
||||
inline float DistanceToSegmentSquared(
|
||||
const Math::Vector2& point,
|
||||
const Math::Vector2& segmentStart,
|
||||
const Math::Vector2& segmentEnd,
|
||||
float* outSegmentT = nullptr) {
|
||||
const Math::Vector2 segment = segmentEnd - segmentStart;
|
||||
const float segmentLengthSq = segment.SqrMagnitude();
|
||||
if (segmentLengthSq <= Math::EPSILON) {
|
||||
if (outSegmentT != nullptr) {
|
||||
*outSegmentT = 0.0f;
|
||||
}
|
||||
return (point - segmentStart).SqrMagnitude();
|
||||
}
|
||||
|
||||
const float segmentT = std::clamp(
|
||||
Math::Vector2::Dot(point - segmentStart, segment) / segmentLengthSq,
|
||||
0.0f,
|
||||
1.0f);
|
||||
if (outSegmentT != nullptr) {
|
||||
*outSegmentT = segmentT;
|
||||
}
|
||||
|
||||
const Math::Vector2 closestPoint = segmentStart + segment * segmentT;
|
||||
return (point - closestPoint).SqrMagnitude();
|
||||
}
|
||||
|
||||
inline Math::Plane BuildSceneViewportPlaneFromPointNormal(
|
||||
const Math::Vector3& point,
|
||||
const Math::Vector3& normal) {
|
||||
const Math::Vector3 planeNormal = normal.Normalized();
|
||||
return Math::Plane(planeNormal, -Math::Vector3::Dot(planeNormal, point));
|
||||
}
|
||||
|
||||
inline bool BuildSceneViewportAxisDragPlaneNormal(
|
||||
const SceneViewportOverlayData& overlay,
|
||||
const Math::Vector3& worldAxis,
|
||||
Math::Vector3& outPlaneNormal) {
|
||||
if (!overlay.valid || worldAxis.SqrMagnitude() <= Math::EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Math::Vector3 axis = worldAxis.Normalized();
|
||||
const Math::Vector3 candidates[] = {
|
||||
Math::Vector3::ProjectOnPlane(overlay.cameraForward.Normalized(), axis),
|
||||
Math::Vector3::ProjectOnPlane(overlay.cameraUp.Normalized(), axis),
|
||||
Math::Vector3::ProjectOnPlane(overlay.cameraRight.Normalized(), axis),
|
||||
Math::Vector3::ProjectOnPlane(Math::Vector3::Up(), axis),
|
||||
Math::Vector3::ProjectOnPlane(Math::Vector3::Right(), axis),
|
||||
Math::Vector3::ProjectOnPlane(Math::Vector3::Forward(), axis)
|
||||
};
|
||||
|
||||
for (const Math::Vector3& candidate : candidates) {
|
||||
if (candidate.SqrMagnitude() <= Math::EPSILON) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outPlaneNormal = candidate.Normalized();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
|
||||
Reference in New Issue
Block a user