|
|
|
@@ -168,6 +168,10 @@ void APS_Editor_Pawn::CreateInputActions()
|
|
|
|
IA_Delete = NewObject<UInputAction>(this, TEXT("IA_Delete"));
|
|
|
|
IA_Delete = NewObject<UInputAction>(this, TEXT("IA_Delete"));
|
|
|
|
IA_Delete->ValueType = EInputActionValueType::Boolean;
|
|
|
|
IA_Delete->ValueType = EInputActionValueType::Boolean;
|
|
|
|
EditorMappingContext->MapKey(IA_Delete, EKeys::Delete);
|
|
|
|
EditorMappingContext->MapKey(IA_Delete, EKeys::Delete);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IA_SnapToGround = NewObject<UInputAction>(this, TEXT("IA_SnapToGround"));
|
|
|
|
|
|
|
|
IA_SnapToGround->ValueType = EInputActionValueType::Boolean;
|
|
|
|
|
|
|
|
EditorMappingContext->MapKey(IA_SnapToGround, EKeys::End);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void APS_Editor_Pawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
|
|
|
void APS_Editor_Pawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
|
|
|
@@ -205,6 +209,7 @@ void APS_Editor_Pawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComp
|
|
|
|
EIC->BindAction(IA_RotateMode, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleRotateMode);
|
|
|
|
EIC->BindAction(IA_RotateMode, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleRotateMode);
|
|
|
|
EIC->BindAction(IA_ScaleMode, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleScaleMode);
|
|
|
|
EIC->BindAction(IA_ScaleMode, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleScaleMode);
|
|
|
|
EIC->BindAction(IA_Delete, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleDelete);
|
|
|
|
EIC->BindAction(IA_Delete, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleDelete);
|
|
|
|
|
|
|
|
EIC->BindAction(IA_SnapToGround, ETriggerEvent::Started, this, &APS_Editor_Pawn::HandleSnapToGround);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Input Handlers ----
|
|
|
|
// ---- Input Handlers ----
|
|
|
|
@@ -290,19 +295,6 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (CameraMode == EPS_Editor_CameraMode::GizmoDrag)
|
|
|
|
else if (CameraMode == EPS_Editor_CameraMode::GizmoDrag)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Intersect mouse ray with drag plane, constrain delta to axis, apply to actors
|
|
|
|
|
|
|
|
FVector RayOrigin, RayDir;
|
|
|
|
|
|
|
|
if (!GetMouseWorldRay(RayOrigin, RayDir))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FVector CurrentIntersection;
|
|
|
|
|
|
|
|
if (!RayPlaneIntersection(RayOrigin, RayDir, GizmoDragPlaneOrigin, GizmoDragPlaneNormal, CurrentIntersection))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetController());
|
|
|
|
APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetController());
|
|
|
|
if (!EditorPC) return;
|
|
|
|
if (!EditorPC) return;
|
|
|
|
UPS_Editor_SelectionManager* SM = EditorPC->GetSelectionManager();
|
|
|
|
UPS_Editor_SelectionManager* SM = EditorPC->GetSelectionManager();
|
|
|
|
@@ -311,14 +303,20 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|
|
|
if (!Gizmo) return;
|
|
|
|
if (!Gizmo) return;
|
|
|
|
|
|
|
|
|
|
|
|
const FVector AxisDir = Gizmo->GetAxisDirection(ActiveGizmoAxis);
|
|
|
|
const FVector AxisDir = Gizmo->GetAxisDirection(ActiveGizmoAxis);
|
|
|
|
const FVector TotalDelta = CurrentIntersection - GizmoDragInitialIntersection;
|
|
|
|
|
|
|
|
const EPS_Editor_TransformMode Mode = SM->GetTransformMode();
|
|
|
|
const EPS_Editor_TransformMode Mode = SM->GetTransformMode();
|
|
|
|
|
|
|
|
|
|
|
|
const TArray<TWeakObjectPtr<AActor>>& Selected = SM->GetSelectedActors();
|
|
|
|
const TArray<TWeakObjectPtr<AActor>>& Selected = SM->GetSelectedActors();
|
|
|
|
|
|
|
|
|
|
|
|
if (Mode == EPS_Editor_TransformMode::Translate)
|
|
|
|
if (Mode == EPS_Editor_TransformMode::Translate)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
// Ray-plane intersection for precise axis-constrained translation
|
|
|
|
|
|
|
|
FVector RayOrigin, RayDir;
|
|
|
|
|
|
|
|
if (!GetMouseWorldRay(RayOrigin, RayDir)) return;
|
|
|
|
|
|
|
|
FVector CurrentIntersection;
|
|
|
|
|
|
|
|
if (!RayPlaneIntersection(RayOrigin, RayDir, GizmoDragPlaneOrigin, GizmoDragPlaneNormal, CurrentIntersection)) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const FVector TotalDelta = CurrentIntersection - GizmoDragInitialIntersection;
|
|
|
|
const FVector ConstrainedDelta = FVector::DotProduct(TotalDelta, AxisDir) * AxisDir;
|
|
|
|
const FVector ConstrainedDelta = FVector::DotProduct(TotalDelta, AxisDir) * AxisDir;
|
|
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
|
|
|
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (AActor* Actor = Selected[i].Get())
|
|
|
|
if (AActor* Actor = Selected[i].Get())
|
|
|
|
@@ -329,29 +327,23 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Mode == EPS_Editor_TransformMode::Rotate)
|
|
|
|
else if (Mode == EPS_Editor_TransformMode::Rotate)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Compute rotation angle from initial to current intersection around the axis
|
|
|
|
// Use combined mouse X+Y screen movement to drive rotation angle
|
|
|
|
|
|
|
|
const float RotationSpeed = 1.0f;
|
|
|
|
|
|
|
|
const float AngleDelta = (LookInput.X + LookInput.Y) * RotationSpeed;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Accumulate total angle since drag start
|
|
|
|
|
|
|
|
GizmoDragAccumulator += AngleDelta;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const FQuat RotationQuat(AxisDir, FMath::DegreesToRadians(GizmoDragAccumulator));
|
|
|
|
const FVector GizmoLoc = GizmoDragPlaneOrigin;
|
|
|
|
const FVector GizmoLoc = GizmoDragPlaneOrigin;
|
|
|
|
const FVector InitDir = (GizmoDragInitialIntersection - GizmoLoc).GetSafeNormal();
|
|
|
|
|
|
|
|
const FVector CurrDir = (CurrentIntersection - GizmoLoc).GetSafeNormal();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Signed angle via cross product
|
|
|
|
|
|
|
|
float Angle = FMath::Atan2(
|
|
|
|
|
|
|
|
FVector::DotProduct(FVector::CrossProduct(InitDir, CurrDir), AxisDir),
|
|
|
|
|
|
|
|
FVector::DotProduct(InitDir, CurrDir));
|
|
|
|
|
|
|
|
Angle = FMath::RadiansToDegrees(Angle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const FQuat RotationQuat(AxisDir, FMath::DegreesToRadians(Angle));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
|
|
|
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (AActor* Actor = Selected[i].Get())
|
|
|
|
if (AActor* Actor = Selected[i].Get())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Rotate around gizmo pivot
|
|
|
|
|
|
|
|
const FVector Offset = GizmoDragInitialTransforms[i].GetLocation() - GizmoLoc;
|
|
|
|
const FVector Offset = GizmoDragInitialTransforms[i].GetLocation() - GizmoLoc;
|
|
|
|
const FVector RotatedOffset = RotationQuat.RotateVector(Offset);
|
|
|
|
Actor->SetActorLocation(GizmoLoc + RotationQuat.RotateVector(Offset));
|
|
|
|
Actor->SetActorLocation(GizmoLoc + RotatedOffset);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Also rotate the actor itself
|
|
|
|
|
|
|
|
const FQuat OrigRot = GizmoDragInitialTransforms[i].GetRotation();
|
|
|
|
const FQuat OrigRot = GizmoDragInitialTransforms[i].GetRotation();
|
|
|
|
Actor->SetActorRotation((RotationQuat * OrigRot).Rotator());
|
|
|
|
Actor->SetActorRotation((RotationQuat * OrigRot).Rotator());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -359,22 +351,17 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (Mode == EPS_Editor_TransformMode::Scale)
|
|
|
|
else if (Mode == EPS_Editor_TransformMode::Scale)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Scale factor from mouse movement along the axis
|
|
|
|
// Use combined mouse X+Y screen movement to drive scale
|
|
|
|
const float InitialDist = FVector::DotProduct(GizmoDragInitialIntersection - GizmoDragPlaneOrigin, AxisDir);
|
|
|
|
const float ScaleSpeed = 0.01f;
|
|
|
|
const float CurrentDist = FVector::DotProduct(CurrentIntersection - GizmoDragPlaneOrigin, AxisDir);
|
|
|
|
GizmoDragAccumulator += (LookInput.X + LookInput.Y) * ScaleSpeed;
|
|
|
|
|
|
|
|
|
|
|
|
float ScaleFactor = 1.0f;
|
|
|
|
const float ScaleFactor = FMath::Clamp(1.0f + GizmoDragAccumulator, 0.01f, 100.0f);
|
|
|
|
if (FMath::Abs(InitialDist) > KINDA_SMALL_NUMBER)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
ScaleFactor = FMath::Clamp(CurrentDist / InitialDist, 0.01f, 100.0f);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
|
|
|
for (int32 i = 0; i < Selected.Num() && i < GizmoDragInitialTransforms.Num(); ++i)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (AActor* Actor = Selected[i].Get())
|
|
|
|
if (AActor* Actor = Selected[i].Get())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
FVector NewScale = GizmoDragInitialTransforms[i].GetScale3D();
|
|
|
|
FVector NewScale = GizmoDragInitialTransforms[i].GetScale3D();
|
|
|
|
// Scale only on the dragged axis
|
|
|
|
|
|
|
|
if (ActiveGizmoAxis == EPS_Editor_GizmoAxis::X) NewScale.X *= ScaleFactor;
|
|
|
|
if (ActiveGizmoAxis == EPS_Editor_GizmoAxis::X) NewScale.X *= ScaleFactor;
|
|
|
|
else if (ActiveGizmoAxis == EPS_Editor_GizmoAxis::Y) NewScale.Y *= ScaleFactor;
|
|
|
|
else if (ActiveGizmoAxis == EPS_Editor_GizmoAxis::Y) NewScale.Y *= ScaleFactor;
|
|
|
|
else if (ActiveGizmoAxis == EPS_Editor_GizmoAxis::Z) NewScale.Z *= ScaleFactor;
|
|
|
|
else if (ActiveGizmoAxis == EPS_Editor_GizmoAxis::Z) NewScale.Z *= ScaleFactor;
|
|
|
|
@@ -383,7 +370,6 @@ void APS_Editor_Pawn::HandleLook(const FInputActionValue& Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update gizmo position
|
|
|
|
|
|
|
|
Gizmo->SetActorLocation(SM->GetSelectionPivot());
|
|
|
|
Gizmo->SetActorLocation(SM->GetSelectionPivot());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -548,6 +534,18 @@ void APS_Editor_Pawn::HandleDelete(const FInputActionValue& Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void APS_Editor_Pawn::HandleSnapToGround(const FInputActionValue& Value)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (CameraMode != EPS_Editor_CameraMode::Idle) return;
|
|
|
|
|
|
|
|
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetController()))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (UPS_Editor_SelectionManager* SM = EditorPC->GetSelectionManager())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
SM->SnapSelectionToGround();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---- Helpers ----
|
|
|
|
// ---- Helpers ----
|
|
|
|
|
|
|
|
|
|
|
|
void APS_Editor_Pawn::SetCameraMode(EPS_Editor_CameraMode NewMode)
|
|
|
|
void APS_Editor_Pawn::SetCameraMode(EPS_Editor_CameraMode NewMode)
|
|
|
|
@@ -684,21 +682,30 @@ void APS_Editor_Pawn::BeginGizmoDrag(EPS_Editor_GizmoAxis Axis)
|
|
|
|
|
|
|
|
|
|
|
|
ActiveGizmoAxis = Axis;
|
|
|
|
ActiveGizmoAxis = Axis;
|
|
|
|
|
|
|
|
|
|
|
|
// Get the axis direction in world space
|
|
|
|
|
|
|
|
const FVector AxisDir = Gizmo->GetAxisDirection(Axis);
|
|
|
|
const FVector AxisDir = Gizmo->GetAxisDirection(Axis);
|
|
|
|
|
|
|
|
const EPS_Editor_TransformMode Mode = SM->GetTransformMode();
|
|
|
|
|
|
|
|
|
|
|
|
// Compute the drag plane: contains the axis and faces the camera as much as possible
|
|
|
|
FVector PlaneNormal;
|
|
|
|
const FVector CameraForward = GetActorForwardVector();
|
|
|
|
if (Mode == EPS_Editor_TransformMode::Rotate)
|
|
|
|
FVector PlaneNormal = FVector::CrossProduct(AxisDir, CameraForward).GetSafeNormal();
|
|
|
|
|
|
|
|
if (PlaneNormal.IsNearlyZero())
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Camera looking along the axis - use camera up instead
|
|
|
|
// For rotation: plane perpendicular to the rotation axis
|
|
|
|
PlaneNormal = FVector::CrossProduct(AxisDir, FVector::UpVector).GetSafeNormal();
|
|
|
|
PlaneNormal = AxisDir;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// For translate/scale: plane containing the axis, facing the camera
|
|
|
|
|
|
|
|
const FVector CameraForward = GetActorForwardVector();
|
|
|
|
|
|
|
|
PlaneNormal = FVector::CrossProduct(AxisDir, CameraForward).GetSafeNormal();
|
|
|
|
|
|
|
|
if (PlaneNormal.IsNearlyZero())
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
PlaneNormal = FVector::CrossProduct(AxisDir, FVector::UpVector).GetSafeNormal();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
PlaneNormal = FVector::CrossProduct(AxisDir, PlaneNormal).GetSafeNormal();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PlaneNormal = FVector::CrossProduct(AxisDir, PlaneNormal).GetSafeNormal();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GizmoDragPlaneNormal = PlaneNormal;
|
|
|
|
GizmoDragPlaneNormal = PlaneNormal;
|
|
|
|
GizmoDragPlaneOrigin = Gizmo->GetActorLocation();
|
|
|
|
GizmoDragPlaneOrigin = Gizmo->GetActorLocation();
|
|
|
|
|
|
|
|
GizmoDragAccumulator = 0.0f;
|
|
|
|
|
|
|
|
|
|
|
|
// Get initial mouse-plane intersection
|
|
|
|
// Get initial mouse-plane intersection
|
|
|
|
FVector RayOrigin, RayDir;
|
|
|
|
FVector RayOrigin, RayDir;
|
|
|
|
|