feat: expand editor scripting asset and viewport flow
This commit is contained in:
@@ -14,7 +14,7 @@ namespace {
|
||||
constexpr float kMoveGizmoHandleLengthPixels = 100.0f;
|
||||
constexpr float kMoveGizmoHoverThresholdPixels = 10.0f;
|
||||
|
||||
Math::Vector3 GetAxisVector(SceneViewportGizmoAxis axis) {
|
||||
Math::Vector3 GetBaseAxisVector(SceneViewportGizmoAxis axis) {
|
||||
switch (axis) {
|
||||
case SceneViewportGizmoAxis::X:
|
||||
return Math::Vector3::Right();
|
||||
@@ -28,6 +28,16 @@ Math::Vector3 GetAxisVector(SceneViewportGizmoAxis axis) {
|
||||
}
|
||||
}
|
||||
|
||||
Math::Vector3 GetAxisVector(
|
||||
SceneViewportGizmoAxis axis,
|
||||
const Math::Quaternion& orientation) {
|
||||
const Math::Vector3 baseAxis = GetBaseAxisVector(axis);
|
||||
const Math::Vector3 orientedAxis = orientation * baseAxis;
|
||||
return orientedAxis.SqrMagnitude() <= Math::EPSILON
|
||||
? baseAxis
|
||||
: orientedAxis.Normalized();
|
||||
}
|
||||
|
||||
Math::Color GetAxisBaseColor(SceneViewportGizmoAxis axis) {
|
||||
switch (axis) {
|
||||
case SceneViewportGizmoAxis::X:
|
||||
@@ -61,20 +71,21 @@ SceneViewportGizmoPlane GetPlaneForIndex(size_t index) {
|
||||
|
||||
void GetPlaneAxes(
|
||||
SceneViewportGizmoPlane plane,
|
||||
const Math::Quaternion& orientation,
|
||||
Math::Vector3& outAxisA,
|
||||
Math::Vector3& outAxisB) {
|
||||
switch (plane) {
|
||||
case SceneViewportGizmoPlane::XY:
|
||||
outAxisA = Math::Vector3::Right();
|
||||
outAxisB = Math::Vector3::Up();
|
||||
outAxisA = GetAxisVector(SceneViewportGizmoAxis::X, orientation);
|
||||
outAxisB = GetAxisVector(SceneViewportGizmoAxis::Y, orientation);
|
||||
return;
|
||||
case SceneViewportGizmoPlane::XZ:
|
||||
outAxisA = Math::Vector3::Right();
|
||||
outAxisB = Math::Vector3::Forward();
|
||||
outAxisA = GetAxisVector(SceneViewportGizmoAxis::X, orientation);
|
||||
outAxisB = GetAxisVector(SceneViewportGizmoAxis::Z, orientation);
|
||||
return;
|
||||
case SceneViewportGizmoPlane::YZ:
|
||||
outAxisA = Math::Vector3::Up();
|
||||
outAxisB = Math::Vector3::Forward();
|
||||
outAxisA = GetAxisVector(SceneViewportGizmoAxis::Y, orientation);
|
||||
outAxisB = GetAxisVector(SceneViewportGizmoAxis::Z, orientation);
|
||||
return;
|
||||
case SceneViewportGizmoPlane::None:
|
||||
default:
|
||||
@@ -84,14 +95,16 @@ void GetPlaneAxes(
|
||||
}
|
||||
}
|
||||
|
||||
Math::Vector3 GetPlaneNormal(SceneViewportGizmoPlane plane) {
|
||||
Math::Vector3 GetPlaneNormal(
|
||||
SceneViewportGizmoPlane plane,
|
||||
const Math::Quaternion& orientation) {
|
||||
switch (plane) {
|
||||
case SceneViewportGizmoPlane::XY:
|
||||
return Math::Vector3::Forward();
|
||||
return GetAxisVector(SceneViewportGizmoAxis::Z, orientation);
|
||||
case SceneViewportGizmoPlane::XZ:
|
||||
return Math::Vector3::Up();
|
||||
return GetAxisVector(SceneViewportGizmoAxis::Y, orientation);
|
||||
case SceneViewportGizmoPlane::YZ:
|
||||
return Math::Vector3::Right();
|
||||
return GetAxisVector(SceneViewportGizmoAxis::X, orientation);
|
||||
case SceneViewportGizmoPlane::None:
|
||||
default:
|
||||
return Math::Vector3::Zero();
|
||||
@@ -185,10 +198,9 @@ float ComputeWorldUnitsPerPixel(
|
||||
void SceneViewportMoveGizmo::Update(const SceneViewportMoveGizmoContext& context) {
|
||||
BuildDrawData(context);
|
||||
if (m_dragMode == DragMode::None && IsMouseInsideViewport(context)) {
|
||||
m_hoveredAxis = HitTestAxis(context.mousePosition);
|
||||
m_hoveredPlane = m_hoveredAxis == SceneViewportGizmoAxis::None
|
||||
? HitTestPlane(context.mousePosition)
|
||||
: SceneViewportGizmoPlane::None;
|
||||
const SceneViewportMoveGizmoHitResult hitResult = EvaluateHit(context.mousePosition);
|
||||
m_hoveredAxis = hitResult.axis;
|
||||
m_hoveredPlane = hitResult.plane;
|
||||
} else if (m_dragMode == DragMode::None) {
|
||||
m_hoveredAxis = SceneViewportGizmoAxis::None;
|
||||
m_hoveredPlane = SceneViewportGizmoPlane::None;
|
||||
@@ -221,18 +233,17 @@ bool SceneViewportMoveGizmo::TryBeginDrag(const SceneViewportMoveGizmoContext& c
|
||||
return false;
|
||||
}
|
||||
|
||||
const Math::Vector3 objectWorldPosition = context.selectedObject->GetTransform()->GetPosition();
|
||||
const Math::Vector3 pivotWorldPosition = context.selectedObject->GetTransform()->GetPosition();
|
||||
const Math::Vector3 pivotWorldPosition = context.pivotWorldPosition;
|
||||
Math::Vector3 dragPlaneNormal = Math::Vector3::Zero();
|
||||
Math::Vector3 worldAxis = Math::Vector3::Zero();
|
||||
|
||||
if (m_hoveredAxis != SceneViewportGizmoAxis::None) {
|
||||
worldAxis = GetAxisVector(m_hoveredAxis);
|
||||
worldAxis = GetAxisVector(m_hoveredAxis, context.axisOrientation);
|
||||
if (!BuildSceneViewportAxisDragPlaneNormal(context.overlay, worldAxis, dragPlaneNormal)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
dragPlaneNormal = GetPlaneNormal(m_hoveredPlane);
|
||||
dragPlaneNormal = GetPlaneNormal(m_hoveredPlane, context.axisOrientation);
|
||||
if (dragPlaneNormal.SqrMagnitude() <= Math::EPSILON) {
|
||||
return false;
|
||||
}
|
||||
@@ -257,10 +268,21 @@ bool SceneViewportMoveGizmo::TryBeginDrag(const SceneViewportMoveGizmoContext& c
|
||||
m_activeAxisDirection = worldAxis;
|
||||
m_activePlaneNormal = dragPlaneNormal;
|
||||
m_dragPlane = dragPlane;
|
||||
m_dragStartObjectWorldPosition = objectWorldPosition;
|
||||
m_dragStartPivotWorldPosition = pivotWorldPosition;
|
||||
m_dragStartHitWorldPosition = hitPoint;
|
||||
m_dragStartAxisScalar = Math::Vector3::Dot(hitPoint - pivotWorldPosition, worldAxis);
|
||||
m_dragObjects = context.selectedObjects;
|
||||
if (m_dragObjects.empty()) {
|
||||
m_dragObjects.push_back(context.selectedObject);
|
||||
}
|
||||
m_dragStartObjectWorldPositions.clear();
|
||||
m_dragStartObjectWorldPositions.reserve(m_dragObjects.size());
|
||||
for (Components::GameObject* gameObject : m_dragObjects) {
|
||||
m_dragStartObjectWorldPositions.push_back(
|
||||
gameObject != nullptr && gameObject->GetTransform() != nullptr
|
||||
? gameObject->GetTransform()->GetPosition()
|
||||
: Math::Vector3::Zero());
|
||||
}
|
||||
RefreshHandleState();
|
||||
return true;
|
||||
}
|
||||
@@ -268,7 +290,9 @@ bool SceneViewportMoveGizmo::TryBeginDrag(const SceneViewportMoveGizmoContext& c
|
||||
void SceneViewportMoveGizmo::UpdateDrag(const SceneViewportMoveGizmoContext& context) {
|
||||
if (m_dragMode == DragMode::None ||
|
||||
context.selectedObject == nullptr ||
|
||||
context.selectedObject->GetID() != m_activeEntityId) {
|
||||
context.selectedObject->GetID() != m_activeEntityId ||
|
||||
m_dragObjects.empty() ||
|
||||
m_dragObjects.size() != m_dragStartObjectWorldPositions.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -290,17 +314,28 @@ void SceneViewportMoveGizmo::UpdateDrag(const SceneViewportMoveGizmoContext& con
|
||||
if (m_dragMode == DragMode::Axis) {
|
||||
const float currentAxisScalar = Math::Vector3::Dot(hitPoint - m_dragStartPivotWorldPosition, m_activeAxisDirection);
|
||||
const float deltaScalar = currentAxisScalar - m_dragStartAxisScalar;
|
||||
context.selectedObject->GetTransform()->SetPosition(
|
||||
m_dragStartObjectWorldPosition + m_activeAxisDirection * deltaScalar);
|
||||
const Math::Vector3 worldDelta = m_activeAxisDirection * deltaScalar;
|
||||
for (size_t index = 0; index < m_dragObjects.size(); ++index) {
|
||||
if (m_dragObjects[index] == nullptr || m_dragObjects[index]->GetTransform() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
m_dragObjects[index]->GetTransform()->SetPosition(
|
||||
m_dragStartObjectWorldPositions[index] + worldDelta);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dragMode == DragMode::Plane) {
|
||||
const Math::Vector3 planeDelta = Math::Vector3::ProjectOnPlane(
|
||||
const Math::Vector3 worldDelta = Math::Vector3::ProjectOnPlane(
|
||||
hitPoint - m_dragStartHitWorldPosition,
|
||||
m_activePlaneNormal);
|
||||
context.selectedObject->GetTransform()->SetPosition(
|
||||
m_dragStartObjectWorldPosition + planeDelta);
|
||||
for (size_t index = 0; index < m_dragObjects.size(); ++index) {
|
||||
if (m_dragObjects[index] == nullptr || m_dragObjects[index]->GetTransform() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
m_dragObjects[index]->GetTransform()->SetPosition(
|
||||
m_dragStartObjectWorldPositions[index] + worldDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,10 +354,11 @@ void SceneViewportMoveGizmo::EndDrag(IUndoManager& undoManager) {
|
||||
m_activeEntityId = 0;
|
||||
m_activeAxisDirection = Math::Vector3::Zero();
|
||||
m_activePlaneNormal = Math::Vector3::Zero();
|
||||
m_dragStartObjectWorldPosition = Math::Vector3::Zero();
|
||||
m_dragStartPivotWorldPosition = Math::Vector3::Zero();
|
||||
m_dragStartHitWorldPosition = Math::Vector3::Zero();
|
||||
m_dragStartAxisScalar = 0.0f;
|
||||
m_dragObjects.clear();
|
||||
m_dragStartObjectWorldPositions.clear();
|
||||
RefreshHandleState();
|
||||
}
|
||||
|
||||
@@ -337,10 +373,11 @@ void SceneViewportMoveGizmo::CancelDrag(IUndoManager* undoManager) {
|
||||
m_activeEntityId = 0;
|
||||
m_activeAxisDirection = Math::Vector3::Zero();
|
||||
m_activePlaneNormal = Math::Vector3::Zero();
|
||||
m_dragStartObjectWorldPosition = Math::Vector3::Zero();
|
||||
m_dragStartPivotWorldPosition = Math::Vector3::Zero();
|
||||
m_dragStartHitWorldPosition = Math::Vector3::Zero();
|
||||
m_dragStartAxisScalar = 0.0f;
|
||||
m_dragObjects.clear();
|
||||
m_dragStartObjectWorldPositions.clear();
|
||||
m_hoveredAxis = SceneViewportGizmoAxis::None;
|
||||
m_hoveredPlane = SceneViewportGizmoPlane::None;
|
||||
RefreshHandleState();
|
||||
@@ -363,19 +400,74 @@ const SceneViewportMoveGizmoDrawData& SceneViewportMoveGizmo::GetDrawData() cons
|
||||
return m_drawData;
|
||||
}
|
||||
|
||||
SceneViewportMoveGizmoHitResult SceneViewportMoveGizmo::EvaluateHit(const Math::Vector2& mousePosition) const {
|
||||
SceneViewportMoveGizmoHitResult result = {};
|
||||
if (!m_drawData.visible) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const float hoverThresholdSq = kMoveGizmoHoverThresholdPixels * kMoveGizmoHoverThresholdPixels;
|
||||
for (const SceneViewportMoveGizmoHandleDrawData& handle : m_drawData.handles) {
|
||||
if (!handle.visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float distanceSq = DistanceToSegmentSquared(mousePosition, handle.start, handle.end);
|
||||
if (distanceSq > result.distanceSq || distanceSq > hoverThresholdSq) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.axis = handle.axis;
|
||||
result.plane = SceneViewportGizmoPlane::None;
|
||||
result.distanceSq = distanceSq;
|
||||
}
|
||||
|
||||
if (result.axis != SceneViewportGizmoAxis::None) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (const SceneViewportMoveGizmoPlaneDrawData& plane : m_drawData.planes) {
|
||||
if (!plane.visible || !PointInQuad(mousePosition, plane.corners)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float distanceSq = (QuadCenter(plane.corners) - mousePosition).SqrMagnitude();
|
||||
if (distanceSq >= result.distanceSq) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.axis = SceneViewportGizmoAxis::None;
|
||||
result.plane = plane.plane;
|
||||
result.distanceSq = distanceSq;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SceneViewportMoveGizmo::SetHoveredHandle(
|
||||
SceneViewportGizmoAxis axis,
|
||||
SceneViewportGizmoPlane plane) {
|
||||
if (m_dragMode != DragMode::None) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_hoveredAxis = axis;
|
||||
m_hoveredPlane = axis == SceneViewportGizmoAxis::None ? plane : SceneViewportGizmoPlane::None;
|
||||
RefreshHandleState();
|
||||
}
|
||||
|
||||
void SceneViewportMoveGizmo::BuildDrawData(const SceneViewportMoveGizmoContext& context) {
|
||||
m_drawData = {};
|
||||
m_drawData.pivotRadius = 5.0f;
|
||||
|
||||
const Components::GameObject* selectedObject = context.selectedObject;
|
||||
if (selectedObject == nullptr ||
|
||||
if ((context.selectedObject == nullptr && context.selectedObjects.empty()) ||
|
||||
!context.overlay.valid ||
|
||||
context.viewportSize.x <= 1.0f ||
|
||||
context.viewportSize.y <= 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Math::Vector3 gizmoWorldOrigin = selectedObject->GetTransform()->GetPosition();
|
||||
const Math::Vector3 gizmoWorldOrigin = context.pivotWorldPosition;
|
||||
const SceneViewportProjectedPoint projectedPivot = ProjectSceneViewportWorldPoint(
|
||||
context.overlay,
|
||||
context.viewportSize.x,
|
||||
@@ -411,7 +503,7 @@ void SceneViewportMoveGizmo::BuildDrawData(const SceneViewportMoveGizmoContext&
|
||||
handle.start = projectedPivot.screenPosition;
|
||||
|
||||
const Math::Vector3 axisEndWorld =
|
||||
gizmoWorldOrigin + GetAxisVector(handle.axis) * axisLengthWorld;
|
||||
gizmoWorldOrigin + GetAxisVector(handle.axis, context.axisOrientation) * axisLengthWorld;
|
||||
const SceneViewportProjectedPoint projectedEnd = ProjectSceneViewportWorldPoint(
|
||||
context.overlay,
|
||||
context.viewportSize.x,
|
||||
@@ -436,7 +528,7 @@ void SceneViewportMoveGizmo::BuildDrawData(const SceneViewportMoveGizmoContext&
|
||||
|
||||
Math::Vector3 axisA = Math::Vector3::Zero();
|
||||
Math::Vector3 axisB = Math::Vector3::Zero();
|
||||
GetPlaneAxes(plane.plane, axisA, axisB);
|
||||
GetPlaneAxes(plane.plane, context.axisOrientation, axisA, axisB);
|
||||
if (axisA.SqrMagnitude() <= Math::EPSILON || axisB.SqrMagnitude() <= Math::EPSILON) {
|
||||
continue;
|
||||
}
|
||||
@@ -502,55 +594,5 @@ void SceneViewportMoveGizmo::RefreshHandleState() {
|
||||
}
|
||||
}
|
||||
|
||||
SceneViewportGizmoAxis SceneViewportMoveGizmo::HitTestAxis(const Math::Vector2& mousePosition) const {
|
||||
if (!m_drawData.visible) {
|
||||
return SceneViewportGizmoAxis::None;
|
||||
}
|
||||
|
||||
const float hoverThresholdSq = kMoveGizmoHoverThresholdPixels * kMoveGizmoHoverThresholdPixels;
|
||||
SceneViewportGizmoAxis bestAxis = SceneViewportGizmoAxis::None;
|
||||
float bestDistanceSq = hoverThresholdSq;
|
||||
|
||||
for (const SceneViewportMoveGizmoHandleDrawData& handle : m_drawData.handles) {
|
||||
if (!handle.visible) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float distanceSq = DistanceToSegmentSquared(mousePosition, handle.start, handle.end);
|
||||
if (distanceSq > bestDistanceSq) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bestDistanceSq = distanceSq;
|
||||
bestAxis = handle.axis;
|
||||
}
|
||||
|
||||
return bestAxis;
|
||||
}
|
||||
|
||||
SceneViewportGizmoPlane SceneViewportMoveGizmo::HitTestPlane(const Math::Vector2& mousePosition) const {
|
||||
if (!m_drawData.visible) {
|
||||
return SceneViewportGizmoPlane::None;
|
||||
}
|
||||
|
||||
SceneViewportGizmoPlane bestPlane = SceneViewportGizmoPlane::None;
|
||||
float bestDistanceSq = Math::FLOAT_MAX;
|
||||
for (const SceneViewportMoveGizmoPlaneDrawData& plane : m_drawData.planes) {
|
||||
if (!plane.visible || !PointInQuad(mousePosition, plane.corners)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const float distanceSq = (QuadCenter(plane.corners) - mousePosition).SqrMagnitude();
|
||||
if (distanceSq >= bestDistanceSq) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bestDistanceSq = distanceSq;
|
||||
bestPlane = plane.plane;
|
||||
}
|
||||
|
||||
return bestPlane;
|
||||
}
|
||||
|
||||
} // namespace Editor
|
||||
} // namespace XCEngine
|
||||
|
||||
Reference in New Issue
Block a user