You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#jira UE-150037 #rb trivial #preflight 628aa43b6278bd4e1e0535b8 [CL 20315747 by zach brockway in ue5-main branch]
1880 lines
77 KiB
C++
1880 lines
77 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "VREditorInteractor.h"
|
|
|
|
#include "ActorTransformer.h"
|
|
#include "Components/PointLightComponent.h"
|
|
#include "Components/SplineComponent.h"
|
|
#include "Components/SplineMeshComponent.h"
|
|
#include "Editor.h"
|
|
#include "EngineUtils.h"
|
|
#include "Engine/Selection.h"
|
|
#include "Engine/StaticMeshSocket.h"
|
|
#include "Features/IModularFeatures.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "IMotionController.h"
|
|
#include "IXRTrackingSystem.h"
|
|
#include "Materials/MaterialInstanceDynamic.h"
|
|
#include "MotionControllerComponent.h"
|
|
#include "ViewportWorldInteraction.h"
|
|
#include "VREditorActions.h"
|
|
#include "VREditorAssetContainer.h"
|
|
#include "VREditorMode.h"
|
|
#include "VREditorDockableWindow.h"
|
|
#include "VREditorFloatingText.h"
|
|
#include "VREditorFloatingUI.h"
|
|
#include "VREditorPlacement.h"
|
|
#include "VREditorRadialFloatingUI.h"
|
|
#include "VREditorUISystem.h"
|
|
#include "VRModeSettings.h"
|
|
#include "XRMotionControllerBase.h" // for FXRMotionControllerBase::Left/RightHandSourceId and GetHandEnumForSourceName()
|
|
|
|
namespace VREd
|
|
{
|
|
static FAutoConsoleVariable TriggerTouchThreshold_Vive( TEXT( "VI.TriggerTouchThreshold_Vive" ), 0.025f, TEXT( "Minimum trigger threshold before we consider the trigger 'touched'" ) );
|
|
static FAutoConsoleVariable TriggerTouchThreshold_Rift( TEXT( "VI.TriggerTouchThreshold_Rift" ), 0.15f, TEXT( "Minimum trigger threshold before we consider the trigger 'touched'" ) );
|
|
static FAutoConsoleVariable TriggerDeadZone_Vive( TEXT( "VI.TriggerDeadZone_Vive" ), 0.25f, TEXT( "Trigger dead zone. The trigger must be fully released before we'll trigger a new 'light press'" ) );
|
|
static FAutoConsoleVariable TriggerDeadZone_Rift( TEXT( "VI.TriggerDeadZone_Rift" ), 0.25f, TEXT( "Trigger dead zone. The trigger must be fully released before we'll trigger a new 'light press'" ) );
|
|
static FAutoConsoleVariable TriggerFullyPressedThreshold_Vive( TEXT( "VI.TriggerFullyPressedThreshold_Vive" ), 0.90f, TEXT( "Minimum trigger threshold before we consider the trigger 'fully pressed'" ) );
|
|
static FAutoConsoleVariable TriggerFullyPressedThreshold_Rift( TEXT( "VI.TriggerFullyPressedThreshold_Rift" ), 0.99f, TEXT( "Minimum trigger threshold before we consider the trigger 'fully pressed'" ) );
|
|
|
|
//Laser
|
|
static FAutoConsoleVariable OculusLaserPointerRotationOffset( TEXT( "VI.OculusLaserPointerRotationOffset" ), 0.0f, TEXT( "How much to rotate the laser pointer (pitch) relative to the forward vector of the controller (Oculus)" ) );
|
|
static FAutoConsoleVariable ViveLaserPointerRotationOffset( TEXT( "VI.ViveLaserPointerRotationOffset" ), /* -57.8f */ 0.0f, TEXT( "How much to rotate the laser pointer (pitch) relative to the forward vector of the controller (Vive)" ) );
|
|
static FAutoConsoleVariable OculusLaserPointerStartOffset( TEXT( "VI.OculusLaserPointerStartOffset" ), 2.8f, TEXT( "How far to offset the start of the laser pointer to avoid overlapping the hand mesh geometry (Oculus)" ) );
|
|
static FAutoConsoleVariable ViveLaserPointerStartOffset( TEXT( "VI.ViveLaserPointerStartOffset" ), 1.25f /* 8.5f */, TEXT( "How far to offset the start of the laser pointer to avoid overlapping the hand mesh geometry (Vive)" ) );
|
|
|
|
// Laser visuals
|
|
static FAutoConsoleVariable LaserPointerLightRadius( TEXT( "VREd.LaserPointLightRadius" ), 10.0f, TEXT( "How big our hover light is" ) );
|
|
static FAutoConsoleVariable LaserPointerRadius( TEXT( "VREd.LaserPointerRadius" ), .5f, TEXT( "Radius of the laser pointer line" ) );
|
|
static FAutoConsoleVariable LaserPointerHoverBallRadius( TEXT( "VREd.LaserPointerHoverBallRadius" ), 1.0f, TEXT( "Radius of the visual cue for a hovered object along the laser pointer ray" ) );
|
|
static FAutoConsoleVariable LaserPointerLightPullBackDistance( TEXT( "VREd.LaserPointerLightPullBackDistance" ), 2.5f, TEXT( "How far to pull back our little hover light from the impact surface" ) );
|
|
static FAutoConsoleVariable LaserRadiusScaleWhenOverUI( TEXT( "VREd.LaserRadiusScaleWhenOverUI" ), 0.25f, TEXT( "How much to scale down the size of the laser pointer radius when over UI" ) );
|
|
static FAutoConsoleVariable HoverBallRadiusScaleWhenOverUI( TEXT( "VREd.HoverBallRadiusScaleWhenOverUI" ), 0.4f, TEXT( "How much to scale down the size of the hover ball when over UI" ) );
|
|
|
|
static FAutoConsoleVariable MinTrackpadOffsetBeforeRadialMenu( TEXT( "VREd.MinTrackpadOffsetBeforeRadialMenu" ), 0.5f, TEXT( "How far you have to hold the trackpad upward before you can placing objects instantly by pulling the trigger" ) );
|
|
static FAutoConsoleVariable MinJoystickOffsetBeforeFlick( TEXT( "VREd.MinJoystickOffsetBeforeFlick" ), 0.4f, TEXT( "Dead zone for flick actions on the motion controller" ) );
|
|
|
|
static FAutoConsoleVariable TrackpadStopImpactAtLaserBuffer( TEXT( "VREd.TrackpadStopImpactAtLaserBuffer" ), 0.4f, TEXT( "Required amount to slide with input to stop transforming to end of laser" ) );
|
|
|
|
static FAutoConsoleVariable TrackpadAbsoluteDragSpeed( TEXT( "VREd.TrackpadAbsoluteDragSpeed" ), 80.0f, TEXT( "How fast objects move toward or away when you drag on the touchpad while carrying them" ) );
|
|
static FAutoConsoleVariable TrackpadRelativeDragSpeed( TEXT( "VREd.TrackpadRelativeDragSpeed" ), 8.0f, TEXT( "How fast objects move toward or away when you hold a direction on an analog stick while carrying them" ) );
|
|
static FAutoConsoleVariable MinVelocityForInertia( TEXT( "VREd.MinVelocityForMotionControllerInertia" ), 1.0f, TEXT( "Minimum velocity (in cm/frame in unscaled room space) before inertia will kick in when releasing objects (or the world)" ) );
|
|
|
|
static FAutoConsoleVariable SequencerScrubMax( TEXT( "VREd.SequencerScrubMax" ), 2.0f, TEXT( "Max fast forward or fast reverse magnitude" ) );
|
|
|
|
static FAutoConsoleVariable ShowControllerHelpLabels( TEXT( "VREd.ShowControllerHelpLabels" ), 0, TEXT( "Enables help text overlay when controllers are near the viewer" ) );
|
|
static FAutoConsoleVariable HelpLabelFadeDuration( TEXT( "VREd.HelpLabelFadeDuration" ), 0.4f, TEXT( "Duration to fade controller help labels in and out" ) );
|
|
static FAutoConsoleVariable HelpLabelFadeDistance( TEXT( "VREd.HelpLabelFadeDistance" ), 30.0f, TEXT( "Distance at which controller help labels should appear (in cm)" ) );
|
|
|
|
static FAutoConsoleVariable InvertTrackpadVertical( TEXT( "VREd.InvertTrackpadVertical" ), 0, TEXT( "Toggles inverting the touch pad vertical axis" ) );
|
|
}
|
|
|
|
namespace VREditorKeyNames
|
|
{
|
|
// @todo vreditor input: Ideally these would not be needed, but SteamVR fires off it's "trigger pressed" event
|
|
// well before the trigger is fully down (*click*)
|
|
static const FName MotionController_Left_PressedTriggerAxis( "MotionController_Left_PressedTriggerAxis" );
|
|
static const FName MotionController_Right_PressedTriggerAxis( "MotionController_Right_PressedTriggerAxis" );
|
|
static const FName MotionController_Left_FullyPressedTriggerAxis( "MotionController_Left_FullyPressedTriggerAxis" );
|
|
static const FName MotionController_Right_FullyPressedTriggerAxis( "MotionController_Right_FullyPressedTriggerAxis" );
|
|
}
|
|
|
|
static const FName OculusDeviceType( TEXT( "OculusHMD" ) );
|
|
static const FName SteamVRDeviceType( TEXT( "SteamVR" ) );
|
|
static const FName OpenXRDeviceType( TEXT( "OpenXR" ) );
|
|
|
|
const FName UVREditorInteractor::TrackpadPositionX = FName( "TrackpadPosition_X" );
|
|
const FName UVREditorInteractor::TrackpadPositionY = FName( "TrackpadPosition_Y" );
|
|
const FName UVREditorInteractor::TriggerAxis = FName( "TriggerAxis" );
|
|
const FName UVREditorInteractor::MotionController_Left_PressedTriggerAxis = FName( "MotionController_Left_PressedTriggerAxis" );
|
|
const FName UVREditorInteractor::MotionController_Right_PressedTriggerAxis = FName( "MotionController_Right_PressedTriggerAxis" );
|
|
const FName UVREditorInteractor::MotionController_Left_FullyPressedTriggerAxis = FName( "MotionController_Left_FullyPressedTriggerAxis" );
|
|
const FName UVREditorInteractor::MotionController_Right_FullyPressedTriggerAxis = FName( "MotionController_Right_FullyPressedTriggerAxis" );
|
|
|
|
#define LOCTEXT_NAMESPACE "UVREditorInteractor"
|
|
|
|
UVREditorInteractor::UVREditorInteractor() :
|
|
Super(),
|
|
bIsUndoRedoSwipeEnabled( true ),
|
|
MotionControllerComponent( nullptr ),
|
|
HandMeshComponent( nullptr ),
|
|
LaserSplineComponent( nullptr ),
|
|
LaserPointerMID( nullptr ),
|
|
TranslucentLaserPointerMID( nullptr ),
|
|
HoverMeshComponent( nullptr ),
|
|
HoverPointLightComponent( nullptr ),
|
|
HandMeshMID( nullptr ),
|
|
bHaveMotionController( false ),
|
|
bIsModifierPressed( false ),
|
|
SelectAndMoveTriggerValue( 0.0f ),
|
|
LaserStart( FVector::ZeroVector ),
|
|
LaserEnd( FVector::ZeroVector ),
|
|
ControllerType( EControllerType::Unknown ),
|
|
OverrideControllerType( EControllerType::Unknown ),
|
|
bHasUIInFront( false ),
|
|
bHasUIOnForearm( false ),
|
|
bIsClickingOnUI( false ),
|
|
bIsRightClickingOnUI( false ),
|
|
bIsHoveringOverUI( false ),
|
|
UIScrollVelocity( 0.0f ),
|
|
LastUIPressTime( 0.0f ),
|
|
bIsTouchingTrackpad( false ),
|
|
bIsPressingTrackpad( false ),
|
|
TrackpadPosition( FVector2D::ZeroVector ),
|
|
LastTrackpadPosition( FVector2D::ZeroVector ),
|
|
bIsTrackpadPositionValid{ false, false },
|
|
LastTrackpadPositionUpdateTime( FTimespan::MinValue() ),
|
|
LastActiveTrackpadUpdateTime( FTimespan::MinValue() ),
|
|
bForceShowLaser(false),
|
|
ForceLaserColor(),
|
|
bFlickActionExecuted(false),
|
|
bIsScrubbingSequence(false),
|
|
ControllerMotionSource(NAME_None),
|
|
bWantHelpLabels( false ),
|
|
HelpLabelShowOrHideStartTime(FTimespan::MinValue()),
|
|
bIsTriggerFullyPressed( false ),
|
|
bIsTriggerPressed( false ),
|
|
bHasTriggerBeenReleasedSinceLastPress( true ),
|
|
InitialTouchPosition(FVector2D::ZeroVector),
|
|
LastSwipe(ETouchSwipeDirection::None),
|
|
VRMode( nullptr )
|
|
{
|
|
}
|
|
|
|
TMap<FViewportActionKeyInput, TArray<FKey>> UVREditorInteractor::GetKnownActionMappings(
|
|
EControllerHand InHand /* = EControllerHand::AnyHand */,
|
|
FName InHMDDeviceType /* = NAME_None */
|
|
) const
|
|
{
|
|
TMap<FViewportActionKeyInput, TArray<FKey>> ReturnMap;
|
|
|
|
if (InHand == EControllerHand::Left || InHand == EControllerHand::AnyHand)
|
|
{
|
|
if (InHMDDeviceType == SteamVRDeviceType || InHMDDeviceType == OpenXRDeviceType || InHMDDeviceType == NAME_None)
|
|
{
|
|
// HTC Vive
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(ViewportWorldActionTypes::WorldMovement)).Add(EKeys::Vive_Left_Grip_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier)).Add(EKeys::Vive_Left_Menu_Click);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadDown)).Add(EKeys::Vive_Left_Trackpad_Down); // down
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadLeft)).Add(EKeys::Vive_Left_Trackpad_Left);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadRight)).Add(EKeys::Vive_Left_Trackpad_Right);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadUp)).Add(EKeys::Vive_Left_Trackpad_Up);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TriggerAxis)).Add(EKeys::Vive_Left_Trigger_Axis);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionX)).Add(EKeys::Vive_Left_Trackpad_X);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionY)).Add(EKeys::Vive_Left_Trackpad_Y);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::ConfirmRadialSelection)).Add(EKeys::Vive_Left_Trackpad_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(VRActionTypes::Touch)).Add(EKeys::Vive_Left_Trackpad_Touch);
|
|
|
|
// Valve Index
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(ViewportWorldActionTypes::WorldMovement)).Add(EKeys::ValveIndex_Left_Trackpad_Touch);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier)).Add(EKeys::ValveIndex_Left_A_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier2)).Add(EKeys::ValveIndex_Left_B_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(VRActionTypes::Touch)).Add(EKeys::ValveIndex_Left_Thumbstick_Touch);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadDown)).Add(EKeys::ValveIndex_Left_Thumbstick_Down); // down
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadUp)).Add(EKeys::ValveIndex_Left_Thumbstick_Up);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadLeft)).Add(EKeys::ValveIndex_Left_Thumbstick_Left);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadRight)).Add(EKeys::ValveIndex_Left_Thumbstick_Right);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TriggerAxis)).Add(EKeys::ValveIndex_Left_Trigger_Axis);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionX)).Add(EKeys::ValveIndex_Left_Thumbstick_X);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionY)).Add(EKeys::ValveIndex_Left_Thumbstick_Y);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::ConfirmRadialSelection)).Add(EKeys::ValveIndex_Left_Thumbstick_Click);
|
|
}
|
|
|
|
if (InHMDDeviceType == OculusDeviceType || InHMDDeviceType == OpenXRDeviceType || InHMDDeviceType == NAME_None)
|
|
{
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(ViewportWorldActionTypes::WorldMovement)).Add(EKeys::OculusTouch_Left_Grip_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier)).Add(EKeys::OculusTouch_Left_X_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier2)).Add(EKeys::OculusTouch_Left_Y_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(VRActionTypes::Touch)).Add(EKeys::OculusTouch_Left_Thumbstick_Touch);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadDown)).Add(EKeys::OculusTouch_Left_Thumbstick_Down); // down
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadUp)).Add(EKeys::OculusTouch_Left_Thumbstick_Up);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadLeft)).Add(EKeys::OculusTouch_Left_Thumbstick_Left);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadRight)).Add(EKeys::OculusTouch_Left_Thumbstick_Right);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TriggerAxis)).Add(EKeys::OculusTouch_Left_Trigger_Axis);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionX)).Add(EKeys::OculusTouch_Left_Thumbstick_X);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionY)).Add(EKeys::OculusTouch_Left_Thumbstick_Y);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::ConfirmRadialSelection)).Add(EKeys::OculusTouch_Left_Thumbstick_Click);
|
|
}
|
|
}
|
|
|
|
if (InHand == EControllerHand::Right || InHand == EControllerHand::AnyHand)
|
|
{
|
|
if (InHMDDeviceType == SteamVRDeviceType || InHMDDeviceType == OpenXRDeviceType || InHMDDeviceType == NAME_None)
|
|
{
|
|
// HTC Vive
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(ViewportWorldActionTypes::WorldMovement)).Add(EKeys::Vive_Right_Grip_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier)).Add(EKeys::Vive_Right_Menu_Click);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadDown)).Add(EKeys::Vive_Right_Trackpad_Down); // down
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadLeft)).Add(EKeys::Vive_Right_Trackpad_Left);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadRight)).Add(EKeys::Vive_Right_Trackpad_Right);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadUp)).Add(EKeys::Vive_Right_Trackpad_Up);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TriggerAxis)).Add(EKeys::Vive_Right_Trigger_Axis);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionX)).Add(EKeys::Vive_Right_Trackpad_X);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionY)).Add(EKeys::Vive_Right_Trackpad_Y);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::ConfirmRadialSelection)).Add(EKeys::Vive_Right_Trackpad_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(VRActionTypes::Touch)).Add(EKeys::Vive_Right_Trackpad_Touch);
|
|
|
|
// Valve Index
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(ViewportWorldActionTypes::WorldMovement)).Add(EKeys::ValveIndex_Right_Trackpad_Touch);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier)).Add(EKeys::ValveIndex_Right_A_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier2)).Add(EKeys::ValveIndex_Right_B_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(VRActionTypes::Touch)).Add(EKeys::ValveIndex_Right_Thumbstick_Touch);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadDown)).Add(EKeys::ValveIndex_Right_Thumbstick_Down); // down
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadUp)).Add(EKeys::ValveIndex_Right_Thumbstick_Up);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadLeft)).Add(EKeys::ValveIndex_Right_Thumbstick_Left);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadRight)).Add(EKeys::ValveIndex_Right_Thumbstick_Right);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TriggerAxis)).Add(EKeys::ValveIndex_Right_Trigger_Axis);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionX)).Add(EKeys::ValveIndex_Right_Thumbstick_X);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionY)).Add(EKeys::ValveIndex_Right_Thumbstick_Y);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::ConfirmRadialSelection)).Add(EKeys::ValveIndex_Right_Thumbstick_Click);
|
|
}
|
|
|
|
if (InHMDDeviceType == OculusDeviceType || InHMDDeviceType == OpenXRDeviceType || InHMDDeviceType == NAME_None)
|
|
{
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(ViewportWorldActionTypes::WorldMovement)).Add(EKeys::OculusTouch_Right_Grip_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier)).Add(EKeys::OculusTouch_Right_A_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::Modifier2)).Add(EKeys::OculusTouch_Right_B_Click);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(VRActionTypes::Touch)).Add(EKeys::OculusTouch_Right_Thumbstick_Touch);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadDown)).Add(EKeys::OculusTouch_Right_Thumbstick_Down); // down
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadUp)).Add(EKeys::OculusTouch_Right_Thumbstick_Up);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadLeft)).Add(EKeys::OculusTouch_Right_Thumbstick_Left);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::TrackpadRight)).Add(EKeys::OculusTouch_Right_Thumbstick_Right);
|
|
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TriggerAxis)).Add(EKeys::OculusTouch_Right_Trigger_Axis);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionX)).Add(EKeys::OculusTouch_Right_Thumbstick_X);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput::Axis(UVREditorInteractor::TrackpadPositionY)).Add(EKeys::OculusTouch_Right_Thumbstick_Y);
|
|
ReturnMap.FindOrAdd(FViewportActionKeyInput(VRActionTypes::ConfirmRadialSelection)).Add(EKeys::OculusTouch_Right_Thumbstick_Click);
|
|
}
|
|
}
|
|
|
|
return ReturnMap;
|
|
}
|
|
|
|
void UVREditorInteractor::Init_Implementation( UVREditorMode* InVRMode )
|
|
{
|
|
VRMode = InVRMode;
|
|
KeyToActionMap.Reset();
|
|
|
|
const FName HMDDeviceType = InVRMode->GetHMDDeviceType();
|
|
|
|
// Setup keys
|
|
if (ControllerMotionSource == FXRMotionControllerBase::LeftHandSourceId)
|
|
{
|
|
AddKeyAction( UVREditorInteractor::MotionController_Left_FullyPressedTriggerAxis, FViewportActionKeyInput( ViewportWorldActionTypes::SelectAndMove_FullyPressed ) );
|
|
AddKeyAction( UVREditorInteractor::MotionController_Left_PressedTriggerAxis, FViewportActionKeyInput( ViewportWorldActionTypes::SelectAndMove ) );
|
|
|
|
for (const TPair<FViewportActionKeyInput, TArray<FKey>>& ActionMappings : GetKnownActionMappings(EControllerHand::Left, HMDDeviceType))
|
|
{
|
|
for (const FKey& Key : ActionMappings.Value)
|
|
{
|
|
AddKeyAction(Key, ActionMappings.Key);
|
|
}
|
|
}
|
|
}
|
|
else if (ControllerMotionSource == FXRMotionControllerBase::RightHandSourceId)
|
|
{
|
|
AddKeyAction( UVREditorInteractor::MotionController_Right_FullyPressedTriggerAxis, FViewportActionKeyInput( ViewportWorldActionTypes::SelectAndMove_FullyPressed ) );
|
|
AddKeyAction( UVREditorInteractor::MotionController_Right_PressedTriggerAxis, FViewportActionKeyInput( ViewportWorldActionTypes::SelectAndMove ) );
|
|
|
|
for (const TPair<FViewportActionKeyInput, TArray<FKey>>& ActionMappings : GetKnownActionMappings(EControllerHand::Right, HMDDeviceType))
|
|
{
|
|
for (const FKey& Key : ActionMappings.Value)
|
|
{
|
|
AddKeyAction(Key, ActionMappings.Key);
|
|
}
|
|
}
|
|
}
|
|
|
|
bHaveMotionController = true;
|
|
}
|
|
|
|
|
|
void UVREditorInteractor::SetupComponent_Implementation( AActor* OwningActor )
|
|
{
|
|
OwningAvatar = OwningActor;
|
|
|
|
// Setup a motion controller component. This allows us to take advantage of late frame updates, so
|
|
// our motion controllers won't lag behind the HMD
|
|
{
|
|
MotionControllerComponent = NewObject<UMotionControllerComponent>(OwningAvatar);
|
|
OwningAvatar->AddOwnedComponent( MotionControllerComponent );
|
|
MotionControllerComponent->SetupAttachment(OwningAvatar->GetRootComponent() );
|
|
MotionControllerComponent->RegisterComponent();
|
|
|
|
MotionControllerComponent->SetMobility( EComponentMobility::Movable );
|
|
MotionControllerComponent->SetCollisionEnabled( ECollisionEnabled::NoCollision );
|
|
|
|
MotionControllerComponent->MotionSource = ControllerMotionSource;
|
|
|
|
// @todo vreditor: Reenable late frame updates after we've sorted out why they cause popping artifacts on Rift
|
|
MotionControllerComponent->bDisableLowLatencyUpdate = true;
|
|
}
|
|
|
|
const UVREditorAssetContainer& AssetContainer = VRMode->GetAssetContainer();
|
|
|
|
// Hand mesh
|
|
{
|
|
HandMeshComponent = VRMode->CreateMotionControllerMesh(OwningAvatar, MotionControllerComponent );
|
|
SetHandMeshComponentProperties();
|
|
|
|
UMaterialInterface* HandMeshMaterial = GetVRMode().GetHMDDeviceType() == SteamVRDeviceType ? AssetContainer.VivePreControllerMaterial : AssetContainer.OculusControllerMaterial;
|
|
check( HandMeshMaterial != nullptr );
|
|
HandMeshMID = UMaterialInstanceDynamic::Create( HandMeshMaterial, GetTransientPackage() );
|
|
check( HandMeshMID != nullptr );
|
|
HandMeshComponent->SetMaterial( 0, HandMeshMID );
|
|
}
|
|
|
|
{
|
|
UMaterialInterface* LaserPointerMaterial = AssetContainer.LaserPointerMaterial;
|
|
check( LaserPointerMaterial != nullptr );
|
|
LaserPointerMID = UMaterialInstanceDynamic::Create( LaserPointerMaterial, GetTransientPackage() );
|
|
check( LaserPointerMID != nullptr );
|
|
|
|
UMaterialInterface* TranslucentLaserPointerMaterial = AssetContainer.LaserPointerTranslucentMaterial;
|
|
check( TranslucentLaserPointerMaterial != nullptr );
|
|
TranslucentLaserPointerMID = UMaterialInstanceDynamic::Create( TranslucentLaserPointerMaterial, GetTransientPackage() );
|
|
check( TranslucentLaserPointerMID != nullptr );
|
|
}
|
|
|
|
// Hover cue for laser pointer
|
|
{
|
|
HoverMeshComponent = NewObject<UStaticMeshComponent>(OwningAvatar);
|
|
OwningAvatar->AddOwnedComponent( HoverMeshComponent );
|
|
HoverMeshComponent->SetupAttachment(OwningAvatar->GetRootComponent() );
|
|
HoverMeshComponent->RegisterComponent();
|
|
|
|
UStaticMesh* HoverMesh = AssetContainer.LaserPointerHoverMesh;
|
|
check( HoverMesh != nullptr );
|
|
HoverMeshComponent->SetStaticMesh( HoverMesh );
|
|
HoverMeshComponent->SetMobility( EComponentMobility::Movable );
|
|
HoverMeshComponent->SetCollisionEnabled( ECollisionEnabled::NoCollision );
|
|
HoverMeshComponent->SetCastShadow( false );
|
|
|
|
HoverMeshComponent->SetMaterial( 0, LaserPointerMID );
|
|
HoverMeshComponent->SetMaterial( 1, TranslucentLaserPointerMID );
|
|
|
|
// Add a light!
|
|
{
|
|
HoverPointLightComponent = NewObject<UPointLightComponent>(OwningAvatar);
|
|
OwningAvatar->AddOwnedComponent( HoverPointLightComponent );
|
|
HoverPointLightComponent->SetupAttachment( HoverMeshComponent );
|
|
HoverPointLightComponent->RegisterComponent();
|
|
|
|
HoverPointLightComponent->SetLightColor( FLinearColor::Red );
|
|
//Hand.HoverPointLightComponent->SetLightColor( FLinearColor( 0.0f, 1.0f, 0.2f, 1.0f ) );
|
|
HoverPointLightComponent->SetIntensity( 30.0f ); // @todo: VREditor tweak
|
|
HoverPointLightComponent->SetMobility( EComponentMobility::Movable );
|
|
HoverPointLightComponent->SetAttenuationRadius( VREd::LaserPointerLightRadius->GetFloat() );
|
|
HoverPointLightComponent->bUseInverseSquaredFalloff = false;
|
|
HoverPointLightComponent->SetCastShadows( false );
|
|
}
|
|
}
|
|
|
|
{
|
|
const int32 NumLaserSplinePoints = 12;
|
|
|
|
UStaticMesh* MiddleSplineMesh = AssetContainer.LaserPointerMesh;
|
|
check( MiddleSplineMesh != nullptr );
|
|
UStaticMesh* StartSplineMesh = AssetContainer.LaserPointerStartMesh;
|
|
check( StartSplineMesh != nullptr );
|
|
UStaticMesh* EndSplineMesh = AssetContainer.LaserPointerEndMesh;
|
|
check( EndSplineMesh != nullptr );
|
|
|
|
LaserSplineComponent = NewObject<USplineComponent>(OwningAvatar);
|
|
OwningAvatar->AddOwnedComponent( LaserSplineComponent );
|
|
LaserSplineComponent->SetupAttachment( MotionControllerComponent );
|
|
LaserSplineComponent->RegisterComponent();
|
|
LaserSplineComponent->SetVisibility( false );
|
|
LaserSplineMeshComponents.Empty();
|
|
|
|
for (int32 i = 0; i < NumLaserSplinePoints; i++)
|
|
{
|
|
USplineMeshComponent* SplineSegment = NewObject<USplineMeshComponent>(OwningAvatar);
|
|
SplineSegment->SetMobility( EComponentMobility::Movable );
|
|
SplineSegment->SetCollisionEnabled( ECollisionEnabled::NoCollision );
|
|
SplineSegment->SetSplineUpDir( FVector::UpVector, false );
|
|
|
|
UStaticMesh* StaticMesh = nullptr;
|
|
if (i == 0)
|
|
{
|
|
StaticMesh = StartSplineMesh;
|
|
}
|
|
else if (i == NumLaserSplinePoints - 1)
|
|
{
|
|
StaticMesh = EndSplineMesh;
|
|
}
|
|
else
|
|
{
|
|
StaticMesh = MiddleSplineMesh;
|
|
}
|
|
|
|
SplineSegment->SetStaticMesh( StaticMesh );
|
|
SplineSegment->bTickInEditor = true;
|
|
SplineSegment->bCastDynamicShadow = false;
|
|
SplineSegment->CastShadow = false;
|
|
SplineSegment->SetMaterial( 0, LaserPointerMID );
|
|
SplineSegment->SetMaterial( 1, TranslucentLaserPointerMID );
|
|
SplineSegment->SetVisibility( true );
|
|
SplineSegment->RegisterComponent();
|
|
|
|
LaserSplineMeshComponents.Add( SplineSegment );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UVREditorInteractor::ReplaceHandMeshComponent(UStaticMesh* NewMesh)
|
|
{
|
|
HandMeshComponent->UnregisterComponent();
|
|
HandMeshComponent->DestroyComponent();
|
|
HandMeshComponent = VRMode->CreateMotionControllerMesh(OwningAvatar, MotionControllerComponent, NewMesh);
|
|
SetHandMeshComponentProperties();
|
|
}
|
|
|
|
void UVREditorInteractor::SetHandMeshComponentProperties()
|
|
{
|
|
check(HandMeshComponent != nullptr);
|
|
|
|
HandMeshComponent->SetCastShadow(false);
|
|
HandMeshComponent->SetCollisionEnabled(ECollisionEnabled::PhysicsOnly);
|
|
HandMeshComponent->SetCollisionResponseToAllChannels(ECR_Overlap);
|
|
HandMeshComponent->SetGenerateOverlapEvents(true);
|
|
}
|
|
|
|
void UVREditorInteractor::Shutdown_Implementation()
|
|
{
|
|
VRMode = nullptr;
|
|
MotionControllerComponent = nullptr;
|
|
HandMeshComponent = nullptr;
|
|
LaserPointerMID = nullptr;
|
|
TranslucentLaserPointerMID = nullptr;
|
|
HoverMeshComponent = nullptr;
|
|
HoverPointLightComponent = nullptr;
|
|
HandMeshComponent = nullptr;
|
|
|
|
for (auto& KeyAndValue : HelpLabels)
|
|
{
|
|
AFloatingText* FloatingText = KeyAndValue.Value;
|
|
GetVRMode().DestroyTransientActor( FloatingText );
|
|
}
|
|
|
|
HelpLabels.Empty();
|
|
|
|
VRMode = nullptr;
|
|
|
|
Super::Shutdown_Implementation();
|
|
}
|
|
|
|
EControllerHand UVREditorInteractor::GetControllerSide() const
|
|
{
|
|
EControllerHand Hand = EControllerHand::Left;
|
|
FXRMotionControllerBase::GetHandEnumForSourceName( ControllerMotionSource, Hand );
|
|
return Hand;
|
|
|
|
}
|
|
|
|
EControllerType UVREditorInteractor::GetControllerType() const
|
|
{
|
|
return OverrideControllerType != EControllerType::Unknown ? OverrideControllerType : ControllerType;
|
|
}
|
|
|
|
void UVREditorInteractor::SetControllerType( const EControllerType InControllerType )
|
|
{
|
|
OverrideControllerType = EControllerType::Unknown;
|
|
ControllerType = InControllerType;
|
|
}
|
|
|
|
bool UVREditorInteractor::TryOverrideControllerType(const EControllerType InControllerType)
|
|
{
|
|
if (InControllerType != EControllerType::Unknown && OverrideControllerType != EControllerType::Unknown)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
OverrideControllerType = InControllerType;
|
|
return true;
|
|
}
|
|
|
|
void UVREditorInteractor::Tick_Implementation( const float DeltaTime )
|
|
{
|
|
Super::Tick_Implementation( DeltaTime );
|
|
|
|
{
|
|
const float WorldScaleFactor = WorldInteraction->GetWorldScaleFactor();
|
|
|
|
// @todo vreditor: Manually ticking motion controller components
|
|
MotionControllerComponent->TickComponent( DeltaTime, ELevelTick::LEVELTICK_PauseTick, nullptr );
|
|
|
|
// The hands need to stay the same size relative to our tracking space, so we inverse compensate for world to meters scale here
|
|
// NOTE: We don't need to set the hand mesh location and rotation, as the MotionControllerComponent does that itself
|
|
if (ControllerMotionSource == FXRMotionControllerBase::RightHandSourceId &&
|
|
GetHMDDeviceType() == OculusDeviceType) // Oculus has asymmetrical controllers, so we mirror the mesh horizontally
|
|
{
|
|
HandMeshComponent->SetRelativeScale3D( FVector( WorldScaleFactor, -WorldScaleFactor, WorldScaleFactor ) );
|
|
}
|
|
else
|
|
{
|
|
HandMeshComponent->SetRelativeScale3D( FVector( WorldScaleFactor ) );
|
|
}
|
|
}
|
|
|
|
UpdateRadialMenuInput( DeltaTime );
|
|
|
|
{
|
|
const float WorldScaleFactor = WorldInteraction->GetWorldScaleFactor();
|
|
|
|
// Don't bother drawing hands if we're not currently tracking them.
|
|
if (bHaveMotionController)
|
|
{
|
|
HandMeshComponent->SetVisibility( true );
|
|
}
|
|
else
|
|
{
|
|
HandMeshComponent->SetVisibility( false );
|
|
}
|
|
|
|
// The laser pointer needs to stay the same size relative to our tracking space, so we inverse compensate for world to meters scale here
|
|
float LaserPointerRadius = VREd::LaserPointerRadius->GetFloat() * WorldScaleFactor;
|
|
float HoverMeshRadius = VREd::LaserPointerHoverBallRadius->GetFloat() * WorldScaleFactor;
|
|
|
|
// If we're hovering over something really close to the camera, go ahead and shrink the effect
|
|
// @todo vreditor: Can we make this actually just sized based on distance automatically? The beam and impact point are basically a cone.
|
|
if (IsHoveringOverUI())
|
|
{
|
|
LaserPointerRadius *= VREd::LaserRadiusScaleWhenOverUI->GetFloat();
|
|
HoverMeshRadius *= VREd::HoverBallRadiusScaleWhenOverUI->GetFloat();
|
|
}
|
|
|
|
const bool bEvenIfBlocked = false;
|
|
|
|
// If we're currently grabbing the world with this interactor
|
|
const bool bDraggingWorld = (InteractorData.DraggingMode == EViewportInteractionDraggingMode::World ||
|
|
(GetOtherInteractor() != nullptr && GetOtherInteractor()->GetInteractorData().DraggingMode == EViewportInteractionDraggingMode::World && InteractorData.DraggingMode == EViewportInteractionDraggingMode::AssistingDrag));
|
|
|
|
FVector LaserPointerStart(0), LaserPointerEnd(0);
|
|
const bool bHasLaser = GetLaserPointer(/* Out */ LaserPointerStart, /* Out */ LaserPointerEnd, bEvenIfBlocked );
|
|
if (bForceShowLaser || (bHasLaser && !bDraggingWorld))
|
|
{
|
|
// Only show the laser if we're actually in VR
|
|
SetLaserVisibility( GetVRMode().IsActuallyUsingVR() );
|
|
|
|
// NOTE: We don't need to set the laser pointer location and rotation, as the MotionControllerComponent will do
|
|
// that later in the frame.
|
|
|
|
// If we're actively dragging something around, then we'll crop the laser length to the hover impact
|
|
// point. Otherwise, we always want the laser to protrude through hovered objects, so that you can
|
|
// interact with translucent gizmo handles that are occluded by geometry
|
|
if (IsHoveringOverGizmo() ||
|
|
IsHoveringOverUI() ||
|
|
IsHovering())
|
|
{
|
|
LaserPointerEnd = GetHoverLocation();
|
|
}
|
|
|
|
if (IsHovering() && !GetIsLaserBlocked())
|
|
{
|
|
const FVector DirectionTowardHoverLocation = (GetHoverLocation() - LaserPointerStart).GetSafeNormal();
|
|
|
|
// The hover effect needs to stay the same size relative to our tracking space, so we inverse compensate for world to meters scale here
|
|
HoverMeshComponent->SetRelativeScale3D( FVector( HoverMeshRadius * 2.0f ) * (0.25f + 1.0f - this->GetSelectAndMoveTriggerValue() * 0.75f) );
|
|
HoverMeshComponent->SetVisibility( true );
|
|
HoverMeshComponent->SetWorldLocation( GetHoverLocation() );
|
|
|
|
// Show the light too, unless it's on top of UI. It looks too distracting on top of UI.
|
|
HoverPointLightComponent->SetVisibility( !IsHoveringOverUI() );
|
|
|
|
// Update radius for world scaling
|
|
HoverPointLightComponent->SetAttenuationRadius( VREd::LaserPointerLightRadius->GetFloat() * WorldScaleFactor );
|
|
|
|
// Pull hover light back a bit from the end of the ray
|
|
const float PullBackAmount = VREd::LaserPointerLightPullBackDistance->GetFloat() * WorldInteraction->GetWorldScaleFactor();
|
|
HoverPointLightComponent->SetWorldLocation( GetHoverLocation() - PullBackAmount * DirectionTowardHoverLocation );
|
|
}
|
|
else
|
|
{
|
|
HoverMeshComponent->SetVisibility( false );
|
|
HoverPointLightComponent->SetVisibility( false );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLaserVisibility( false );
|
|
HoverMeshComponent->SetVisibility( false );
|
|
HoverPointLightComponent->SetVisibility( false );
|
|
}
|
|
|
|
// Update the curved laser. No matter if we actually show the laser it needs to update,
|
|
// so if in the next frame it needs to be visible it won't interpolate from a previous location.
|
|
{
|
|
// Offset the beginning of the laser pointer a bit, so that it doesn't overlap the hand mesh
|
|
const float LaserPointerStartOffset = WorldScaleFactor *
|
|
(GetVRMode().GetHMDDeviceType() == OculusDeviceType ? VREd::OculusLaserPointerStartOffset->GetFloat() : VREd::ViveLaserPointerStartOffset->GetFloat());
|
|
|
|
// Get the hand transform and forward vector.
|
|
FTransform InteractorTransform;
|
|
FVector InteractorForwardVector;
|
|
|
|
if (GetTransformAndForwardVector( /* Out */ InteractorTransform, /* Out */ InteractorForwardVector))
|
|
{
|
|
InteractorForwardVector.Normalize();
|
|
|
|
// Offset the start point of the laser.
|
|
LaserPointerStart = InteractorTransform.GetLocation() + (InteractorForwardVector * LaserPointerStartOffset);
|
|
|
|
UpdateSplineLaser(LaserPointerStart, LaserPointerEnd, InteractorForwardVector);
|
|
}
|
|
}
|
|
|
|
bForceShowLaser = false;
|
|
}
|
|
|
|
// Updating laser colors for both hands
|
|
{
|
|
FLinearColor ResultColor;
|
|
float CrawlSpeed = 0.0f;
|
|
float CrawlFade = 0.0f;
|
|
|
|
if (ForceLaserColor.IsSet())
|
|
{
|
|
ResultColor = ForceLaserColor.GetValue();
|
|
ForceLaserColor.Reset();
|
|
}
|
|
else
|
|
{
|
|
if (InteractorData.HoveringOverTransformGizmoComponent != nullptr)
|
|
{
|
|
ResultColor = WorldInteraction->GetColor( UViewportWorldInteraction::EColors::GizmoHover );
|
|
}
|
|
else
|
|
{
|
|
const EViewportInteractionDraggingMode DraggingMode = GetDraggingMode();
|
|
if (DraggingMode == EViewportInteractionDraggingMode::World ||
|
|
(DraggingMode == EViewportInteractionDraggingMode::AssistingDrag && GetOtherInteractor() != nullptr && GetOtherInteractor()->GetDraggingMode() == EViewportInteractionDraggingMode::World))
|
|
{
|
|
// We can teleport in this mode, so animate the laser a bit
|
|
CrawlFade = 1.0f;
|
|
CrawlSpeed = 5.0f;
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::WorldDraggingColor );
|
|
}
|
|
else if (DraggingMode == EViewportInteractionDraggingMode::TransformablesAtLaserImpact ||
|
|
DraggingMode == EViewportInteractionDraggingMode::Material ||
|
|
DraggingMode == EViewportInteractionDraggingMode::TransformablesFreely ||
|
|
DraggingMode == EViewportInteractionDraggingMode::AssistingDrag)
|
|
{
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::SelectionColor );
|
|
}
|
|
else if (DraggingMode == EViewportInteractionDraggingMode::TransformablesWithGizmo)
|
|
{
|
|
ResultColor = WorldInteraction->GetColor( UViewportWorldInteraction::EColors::GizmoHover );
|
|
}
|
|
else if (DraggingMode == EViewportInteractionDraggingMode::Interactable ||
|
|
(GetVRMode().GetUISystem().IsInteractorDraggingDockUI( this ) && GetVRMode().GetUISystem().IsDraggingDockUI()))
|
|
{
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::UIColor );
|
|
}
|
|
else if (GetLastHoverComponent() != nullptr && GetLastHoverComponent()->GetOwner() != nullptr && GetLastHoverComponent()->GetOwner()->IsA( AVREditorDockableWindow::StaticClass() ))
|
|
{
|
|
AVREditorDockableWindow* HoveredDockWindow = Cast<AVREditorDockableWindow>( GetLastHoverComponent()->GetOwner() );
|
|
if (HoveredDockWindow && HoveredDockWindow->GetSelectionBarMeshComponent() == GetLastHoverComponent())
|
|
{
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::UIColor );
|
|
}
|
|
else
|
|
{
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::DefaultColor );
|
|
}
|
|
}
|
|
else if (GetControllerType() == EControllerType::Laser && VRMode->IsAimingTeleport())
|
|
{
|
|
CrawlFade = 1.0f;
|
|
CrawlSpeed = 5.0f;
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::WorldDraggingColor );
|
|
}
|
|
else if (GetControllerType() == EControllerType::UI)
|
|
{
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::UIColor );
|
|
}
|
|
else if (IsHoveringOverSelectedActor())
|
|
{
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::SelectionColor );
|
|
}
|
|
else
|
|
{
|
|
ResultColor = GetVRMode().GetColor( UVREditorMode::EColors::DefaultColor );
|
|
}
|
|
}
|
|
}
|
|
|
|
SetLaserVisuals( ResultColor, CrawlFade, CrawlSpeed );
|
|
}
|
|
|
|
UpdateHelpLabels();
|
|
|
|
// If the other controller is dragging freely, the UI controller can assist
|
|
if (GetControllerType() == EControllerType::UI)
|
|
{
|
|
if (GetOtherInteractor() &&
|
|
(GetOtherInteractor()->GetDraggingMode() == EViewportInteractionDraggingMode::TransformablesFreely))
|
|
{
|
|
TryOverrideControllerType( EControllerType::AssistingLaser );
|
|
}
|
|
}
|
|
// Otherwise the UI controller resets to a UI controller
|
|
// Allow for "trading off" during an assisted drag
|
|
else if (GetControllerType() == EControllerType::AssistingLaser)
|
|
{
|
|
if (GetOtherInteractor() &&
|
|
!(GetOtherInteractor()->GetDraggingMode() == EViewportInteractionDraggingMode::TransformablesFreely ||
|
|
GetOtherInteractor()->GetInteractorData().bWasAssistingDrag))
|
|
{
|
|
TryOverrideControllerType( EControllerType::Unknown );
|
|
}
|
|
}
|
|
}
|
|
|
|
FName UVREditorInteractor::GetHMDDeviceType() const
|
|
{
|
|
return GetVRMode().GetHMDDeviceType();
|
|
}
|
|
|
|
void UVREditorInteractor::CalculateDragRay( float& InOutDragRayLength, float& InOutDragRayVelocity )
|
|
{
|
|
const FTimespan CurrentTime = FTimespan::FromSeconds( FPlatformTime::Seconds() );
|
|
const float WorldScaleFactor = WorldInteraction->GetWorldScaleFactor();
|
|
|
|
// If we're dragging an object, go ahead and slide the object along the ray based on how far they slide their touch
|
|
// Make sure they are touching the trackpad, otherwise we get bad data
|
|
if (bIsTrackpadPositionValid[1])
|
|
{
|
|
const bool bIsAbsolute = (GetVRMode().GetHMDDeviceType() == SteamVRDeviceType);
|
|
float SlideDelta = GetTrackpadSlideDelta() * WorldScaleFactor;
|
|
|
|
if (!FMath::IsNearlyZero( SlideDelta ))
|
|
{
|
|
InOutDragRayLength += SlideDelta;
|
|
|
|
InOutDragRayVelocity = 0.0f;
|
|
|
|
// Don't apply inertia unless the user dragged a decent amount this frame
|
|
if (bIsAbsolute && FMath::Abs( SlideDelta ) >= VREd::MinVelocityForInertia->GetFloat() * WorldScaleFactor)
|
|
{
|
|
// Don't apply inertia if our data is sort of old
|
|
if (CurrentTime - LastTrackpadPositionUpdateTime <= FTimespan::FromSeconds( 1.0f / 30.0f ))
|
|
{
|
|
InOutDragRayVelocity = SlideDelta;
|
|
}
|
|
}
|
|
|
|
// Don't go too far
|
|
if (InOutDragRayLength < 0.0f)
|
|
{
|
|
InOutDragRayLength = 0.0f;
|
|
InOutDragRayVelocity = 0.0f;
|
|
}
|
|
|
|
// Stop transforming object to laser impact point when trying to slide with touchpad or analog stick.
|
|
if (InteractorData.DraggingMode == EViewportInteractionDraggingMode::TransformablesAtLaserImpact && !FMath::IsNearlyZero( SlideDelta, VREd::TrackpadStopImpactAtLaserBuffer->GetFloat() ))
|
|
{
|
|
InteractorData.DraggingMode = EViewportInteractionDraggingMode::TransformablesFreely;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!FMath::IsNearlyZero( InOutDragRayVelocity ))
|
|
{
|
|
// Apply drag ray length inertia
|
|
InOutDragRayLength += InOutDragRayVelocity;
|
|
|
|
// Don't go too far!
|
|
if (InOutDragRayLength < 0.0f)
|
|
{
|
|
InOutDragRayLength = 0.0f;
|
|
InOutDragRayVelocity = 0.0f;
|
|
}
|
|
|
|
// Apply damping
|
|
FVector RayVelocityVector( InOutDragRayVelocity, 0.0f, 0.0f );
|
|
const bool bVelocitySensitive = true;
|
|
WorldInteraction->ApplyVelocityDamping( RayVelocityVector, bVelocitySensitive );
|
|
InOutDragRayVelocity = RayVelocityVector.X;
|
|
}
|
|
else
|
|
{
|
|
InOutDragRayVelocity = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
FHitResult UVREditorInteractor::GetHitResultFromLaserPointer( TArray<AActor*>* OptionalListOfIgnoredActors /*= nullptr*/, const EHitResultGizmoFilterMode GizmoFilterMode /*= false*/,
|
|
TArray<UClass*>* ObjectsInFrontOfGizmo /*= nullptr */, const bool bEvenIfBlocked /*= false */, const float LaserLengthOverride /*= 0.0f */ )
|
|
{
|
|
static TArray<AActor*> IgnoredActors;
|
|
IgnoredActors.Reset();
|
|
if (OptionalListOfIgnoredActors == nullptr)
|
|
{
|
|
OptionalListOfIgnoredActors = &IgnoredActors;
|
|
}
|
|
|
|
// Ignore UI widgets too
|
|
if (GetDraggingMode() == EViewportInteractionDraggingMode::TransformablesAtLaserImpact)
|
|
{
|
|
for (TActorIterator<AVREditorFloatingUI> UIActorIt( WorldInteraction->GetWorld() ); UIActorIt; ++UIActorIt)
|
|
{
|
|
OptionalListOfIgnoredActors->Add( *UIActorIt );
|
|
}
|
|
}
|
|
|
|
static TArray<UClass*> PriorityOverGizmoObjects;
|
|
PriorityOverGizmoObjects.Reset();
|
|
if (ObjectsInFrontOfGizmo == nullptr)
|
|
{
|
|
ObjectsInFrontOfGizmo = &PriorityOverGizmoObjects;
|
|
}
|
|
|
|
ObjectsInFrontOfGizmo->Add( AVREditorDockableWindow::StaticClass() );
|
|
ObjectsInFrontOfGizmo->Add( AVREditorFloatingUI::StaticClass() );
|
|
|
|
return UViewportInteractor::GetHitResultFromLaserPointer( OptionalListOfIgnoredActors, GizmoFilterMode, ObjectsInFrontOfGizmo, bEvenIfBlocked, LaserLengthOverride );
|
|
}
|
|
|
|
void UVREditorInteractor::PreviewInputKey( class FEditorViewportClient& ViewportClient, FViewportActionKeyInput& Action, const FKey Key, const EInputEvent Event, bool& bOutWasHandled )
|
|
{
|
|
if (bIsScrubbingSequence &&
|
|
GetControllerType() == EControllerType::UI &&
|
|
Action.ActionType == ViewportWorldActionTypes::SelectAndMove &&
|
|
Action.Event == IE_Pressed)
|
|
{
|
|
ToggleSequencerScrubbingMode();
|
|
GetVRMode().GetUISystem().TryToSpawnRadialMenu( this, true );
|
|
}
|
|
|
|
// Update touch state
|
|
if (Action.ActionType == VRActionTypes::Touch)
|
|
{
|
|
if (Event == IE_Pressed)
|
|
{
|
|
bIsTouchingTrackpad = true;
|
|
|
|
// Set initial position when starting to touch the trackpad
|
|
InitialTouchPosition = TrackpadPosition;
|
|
}
|
|
else if (Event == IE_Released)
|
|
{
|
|
bIsTouchingTrackpad = false;
|
|
bIsTrackpadPositionValid[0] = false;
|
|
bIsTrackpadPositionValid[1] = false;
|
|
|
|
// Detect swipe on trackpad.
|
|
const FVector2D SwipeDelta = LastTrackpadPosition - InitialTouchPosition;
|
|
const float AbsSwipeDeltaX = FMath::Abs( SwipeDelta.X );
|
|
const float AbsSwipeDeltaY = FMath::Abs( SwipeDelta.Y );
|
|
if (!FMath::IsNearlyZero( SwipeDelta.X, 1.0f ) && AbsSwipeDeltaX > AbsSwipeDeltaY)
|
|
{
|
|
if (SwipeDelta.X > 0)
|
|
{
|
|
LastSwipe = ETouchSwipeDirection::Right;
|
|
if (bIsUndoRedoSwipeEnabled)
|
|
{
|
|
UndoRedoFromSwipe( LastSwipe );
|
|
}
|
|
}
|
|
else if (SwipeDelta.X < 0)
|
|
{
|
|
LastSwipe = ETouchSwipeDirection::Left;
|
|
if (bIsUndoRedoSwipeEnabled)
|
|
{
|
|
UndoRedoFromSwipe( LastSwipe );
|
|
}
|
|
}
|
|
}
|
|
else if (!FMath::IsNearlyZero( SwipeDelta.Y, 1.0f ))
|
|
{
|
|
if (SwipeDelta.Y > 0)
|
|
{
|
|
LastSwipe = ETouchSwipeDirection::Up;
|
|
}
|
|
else if (SwipeDelta.Y < 0)
|
|
{
|
|
LastSwipe = ETouchSwipeDirection::Down;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Action.ActionType == VRActionTypes::ConfirmRadialSelection)
|
|
{
|
|
bIsPressingTrackpad = Event == IE_Released ? false : true;
|
|
}
|
|
|
|
// Update modifier state
|
|
if (Action.ActionType == VRActionTypes::Modifier)
|
|
{
|
|
if (Event == IE_Pressed)
|
|
{
|
|
bIsModifierPressed = true;
|
|
}
|
|
else if (Event == IE_Released)
|
|
{
|
|
bIsModifierPressed = false;
|
|
}
|
|
}
|
|
|
|
if (!bOutWasHandled)
|
|
{
|
|
Super::PreviewInputKey( ViewportClient, Action, Key, Event, bOutWasHandled );
|
|
}
|
|
}
|
|
|
|
void UVREditorInteractor::HandleInputKey( class FEditorViewportClient& ViewportClient, FViewportActionKeyInput& Action, const FKey Key, const EInputEvent Event, bool& bOutWasHandled )
|
|
{
|
|
if (!bOutWasHandled && Action.ActionType == VRActionTypes::ConfirmRadialSelection)
|
|
{
|
|
bOutWasHandled = true;
|
|
const EViewportInteractionDraggingMode DraggingMode = GetDraggingMode();
|
|
|
|
if (Event == IE_Pressed)
|
|
{
|
|
// Start dragging at laser impact when already dragging actors freely
|
|
if (!IsCarrying() && DraggingMode == EViewportInteractionDraggingMode::TransformablesFreely)
|
|
{
|
|
FVector PlaceAt = GetHoverLocation();
|
|
const bool bIsPlacingActors = true;
|
|
const bool bAllowInterpolationWhenPlacing = true;
|
|
const bool bShouldUseLaserImpactDrag = true;
|
|
const bool bStartTransaction = true;
|
|
const bool bWithGrabberSphere = false; // Never use the grabber sphere when dragging at laser impact
|
|
WorldInteraction->StartDragging( this, WorldInteraction->GetTransformGizmoActor()->GetRootComponent(), PlaceAt, bIsPlacingActors, bAllowInterpolationWhenPlacing, bShouldUseLaserImpactDrag, bStartTransaction, bWithGrabberSphere );
|
|
}
|
|
}
|
|
else if (Event == IE_Released)
|
|
{
|
|
// Disable dragging at laser impact when releasing
|
|
if (DraggingMode == EViewportInteractionDraggingMode::TransformablesAtLaserImpact)
|
|
{
|
|
SetDraggingMode( EViewportInteractionDraggingMode::TransformablesFreely );
|
|
}
|
|
}
|
|
}
|
|
|
|
ApplyButtonPressColors( Action );
|
|
Super::HandleInputKey( ViewportClient, Action, Key, Event, bOutWasHandled );
|
|
}
|
|
|
|
|
|
void UVREditorInteractor::HandleInputAxis( FEditorViewportClient& ViewportClient, FViewportActionKeyInput& Action, const FKey Key, const float Delta, const float DeltaTime, bool& bOutWasHandled )
|
|
{
|
|
if (!bOutWasHandled && Action.ActionType == TriggerAxis)
|
|
{
|
|
const float TriggerPressedThreshold = (GetHMDDeviceType() == OculusDeviceType) ? GetDefault<UVRModeSettings>()->TriggerPressedThreshold_Rift : GetDefault<UVRModeSettings>()->TriggerPressedThreshold_Vive;
|
|
const float TriggerDeadZone = (GetHMDDeviceType() == OculusDeviceType) ? VREd::TriggerDeadZone_Rift->GetFloat() : VREd::TriggerDeadZone_Vive->GetFloat();
|
|
|
|
// Synthesize "lightly pressed" events for the trigger
|
|
{
|
|
// Store latest trigger value amount
|
|
SelectAndMoveTriggerValue = Delta;
|
|
|
|
if (!bIsTriggerPressed && // Don't fire if we are already pressed
|
|
bHasTriggerBeenReleasedSinceLastPress && // Only if we've been fully released since the last time we fired
|
|
Delta >= TriggerPressedThreshold)
|
|
{
|
|
bIsTriggerPressed = true;
|
|
bHasTriggerBeenReleasedSinceLastPress = false;
|
|
// Synthesize an input key for this light press
|
|
const EInputEvent InputEvent = IE_Pressed;
|
|
const bool bWasLightPressHandled = UViewportInteractor::HandleInputKey( ViewportClient, ControllerMotionSource == FXRMotionControllerBase::LeftHandSourceId ? MotionController_Left_PressedTriggerAxis : MotionController_Right_PressedTriggerAxis, InputEvent );
|
|
}
|
|
else if (bIsTriggerPressed && Delta < TriggerPressedThreshold)
|
|
{
|
|
bIsTriggerPressed = false;
|
|
|
|
// Synthesize an input key for this light press
|
|
const EInputEvent InputEvent = IE_Released;
|
|
const bool bWasLightReleaseHandled = UViewportInteractor::HandleInputKey( ViewportClient, ControllerMotionSource == FXRMotionControllerBase::LeftHandSourceId ? MotionController_Left_PressedTriggerAxis : MotionController_Right_PressedTriggerAxis, InputEvent );
|
|
}
|
|
}
|
|
|
|
if (!bHasTriggerBeenReleasedSinceLastPress && Delta < TriggerDeadZone)
|
|
{
|
|
bHasTriggerBeenReleasedSinceLastPress = true;
|
|
}
|
|
|
|
// Synthesize "fully pressed" events for the trigger
|
|
{
|
|
const float TriggerFullyPressedThreshold = (GetHMDDeviceType() == OculusDeviceType) ? VREd::TriggerFullyPressedThreshold_Rift->GetFloat() : VREd::TriggerFullyPressedThreshold_Vive->GetFloat();
|
|
|
|
if (!bIsTriggerFullyPressed && // Don't fire if we are already pressed
|
|
Delta >= TriggerFullyPressedThreshold)
|
|
{
|
|
bIsTriggerFullyPressed = true;
|
|
|
|
const EInputEvent InputEvent = IE_Pressed;
|
|
UViewportInteractor::HandleInputKey( ViewportClient, ControllerMotionSource == FXRMotionControllerBase::LeftHandSourceId ? MotionController_Left_FullyPressedTriggerAxis : MotionController_Right_FullyPressedTriggerAxis, InputEvent );
|
|
}
|
|
else if (bIsTriggerFullyPressed && Delta < TriggerPressedThreshold)
|
|
{
|
|
bIsTriggerFullyPressed = false;
|
|
|
|
const EInputEvent InputEvent = IE_Released;
|
|
UViewportInteractor::HandleInputKey( ViewportClient, ControllerMotionSource == FXRMotionControllerBase::LeftHandSourceId ? MotionController_Left_FullyPressedTriggerAxis : MotionController_Right_FullyPressedTriggerAxis, InputEvent );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bOutWasHandled)
|
|
{
|
|
if (Delta != 0)
|
|
{
|
|
if (Action.ActionType == VRActionTypes::TrackpadPositionX)
|
|
{
|
|
LastTrackpadPosition.X = bIsTrackpadPositionValid[0] ? TrackpadPosition.X : Delta;
|
|
LastTrackpadPositionUpdateTime = FTimespan::FromSeconds(FPlatformTime::Seconds());
|
|
TrackpadPosition.X = Delta;
|
|
bIsTrackpadPositionValid[0] = true;
|
|
}
|
|
|
|
if (Action.ActionType == VRActionTypes::TrackpadPositionY)
|
|
{
|
|
float DeltaAxis = Delta;
|
|
if (VREd::InvertTrackpadVertical->GetInt() != 0)
|
|
{
|
|
DeltaAxis = -DeltaAxis; // Y axis is inverted from HMD
|
|
}
|
|
|
|
LastTrackpadPosition.Y = bIsTrackpadPositionValid[1] ? TrackpadPosition.Y : DeltaAxis;
|
|
LastTrackpadPositionUpdateTime = FTimespan::FromSeconds(FPlatformTime::Seconds());
|
|
TrackpadPosition.Y = DeltaAxis;
|
|
bIsTrackpadPositionValid[1] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Super::HandleInputAxis( ViewportClient, Action, Key, Delta, DeltaTime, bOutWasHandled );
|
|
}
|
|
|
|
void UVREditorInteractor::ToggleSequencerScrubbingMode()
|
|
{
|
|
bIsScrubbingSequence = !bIsScrubbingSequence;
|
|
}
|
|
|
|
bool UVREditorInteractor::IsScrubbingSequencer() const
|
|
{
|
|
return bIsScrubbingSequence;
|
|
}
|
|
|
|
class UMotionControllerComponent* UVREditorInteractor::GetMotionControllerComponent() const
|
|
{
|
|
return MotionControllerComponent;
|
|
}
|
|
|
|
void UVREditorInteractor::SetControllerHandSide( const FName InControllerHandSide )
|
|
{
|
|
ControllerMotionSource = InControllerHandSide;
|
|
}
|
|
|
|
FName UVREditorInteractor::GetControllerHandSide() const
|
|
{
|
|
return ControllerMotionSource;
|
|
}
|
|
|
|
void UVREditorInteractor::ResetHoverState()
|
|
{
|
|
Super::ResetHoverState();
|
|
bIsHoveringOverUI = false;
|
|
}
|
|
|
|
float UVREditorInteractor::GetSlideDelta_Implementation() const
|
|
{
|
|
return GetTrackpadSlideDelta();
|
|
}
|
|
|
|
void UVREditorInteractor::PlayHapticEffect( const float Strength )
|
|
{
|
|
IInputInterface* InputInterface = FSlateApplication::Get().GetInputInterface();
|
|
if (InputInterface)
|
|
{
|
|
const double CurrentTime = FPlatformTime::Seconds();
|
|
|
|
//@todo viewportinteration
|
|
FForceFeedbackValues ForceFeedbackValues;
|
|
ForceFeedbackValues.LeftLarge = ControllerMotionSource == FXRMotionControllerBase::LeftHandSourceId ? Strength : 0;
|
|
ForceFeedbackValues.RightLarge = ControllerMotionSource == FXRMotionControllerBase::RightHandSourceId ? Strength : 0;
|
|
|
|
// @todo vreditor: If an Xbox controller is plugged in, this causes both the motion controllers and the Xbox controller to vibrate!
|
|
InputInterface->SetForceFeedbackChannelValues( WorldInteraction->GetMotionControllerID(), ForceFeedbackValues );
|
|
}
|
|
}
|
|
|
|
void UVREditorInteractor::SetForceShowLaser( const bool bInForceShow )
|
|
{
|
|
bForceShowLaser = bInForceShow;
|
|
}
|
|
|
|
bool UVREditorInteractor::IsCarrying() const
|
|
{
|
|
const TArray<TUniquePtr<class FViewportTransformable>>& Transformables = VRMode->GetWorldInteraction().GetTransformables();
|
|
const bool bCanBeCarried = Transformables.Num() == 1 && Transformables[0].Get()->ShouldBeCarried();
|
|
return (bCanBeCarried && GetDraggingMode() == EViewportInteractionDraggingMode::TransformablesFreely);
|
|
}
|
|
|
|
float UVREditorInteractor::GetTrackpadSlideDelta( const bool Axis /*= 1*/ ) const
|
|
{
|
|
if (IsCarrying())
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
const bool bIsAbsolute = (GetVRMode().GetHMDDeviceType() == SteamVRDeviceType);
|
|
float SlideDelta = 0.0f;
|
|
if (bIsTouchingTrackpad || !bIsAbsolute)
|
|
{
|
|
if (bIsAbsolute)
|
|
{
|
|
SlideDelta = ((TrackpadPosition[Axis] - LastTrackpadPosition[Axis]) * VREd::TrackpadAbsoluteDragSpeed->GetFloat());
|
|
}
|
|
else
|
|
{
|
|
SlideDelta = (TrackpadPosition[Axis] * VREd::TrackpadRelativeDragSpeed->GetFloat());
|
|
}
|
|
}
|
|
|
|
return SlideDelta;
|
|
|
|
}
|
|
|
|
|
|
void UVREditorInteractor::PollInput()
|
|
{
|
|
bHaveMotionController = false;
|
|
InteractorData.LastTransform = InteractorData.Transform;
|
|
InteractorData.LastRoomSpaceTransform = InteractorData.RoomSpaceTransform;
|
|
|
|
// Generic motion controllers
|
|
TArray<IMotionController*> MotionControllers = IModularFeatures::Get().GetModularFeatureImplementations<IMotionController>( IMotionController::GetModularFeatureName() );
|
|
for (auto MotionController : MotionControllers) // @todo viewportinteraction: Needs support for multiple pairs of motion controllers
|
|
{
|
|
if (MotionController != nullptr && !bHaveMotionController)
|
|
{
|
|
FVector Location = FVector::ZeroVector;
|
|
FRotator Rotation = FRotator::ZeroRotator;
|
|
float WorldScale = 100.0f;
|
|
if (VRMode != nullptr)
|
|
{
|
|
WorldScale = GetVRMode().GetWorldScaleFactor() *100.0f; // WorldScaleFactor is worldscale / 100.0
|
|
}
|
|
|
|
if (MotionController->GetControllerOrientationAndPosition( WorldInteraction->GetMotionControllerID(), ControllerMotionSource, /* Out */ Rotation, /* Out */ Location, WorldScale ))
|
|
{
|
|
bHaveMotionController = true;
|
|
InteractorData.RoomSpaceTransform = FTransform( Rotation.Quaternion(), Location, FVector( 1.0f ) );
|
|
InteractorData.Transform = InteractorData.RoomSpaceTransform * WorldInteraction->GetRoomTransform();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool UVREditorInteractor::GetTransformAndForwardVector( FTransform& OutHandTransform, FVector& OutForwardVector ) const
|
|
{
|
|
if (bHaveMotionController)
|
|
{
|
|
OutHandTransform = InteractorData.Transform;
|
|
|
|
const float LaserPointerRotationOffset = GetHMDDeviceType() == OculusDeviceType ? VREd::OculusLaserPointerRotationOffset->GetFloat() : VREd::ViveLaserPointerRotationOffset->GetFloat();
|
|
OutForwardVector = OutHandTransform.GetRotation().RotateVector( FRotator( LaserPointerRotationOffset, 0.0f, 0.0f ).RotateVector( FVector( 1.0f, 0.0f, 0.0f ) ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/** Changes the color of the buttons on the handmesh */
|
|
void UVREditorInteractor::ApplyButtonPressColors( const FViewportActionKeyInput& Action )
|
|
{
|
|
const float PressStrength = 10.0f;
|
|
const FName ActionType = Action.ActionType;
|
|
const EInputEvent Event = Action.Event;
|
|
|
|
// Trigger
|
|
if (ActionType == ViewportWorldActionTypes::SelectAndMove)
|
|
{
|
|
static FName StaticTriggerParameter( "B1" );
|
|
SetMotionControllerButtonPressedVisuals( Event, StaticTriggerParameter, PressStrength );
|
|
}
|
|
|
|
// Shoulder button
|
|
if (ActionType == ViewportWorldActionTypes::WorldMovement)
|
|
{
|
|
static FName StaticShoulderParameter( "B2" );
|
|
SetMotionControllerButtonPressedVisuals( Event, StaticShoulderParameter, PressStrength );
|
|
}
|
|
|
|
// Trackpad
|
|
if (ActionType == VRActionTypes::ConfirmRadialSelection)
|
|
{
|
|
static FName StaticTrackpadParameter( "B3" );
|
|
SetMotionControllerButtonPressedVisuals( Event, StaticTrackpadParameter, PressStrength );
|
|
}
|
|
|
|
// Modifier
|
|
if (ActionType == VRActionTypes::Modifier)
|
|
{
|
|
static FName StaticModifierParameter( "B4" );
|
|
SetMotionControllerButtonPressedVisuals( Event, StaticModifierParameter, PressStrength );
|
|
}
|
|
|
|
if (ActionType == VRActionTypes::Modifier2)
|
|
{
|
|
static FName StaticModifierParameter( "B5" );
|
|
SetMotionControllerButtonPressedVisuals( Event, StaticModifierParameter, PressStrength );
|
|
}
|
|
}
|
|
|
|
void UVREditorInteractor::SetMotionControllerButtonPressedVisuals( const EInputEvent Event, const FName& ParameterName, const float PressStrength )
|
|
{
|
|
if (Event == IE_Pressed)
|
|
{
|
|
HandMeshMID->SetScalarParameterValue( ParameterName, PressStrength );
|
|
}
|
|
else if (Event == IE_Released)
|
|
{
|
|
HandMeshMID->SetScalarParameterValue( ParameterName, 0.0f );
|
|
}
|
|
}
|
|
|
|
void UVREditorInteractor::ShowHelpForHand( const bool bShowIt )
|
|
{
|
|
if (bShowIt != bWantHelpLabels)
|
|
{
|
|
bWantHelpLabels = bShowIt;
|
|
|
|
const FTimespan CurrentTime = FTimespan::FromSeconds( FApp::GetCurrentTime() );
|
|
const FTimespan TimeSinceStartedFadingOut = CurrentTime - HelpLabelShowOrHideStartTime;
|
|
const FTimespan HelpLabelFadeDuration = FTimespan::FromSeconds( VREd::HelpLabelFadeDuration->GetFloat() );
|
|
|
|
// If we were already fading, account for that here
|
|
if (TimeSinceStartedFadingOut < HelpLabelFadeDuration)
|
|
{
|
|
// We were already fading, so we'll reverse the time value so it feels continuous
|
|
HelpLabelShowOrHideStartTime = CurrentTime - (HelpLabelFadeDuration - TimeSinceStartedFadingOut);
|
|
}
|
|
else
|
|
{
|
|
HelpLabelShowOrHideStartTime = FTimespan::FromSeconds( FApp::GetCurrentTime() );
|
|
}
|
|
|
|
if (bShowIt && HelpLabels.Num() == 0)
|
|
{
|
|
for (const auto& KeyToAction : KeyToActionMap)
|
|
{
|
|
const FKey Key = KeyToAction.Key;
|
|
const FViewportActionKeyInput& Action = KeyToAction.Value;
|
|
|
|
UStaticMeshSocket* Socket = FindMeshSocketForKey( HandMeshComponent->GetStaticMesh(), Key );
|
|
if (Socket != nullptr)
|
|
{
|
|
FText LabelText;
|
|
FString ComponentName;
|
|
|
|
if (Action.ActionType == VRActionTypes::Modifier)
|
|
{
|
|
LabelText = LOCTEXT( "ModifierHelp", "Modifier" );
|
|
ComponentName = TEXT( "ModifierHelp" );
|
|
}
|
|
else if (Action.ActionType == ViewportWorldActionTypes::WorldMovement)
|
|
{
|
|
LabelText = LOCTEXT( "WorldMovementHelp", "Move World" );
|
|
ComponentName = TEXT( "WorldMovementHelp" );
|
|
}
|
|
else if (Action.ActionType == ViewportWorldActionTypes::SelectAndMove_FullyPressed)
|
|
{
|
|
LabelText = LOCTEXT( "SelectAndMove_FullyPressedHelp", "Select & Move" );
|
|
ComponentName = TEXT( "SelectAndMove_FullyPressedHelp" );
|
|
}
|
|
else if (Action.ActionType == ViewportWorldActionTypes::SelectAndMove)
|
|
{
|
|
LabelText = LOCTEXT( "SelectAndMove_Help", "Select & Move" );
|
|
ComponentName = TEXT( "SelectAndMove_Help" );
|
|
}
|
|
else if (Action.ActionType == VRActionTypes::Touch)
|
|
{
|
|
LabelText = LOCTEXT( "TouchHelp", "Slide" );
|
|
ComponentName = TEXT( "TouchHelp" );
|
|
}
|
|
else if (Action.ActionType == ViewportWorldActionTypes::Undo)
|
|
{
|
|
LabelText = LOCTEXT( "UndoHelp", "Undo" );
|
|
ComponentName = TEXT( "UndoHelp" );
|
|
}
|
|
else if (Action.ActionType == ViewportWorldActionTypes::Redo)
|
|
{
|
|
LabelText = LOCTEXT( "RedoHelp", "Redo" );
|
|
ComponentName = TEXT( "RedoHelp" );
|
|
}
|
|
else if (Action.ActionType == ViewportWorldActionTypes::Delete)
|
|
{
|
|
LabelText = LOCTEXT( "DeleteHelp", "Delete" );
|
|
ComponentName = TEXT( "DeleteHelp" );
|
|
}
|
|
else if (Action.ActionType == VRActionTypes::ConfirmRadialSelection)
|
|
{
|
|
LabelText = LOCTEXT( "ConfirmRadialSelectionHelp", "Radial Menu" );
|
|
ComponentName = TEXT( "ConfirmRadialSelectionHelp" );
|
|
}
|
|
|
|
const bool bWithSceneComponent = false; // Nope, we'll spawn our own inside AFloatingText
|
|
check( VRMode );
|
|
AFloatingText* FloatingText = GetVRMode().SpawnTransientSceneActor<AFloatingText>( ComponentName );
|
|
FloatingText->SetText( LabelText );
|
|
|
|
HelpLabels.Add( Key, FloatingText );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void UVREditorInteractor::UpdateHelpLabels()
|
|
{
|
|
const FTimespan HelpLabelFadeDuration = FTimespan::FromSeconds( VREd::HelpLabelFadeDuration->GetFloat() );
|
|
|
|
const FTransform HeadTransform = GetVRMode().GetHeadTransform();
|
|
|
|
// Only show help labels if the hand is pretty close to the face
|
|
const float DistanceToHead = (GetTransform().GetLocation() - HeadTransform.GetLocation()).Size();
|
|
const float MinDistanceToHeadForHelp = VREd::HelpLabelFadeDistance->GetFloat() * GetVRMode().GetWorldScaleFactor(); // (in cm)
|
|
bool bShowHelp = VREd::ShowControllerHelpLabels->GetInt() != 0 && DistanceToHead <= MinDistanceToHeadForHelp;
|
|
|
|
// Don't show help if a UI is summoned on that hand
|
|
if (HasUIOnForearm() || GetVRMode().GetUISystem().IsShowingRadialMenu( this ))
|
|
{
|
|
bShowHelp = false;
|
|
}
|
|
|
|
ShowHelpForHand( bShowHelp );
|
|
|
|
// Have the labels finished fading out? If so, we'll kill their actors!
|
|
const FTimespan CurrentTime = FTimespan::FromSeconds( FApp::GetCurrentTime() );
|
|
const FTimespan TimeSinceStartedFadingOut = CurrentTime - HelpLabelShowOrHideStartTime;
|
|
if (!bWantHelpLabels && (TimeSinceStartedFadingOut > HelpLabelFadeDuration))
|
|
{
|
|
// Get rid of help text
|
|
for (auto& KeyAndValue : HelpLabels)
|
|
{
|
|
AFloatingText* FloatingText = KeyAndValue.Value;
|
|
GetVRMode().DestroyTransientActor( FloatingText );
|
|
}
|
|
HelpLabels.Reset();
|
|
}
|
|
else
|
|
{
|
|
// Update fading state
|
|
float FadeAlpha = FMath::Clamp( (float)TimeSinceStartedFadingOut.GetTotalSeconds() / (float)HelpLabelFadeDuration.GetTotalSeconds(), 0.0f, 1.0f );
|
|
if (!bWantHelpLabels)
|
|
{
|
|
FadeAlpha = 1.0f - FadeAlpha;
|
|
}
|
|
|
|
// Exponential falloff, so the fade is really obvious (gamma/HDR)
|
|
FadeAlpha = FMath::Pow( FadeAlpha, 3.0f );
|
|
|
|
for (auto& KeyAndValue : HelpLabels)
|
|
{
|
|
const FKey Key = KeyAndValue.Key;
|
|
AFloatingText* FloatingText = KeyAndValue.Value;
|
|
|
|
UStaticMeshSocket* Socket = FindMeshSocketForKey( HandMeshComponent->GetStaticMesh(), Key );
|
|
check( Socket != nullptr );
|
|
FTransform SocketRelativeTransform( Socket->RelativeRotation, Socket->RelativeLocation, Socket->RelativeScale );
|
|
|
|
// Oculus has asymmetrical controllers, so we the sock transform horizontally
|
|
if (ControllerMotionSource == FXRMotionControllerBase::RightHandSourceId && GetVRMode().GetHMDDeviceType() == OculusDeviceType)
|
|
{
|
|
const FVector Scale3D = SocketRelativeTransform.GetLocation();
|
|
SocketRelativeTransform.SetLocation( FVector( Scale3D.X, -Scale3D.Y, Scale3D.Z ) );
|
|
}
|
|
|
|
// Make sure the labels stay the same size even when the world is scaled
|
|
FTransform HandTransformWithWorldToMetersScaling = GetTransform();
|
|
HandTransformWithWorldToMetersScaling.SetScale3D( HandTransformWithWorldToMetersScaling.GetScale3D() * FVector( GetVRMode().GetWorldScaleFactor() ) );
|
|
|
|
// Position right on top of the controller itself
|
|
FTransform FloatingTextTransform = SocketRelativeTransform * HandTransformWithWorldToMetersScaling;
|
|
FloatingText->SetActorTransform( FloatingTextTransform );
|
|
|
|
// Orientate it toward the viewer
|
|
FloatingText->Update( HeadTransform.GetLocation() );
|
|
|
|
// Update fade state
|
|
FloatingText->SetOpacity( FadeAlpha );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UStaticMeshSocket* UVREditorInteractor::FindMeshSocketForKey( UStaticMesh* StaticMesh, const FKey Key )
|
|
{
|
|
// @todo vreditor: Hard coded mapping of socket names (e.g. "Shoulder") to expected names of sockets in the static mesh
|
|
FName SocketName = NAME_None;
|
|
if (Key == EKeys::Vive_Left_Menu_Click || Key == EKeys::Vive_Right_Menu_Click)
|
|
{
|
|
static FName ShoulderSocketName( "Shoulder" );
|
|
SocketName = ShoulderSocketName;
|
|
}
|
|
else if (Key == VREditorKeyNames::MotionController_Left_FullyPressedTriggerAxis || Key == VREditorKeyNames::MotionController_Right_FullyPressedTriggerAxis ||
|
|
Key == VREditorKeyNames::MotionController_Left_PressedTriggerAxis || Key == VREditorKeyNames::MotionController_Right_PressedTriggerAxis)
|
|
{
|
|
static FName TriggerSocketName( "Trigger" );
|
|
SocketName = TriggerSocketName;
|
|
}
|
|
else if (Key == EKeys::Vive_Left_Grip_Click || Key == EKeys::Vive_Right_Grip_Click ||
|
|
Key == EKeys::ValveIndex_Left_Trackpad_Touch || Key == EKeys::ValveIndex_Right_Trackpad_Touch ||
|
|
Key == EKeys::OculusTouch_Left_Grip_Click || Key == EKeys::OculusTouch_Right_Grip_Click)
|
|
{
|
|
static FName GripSocketName( "Grip" );
|
|
SocketName = GripSocketName;
|
|
}
|
|
else if (Key == EKeys::Vive_Left_Trackpad_Click || Key == EKeys::Vive_Right_Trackpad_Click ||
|
|
Key == EKeys::ValveIndex_Left_Thumbstick_Touch || Key == EKeys::ValveIndex_Right_Thumbstick_Click ||
|
|
Key == EKeys::OculusTouch_Left_Thumbstick_Touch || Key == EKeys::OculusTouch_Right_Thumbstick_Click)
|
|
{
|
|
static FName ThumbstickSocketName( "Thumbstick" );
|
|
SocketName = ThumbstickSocketName;
|
|
}
|
|
else if (Key == EKeys::Vive_Left_Trackpad_Touch || Key == EKeys::Vive_Right_Trackpad_Touch ||
|
|
Key == EKeys::ValveIndex_Left_Thumbstick_Touch || Key == EKeys::ValveIndex_Right_Thumbstick_Touch ||
|
|
Key == EKeys::OculusTouch_Left_Thumbstick_Touch || Key == EKeys::OculusTouch_Right_Thumbstick_Touch)
|
|
{
|
|
static FName TouchSocketName( "Touch" );
|
|
SocketName = TouchSocketName;
|
|
}
|
|
else if (Key == EKeys::Vive_Left_Trackpad_Down || Key == EKeys::Vive_Right_Trackpad_Down ||
|
|
Key == EKeys::ValveIndex_Left_Thumbstick_Down || Key == EKeys::ValveIndex_Right_Thumbstick_Down ||
|
|
Key == EKeys::OculusTouch_Left_Thumbstick_Down || Key == EKeys::OculusTouch_Right_Thumbstick_Down)
|
|
{
|
|
static FName DownSocketName( "Down" );
|
|
SocketName = DownSocketName;
|
|
}
|
|
else if (Key == EKeys::Vive_Left_Trackpad_Up || Key == EKeys::Vive_Right_Trackpad_Up ||
|
|
Key == EKeys::ValveIndex_Left_Thumbstick_Up || Key == EKeys::ValveIndex_Right_Thumbstick_Up ||
|
|
Key == EKeys::OculusTouch_Left_Thumbstick_Up || Key == EKeys::OculusTouch_Right_Thumbstick_Up)
|
|
{
|
|
static FName UpSocketName( "Up" );
|
|
SocketName = UpSocketName;
|
|
}
|
|
else if (Key == EKeys::Vive_Left_Trackpad_Left || Key == EKeys::Vive_Right_Trackpad_Left ||
|
|
Key == EKeys::ValveIndex_Left_Thumbstick_Left || Key == EKeys::ValveIndex_Right_Thumbstick_Left ||
|
|
Key == EKeys::OculusTouch_Left_Thumbstick_Left || Key == EKeys::OculusTouch_Right_Thumbstick_Left)
|
|
{
|
|
static FName LeftSocketName( "Left" );
|
|
SocketName = LeftSocketName;
|
|
}
|
|
else if (Key == EKeys::Vive_Left_Trackpad_Right || Key == EKeys::Vive_Right_Trackpad_Right ||
|
|
Key == EKeys::ValveIndex_Left_Thumbstick_Right || Key == EKeys::ValveIndex_Right_Thumbstick_Right ||
|
|
Key == EKeys::OculusTouch_Left_Thumbstick_Right || Key == EKeys::OculusTouch_Right_Thumbstick_Right)
|
|
{
|
|
static FName RightSocketName( "Right" );
|
|
SocketName = RightSocketName;
|
|
}
|
|
else if (Key == EKeys::ValveIndex_Left_A_Click || Key == EKeys::ValveIndex_Right_A_Click ||
|
|
Key == EKeys::OculusTouch_Left_X_Click || Key == EKeys::OculusTouch_Right_A_Click)
|
|
{
|
|
static FName FaceButton1SocketName( "FaceButton1" );
|
|
SocketName = FaceButton1SocketName;
|
|
}
|
|
else if (Key == EKeys::ValveIndex_Left_B_Click || Key == EKeys::ValveIndex_Right_B_Click ||
|
|
Key == EKeys::OculusTouch_Left_Y_Click || Key == EKeys::OculusTouch_Right_B_Click)
|
|
{
|
|
static FName FaceButton2SocketName( "FaceButton2" );
|
|
SocketName = FaceButton2SocketName;
|
|
}
|
|
else
|
|
{
|
|
// Not a key that we care about
|
|
}
|
|
|
|
if (SocketName != NAME_None)
|
|
{
|
|
UStaticMeshSocket* Socket = StaticMesh->FindSocket( SocketName );
|
|
if (Socket != nullptr)
|
|
{
|
|
return Socket;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
};
|
|
|
|
void UVREditorInteractor::UpdateSplineLaser( const FVector& InStartLocation, const FVector& InEndLocation, const FVector& InForward )
|
|
{
|
|
if (LaserSplineComponent)
|
|
{
|
|
LaserStart = InStartLocation;
|
|
LaserEnd = InEndLocation;
|
|
|
|
// Clear the segments before updating it
|
|
LaserSplineComponent->ClearSplinePoints( true );
|
|
|
|
const FVector SmoothLaserDirection = InEndLocation - InStartLocation;
|
|
float Distance = SmoothLaserDirection.Size();
|
|
const FVector StraightLaserEndLocation = InStartLocation + (InForward * Distance);
|
|
const int32 NumLaserSplinePoints = LaserSplineMeshComponents.Num();
|
|
|
|
LaserSplineComponent->AddSplinePoint( InStartLocation, ESplineCoordinateSpace::Local, false );
|
|
for (int32 Index = 1; Index < NumLaserSplinePoints; Index++)
|
|
{
|
|
float Alpha = (float)Index / (float)NumLaserSplinePoints;
|
|
Alpha = FMath::Sin( Alpha * PI * 0.5f );
|
|
const FVector PointOnStraightLaser = FMath::Lerp( InStartLocation, StraightLaserEndLocation, Alpha );
|
|
const FVector PointOnSmoothLaser = FMath::Lerp( InStartLocation, InEndLocation, Alpha );
|
|
const FVector PointBetweenLasers = FMath::Lerp( PointOnStraightLaser, PointOnSmoothLaser, Alpha );
|
|
LaserSplineComponent->AddSplinePoint( PointBetweenLasers, ESplineCoordinateSpace::Local, false );
|
|
}
|
|
LaserSplineComponent->AddSplinePoint( InEndLocation, ESplineCoordinateSpace::Local, false );
|
|
|
|
// Update all the segments of the spline
|
|
LaserSplineComponent->UpdateSpline();
|
|
|
|
const float LaserPointerRadius = VREd::LaserPointerRadius->GetFloat() * VRMode->GetWorldScaleFactor();
|
|
Distance *= 0.0001f;
|
|
for (int32 Index = 0; Index < NumLaserSplinePoints; Index++)
|
|
{
|
|
USplineMeshComponent* SplineMeshComponent = LaserSplineMeshComponents[Index];
|
|
check( SplineMeshComponent != nullptr );
|
|
|
|
FVector StartLoc, StartTangent, EndLoc, EndTangent;
|
|
LaserSplineComponent->GetLocationAndTangentAtSplinePoint( Index, StartLoc, StartTangent, ESplineCoordinateSpace::Local );
|
|
LaserSplineComponent->GetLocationAndTangentAtSplinePoint( Index + 1, EndLoc, EndTangent, ESplineCoordinateSpace::Local );
|
|
|
|
const float AlphaIndex = (float)Index / (float)NumLaserSplinePoints;
|
|
const float AlphaDistance = Distance * AlphaIndex;
|
|
float Radius = LaserPointerRadius * ((AlphaIndex * AlphaDistance) + 1);
|
|
FVector2D LaserScale( Radius, Radius );
|
|
SplineMeshComponent->SetStartScale( LaserScale, false );
|
|
|
|
const float NextAlphaIndex = (float)(Index + 1) / (float)NumLaserSplinePoints;
|
|
const float NextAlphaDistance = Distance * NextAlphaIndex;
|
|
Radius = LaserPointerRadius * ((NextAlphaIndex * NextAlphaDistance) + 1);
|
|
LaserScale = FVector2D( Radius, Radius );
|
|
SplineMeshComponent->SetEndScale( LaserScale, false );
|
|
|
|
SplineMeshComponent->SetStartAndEnd( StartLoc, StartTangent, EndLoc, EndTangent, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
void UVREditorInteractor::SetLaserVisibility( const bool bVisible )
|
|
{
|
|
for (USplineMeshComponent* SplineMeshComponent : LaserSplineMeshComponents)
|
|
{
|
|
SplineMeshComponent->SetVisibility( bVisible );
|
|
}
|
|
}
|
|
|
|
void UVREditorInteractor::SetLaserVisuals( const FLinearColor& NewColor, const float CrawlFade, const float CrawlSpeed )
|
|
{
|
|
static FName StaticLaserColorParameterName( "LaserColor" );
|
|
LaserPointerMID->SetVectorParameterValue( StaticLaserColorParameterName, NewColor );
|
|
TranslucentLaserPointerMID->SetVectorParameterValue( StaticLaserColorParameterName, NewColor );
|
|
|
|
static FName StaticCrawlParameterName( "Crawl" );
|
|
LaserPointerMID->SetScalarParameterValue( StaticCrawlParameterName, CrawlFade );
|
|
TranslucentLaserPointerMID->SetScalarParameterValue( StaticCrawlParameterName, CrawlFade );
|
|
|
|
static FName StaticCrawlSpeedParameterName( "CrawlSpeed" );
|
|
LaserPointerMID->SetScalarParameterValue( StaticCrawlSpeedParameterName, CrawlSpeed );
|
|
TranslucentLaserPointerMID->SetScalarParameterValue( StaticCrawlSpeedParameterName, CrawlSpeed );
|
|
|
|
static FName StaticHandTrimColorParameter( "TrimGlowColor" );
|
|
HandMeshMID->SetVectorParameterValue( StaticHandTrimColorParameter, NewColor );
|
|
|
|
HoverPointLightComponent->SetLightColor( NewColor );
|
|
}
|
|
|
|
void UVREditorInteractor::UpdateRadialMenuInput( const float DeltaTime )
|
|
{
|
|
UVREditorUISystem& UISystem = GetVRMode().GetUISystem();
|
|
const FName HMDDeviceType = GetVRMode().GetHMDDeviceType();
|
|
//Update the radial menu
|
|
EViewportInteractionDraggingMode DraggingMode = GetDraggingMode();
|
|
if (GetControllerType() == EControllerType::UI)
|
|
{
|
|
if ((bIsTrackpadPositionValid[0] && bIsTrackpadPositionValid[1]) &&
|
|
DraggingMode != EViewportInteractionDraggingMode::AssistingDrag)
|
|
{
|
|
if (bIsScrubbingSequence)
|
|
{
|
|
const FVector2D ReturnToCenter = FVector2D::ZeroVector;
|
|
UISystem.GetRadialMenuFloatingUI()->HighlightSlot( ReturnToCenter );
|
|
|
|
const float NewPlayRate = FMath::GetMappedRangeValueClamped( FVector2D( -1.0f, 1.0f ), FVector2D( -1.0f*VREd::SequencerScrubMax->GetFloat(), VREd::SequencerScrubMax->GetFloat() ), TrackpadPosition.X );
|
|
FVREditorActionCallbacks::PlaySequenceAtRate( VRMode, NewPlayRate );
|
|
}
|
|
else
|
|
{
|
|
// Update the radial menu if we are already showing the radial menu
|
|
if (UISystem.IsShowingRadialMenu( this ))
|
|
{
|
|
if (UISystem.GetRadialMenuFloatingUI()->GetWidgetComponents().Num() > 0)
|
|
{
|
|
UISystem.GetRadialMenuFloatingUI()->HighlightSlot( TrackpadPosition );
|
|
|
|
if (TrackpadPosition.GetAbsMax() > VREd::MinJoystickOffsetBeforeFlick->GetFloat())
|
|
{
|
|
LastActiveTrackpadUpdateTime = FTimespan::FromSeconds( FPlatformTime::Seconds() );
|
|
}
|
|
}
|
|
}
|
|
else if (!UISystem.IsShowingRadialMenu( this ) &&
|
|
TrackpadPosition.GetAbsMax() > VREd::MinJoystickOffsetBeforeFlick->GetFloat())
|
|
{
|
|
const bool bForceRefresh = false;
|
|
UISystem.TryToSpawnRadialMenu( this, bForceRefresh );
|
|
LastActiveTrackpadUpdateTime = FTimespan::FromSeconds( FPlatformTime::Seconds() );
|
|
}
|
|
}
|
|
}
|
|
// If we are not currently touching the Vive touchpad, reset the highlighted button and pause sequencer playback if scrubbing
|
|
else if (HMDDeviceType == SteamVRDeviceType && !bIsTouchingTrackpad)
|
|
{
|
|
if (bIsScrubbingSequence)
|
|
{
|
|
FVREditorActionCallbacks::PauseSequencePlayback( VRMode );
|
|
}
|
|
|
|
if (UISystem.IsShowingRadialMenu( this ))
|
|
{
|
|
const FVector2D ReturnToCenter = FVector2D::ZeroVector;
|
|
UISystem.GetRadialMenuFloatingUI()->HighlightSlot( ReturnToCenter );
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (GetControllerType() == EControllerType::Laser)
|
|
{
|
|
if (HMDDeviceType != SteamVRDeviceType &&
|
|
(bIsTrackpadPositionValid[0] && bIsTrackpadPositionValid[1]) &&
|
|
DraggingMode != EViewportInteractionDraggingMode::TransformablesWithGizmo &&
|
|
DraggingMode != EViewportInteractionDraggingMode::TransformablesFreely &&
|
|
DraggingMode != EViewportInteractionDraggingMode::TransformablesAtLaserImpact &&
|
|
DraggingMode != EViewportInteractionDraggingMode::AssistingDrag &&
|
|
!VRMode->IsAimingTeleport())
|
|
{
|
|
// Move thumbstick left to undo
|
|
if (TrackpadPosition.X < -1 * VREd::MinJoystickOffsetBeforeFlick->GetFloat() &&
|
|
bFlickActionExecuted == false &&
|
|
!IsHoveringOverUI())
|
|
{
|
|
if (bIsUndoRedoSwipeEnabled)
|
|
{
|
|
VRMode->GetWorldInteraction().Undo();
|
|
}
|
|
bFlickActionExecuted = true;
|
|
}
|
|
// Move thumbstick right to redo
|
|
if (TrackpadPosition.X > VREd::MinJoystickOffsetBeforeFlick->GetFloat() &&
|
|
bFlickActionExecuted == false &&
|
|
!IsHoveringOverUI())
|
|
{
|
|
if (bIsUndoRedoSwipeEnabled)
|
|
{
|
|
VRMode->GetWorldInteraction().Redo();
|
|
}
|
|
bFlickActionExecuted = true;
|
|
}
|
|
// Center to reset
|
|
// TODO: Remove finger from touchpad to reset vive
|
|
if (FMath::IsNearlyZero( TrackpadPosition.X ) &&
|
|
!IsHoveringOverUI())
|
|
{
|
|
bFlickActionExecuted = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UVREditorInteractor::UndoRedoFromSwipe( const ETouchSwipeDirection InSwipeDirection )
|
|
{
|
|
EViewportInteractionDraggingMode DraggingMode = GetDraggingMode();
|
|
if (GetControllerType() == EControllerType::Laser &&
|
|
VRMode->GetHMDDeviceType() == SteamVRDeviceType &&
|
|
DraggingMode != EViewportInteractionDraggingMode::TransformablesWithGizmo &&
|
|
DraggingMode != EViewportInteractionDraggingMode::TransformablesFreely &&
|
|
DraggingMode != EViewportInteractionDraggingMode::TransformablesAtLaserImpact &&
|
|
DraggingMode != EViewportInteractionDraggingMode::AssistingDrag &&
|
|
!VRMode->IsAimingTeleport())
|
|
{
|
|
if (InSwipeDirection == ETouchSwipeDirection::Left)
|
|
{
|
|
VRMode->GetWorldInteraction().Undo();
|
|
bFlickActionExecuted = true;
|
|
}
|
|
else if (InSwipeDirection == ETouchSwipeDirection::Right)
|
|
{
|
|
VRMode->GetWorldInteraction().Redo();
|
|
bFlickActionExecuted = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UVREditorInteractor::GetIsLaserBlocked() const
|
|
{
|
|
return Super::GetIsLaserBlocked() || (GetControllerType() != EControllerType::Laser && GetControllerType() != EControllerType::AssistingLaser);
|
|
}
|
|
|
|
|
|
void UVREditorInteractor::ResetTrackpad()
|
|
{
|
|
TrackpadPosition = FVector2D::ZeroVector;
|
|
bIsTouchingTrackpad = false;
|
|
bIsTrackpadPositionValid[0] = false;
|
|
bIsTrackpadPositionValid[1] = false;
|
|
}
|
|
|
|
bool UVREditorInteractor::IsTouchingTrackpad() const
|
|
{
|
|
return bIsTouchingTrackpad;
|
|
}
|
|
|
|
FVector2D UVREditorInteractor::GetTrackpadPosition() const
|
|
{
|
|
return TrackpadPosition;
|
|
}
|
|
|
|
FVector2D UVREditorInteractor::GetLastTrackpadPosition() const
|
|
{
|
|
return LastTrackpadPosition;
|
|
}
|
|
|
|
bool UVREditorInteractor::IsTrackpadPositionValid( const int32 AxisIndex ) const
|
|
{
|
|
return bIsTrackpadPositionValid[AxisIndex];
|
|
}
|
|
|
|
FTimespan& UVREditorInteractor::GetLastTrackpadPositionUpdateTime()
|
|
{
|
|
return LastTrackpadPositionUpdateTime;
|
|
}
|
|
|
|
FTimespan& UVREditorInteractor::GetLastActiveTrackpadUpdateTime()
|
|
{
|
|
return LastActiveTrackpadUpdateTime;
|
|
}
|
|
|
|
const FVector& UVREditorInteractor::GetLaserStart() const
|
|
{
|
|
return LaserStart;
|
|
}
|
|
|
|
const FVector& UVREditorInteractor::GetLaserEnd() const
|
|
{
|
|
return LaserEnd;
|
|
}
|
|
|
|
void UVREditorInteractor::SetForceLaserColor( const FLinearColor& InColor )
|
|
{
|
|
ForceLaserColor = InColor;
|
|
}
|
|
|
|
AVREditorTeleporter* UVREditorInteractor::GetTeleportActor()
|
|
{
|
|
return VRMode->GetTeleportActor();
|
|
}
|
|
|
|
bool UVREditorInteractor::IsHoveringOverUI() const
|
|
{
|
|
return bIsHoveringOverUI;
|
|
}
|
|
|
|
void UVREditorInteractor::SetHasUIOnForearm( const bool bInHasUIOnForearm )
|
|
{
|
|
bHasUIOnForearm = bInHasUIOnForearm;
|
|
}
|
|
|
|
bool UVREditorInteractor::HasUIOnForearm() const
|
|
{
|
|
return bHasUIOnForearm;
|
|
}
|
|
|
|
UWidgetComponent* UVREditorInteractor::GetLastHoveredWidgetComponent() const
|
|
{
|
|
return InteractorData.LastHoveredWidgetComponent.Get();
|
|
}
|
|
|
|
void UVREditorInteractor::SetLastHoveredWidgetComponent( UWidgetComponent* NewHoveringOverWidgetComponent )
|
|
{
|
|
InteractorData.LastHoveredWidgetComponent = NewHoveringOverWidgetComponent;
|
|
}
|
|
|
|
bool UVREditorInteractor::IsModifierPressed() const
|
|
{
|
|
return bIsModifierPressed;
|
|
}
|
|
|
|
void UVREditorInteractor::SetIsClickingOnUI( const bool bInIsClickingOnUI )
|
|
{
|
|
bIsClickingOnUI = bInIsClickingOnUI;
|
|
}
|
|
|
|
bool UVREditorInteractor::IsClickingOnUI() const
|
|
{
|
|
return bIsClickingOnUI;
|
|
}
|
|
|
|
void UVREditorInteractor::SetIsHoveringOverUI( const bool bInIsHoveringOverUI )
|
|
{
|
|
bIsHoveringOverUI = bInIsHoveringOverUI;
|
|
}
|
|
|
|
void UVREditorInteractor::SetIsRightClickingOnUI( const bool bInIsRightClickingOnUI )
|
|
{
|
|
bIsRightClickingOnUI = bInIsRightClickingOnUI;
|
|
}
|
|
|
|
bool UVREditorInteractor::IsRightClickingOnUI() const
|
|
{
|
|
return bIsRightClickingOnUI;
|
|
}
|
|
|
|
void UVREditorInteractor::SetLastUIPressTime( const double InLastUIPressTime )
|
|
{
|
|
LastUIPressTime = InLastUIPressTime;
|
|
}
|
|
|
|
double UVREditorInteractor::GetLastUIPressTime() const
|
|
{
|
|
return LastUIPressTime;
|
|
}
|
|
|
|
void UVREditorInteractor::SetUIScrollVelocity( const float InUIScrollVelocity )
|
|
{
|
|
UIScrollVelocity = InUIScrollVelocity;
|
|
}
|
|
|
|
float UVREditorInteractor::GetUIScrollVelocity() const
|
|
{
|
|
return UIScrollVelocity;
|
|
}
|
|
|
|
float UVREditorInteractor::GetSelectAndMoveTriggerValue() const
|
|
{
|
|
return SelectAndMoveTriggerValue;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|