TSharedRef<SNotificationList> FSlateNotificationManager::CreateStackForArea(const FSlateRect& InRectangle) { TSharedRef<SNotificationList> NotificationList = SNew(SNotificationList); TSharedRef<SWindow> NotificationWindow = SWindow::MakeNotificationWindow(); NotificationWindow->SetContent(NotificationList); NotificationList->ParentWindowPtr = NotificationWindow; if( RootWindowPtr.IsValid() ) { FSlateApplication::Get().AddWindowAsNativeChild( NotificationWindow, RootWindowPtr.Pin().ToSharedRef() ); } else { FSlateApplication::Get().AddWindow( NotificationWindow ); } if( !FSlateApplication::Get().GetActiveModalWindow().IsValid() ) { if ( NotificationWindow->IsActive() || NotificationWindow->HasActiveParent() ) { NotificationWindow->BringToFront(); } } bool bFound = false; for (FRegionalNotificationList& List : RegionalLists) { if (FSlateRect::IsRectangleContained(List.Region, InRectangle)) { List.Notifications.Add(NotificationList); bFound = true; } } if (!bFound) { FRegionalNotificationList NewList(FSlateApplication::Get().GetWorkArea(InRectangle)); NewList.Notifications.Add(NotificationList); RegionalLists.Add(NewList); } return NotificationList; }
void UVREditorMode::Enter() { bWantsToExitMode = false; ExitType = EVREditorExitType::Normal; { IViewportWorldInteractionManager& ViewportWorldInteraction = IViewportInteractionModule::Get().GetWorldInteractionManager(); ViewportWorldInteraction.OnPreWorldInteractionTick().AddUObject( this, &UVREditorMode::PreTick ); ViewportWorldInteraction.OnPostWorldInteractionTick().AddUObject( this, &UVREditorMode::Tick ); } // @todo vreditor: We need to make sure the user can never switch to orthographic mode, or activate settings that // would disrupt the user's ability to view the VR scene. // @todo vreditor: Don't bother drawing toolbars in VR, or other things that won't matter in VR { const TSharedRef< ILevelEditor >& LevelEditor = FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor").GetFirstLevelEditor().ToSharedRef(); bool bSummonNewWindow = true; // Do we have an active perspective viewport that is valid for VR? If so, go ahead and use that. TSharedPtr<SLevelViewport> ExistingActiveLevelViewport; { TSharedPtr<ILevelViewport> ActiveLevelViewport = LevelEditor->GetActiveViewportInterface(); if(ActiveLevelViewport.IsValid()) { ExistingActiveLevelViewport = StaticCastSharedRef< SLevelViewport >(ActiveLevelViewport->AsWidget()); // Use the currently active window instead bSummonNewWindow = false; } } TSharedPtr< SLevelViewport > VREditorLevelViewport; if(bSummonNewWindow) { // @todo vreditor: The resolution we set here doesn't matter, as HMDs will draw at their native resolution // no matter what. We should probably allow the window to be freely resizable by the user // @todo vreditor: Should save and restore window position and size settings FVector2D WindowSize; { IHeadMountedDisplay::MonitorInfo HMDMonitorInfo; if(bActuallyUsingVR && GEngine->HMDDevice->GetHMDMonitorInfo(HMDMonitorInfo)) { WindowSize = FVector2D(HMDMonitorInfo.ResolutionX, HMDMonitorInfo.ResolutionY); } else { // @todo vreditor: Hard-coded failsafe window size WindowSize = FVector2D(1920.0f, 1080.0f); } } // @todo vreditor: Use SLevelEditor::GetTableTitle() for the VR window title (needs dynamic update) const FText VREditorWindowTitle = NSLOCTEXT("VREditor", "VRWindowTitle", "Unreal Editor VR"); TSharedRef< SWindow > VREditorWindow = SNew(SWindow) .Title(VREditorWindowTitle) .ClientSize(WindowSize) .AutoCenter(EAutoCenter::PreferredWorkArea) .UseOSWindowBorder(true) // @todo vreditor: Allow window to be freely resized? Shouldn't really hurt anything. We should save position/size too. .SizingRule(ESizingRule::UserSized); this->VREditorWindowWeakPtr = VREditorWindow; VREditorLevelViewport = SNew(SLevelViewport) .ViewportType(LVT_Perspective) // Perspective .Realtime(true) // .ParentLayout( AsShared() ) // @todo vreditor: We don't have one and we probably don't need one, right? Make sure a null parent layout is handled properly everywhere. .ParentLevelEditor(LevelEditor) // .ConfigKey( BottomLeftKey ) // @todo vreditor: This is for saving/loading layout. We would need this in order to remember viewport settings like show flags, etc. .IsEnabled(FSlateApplication::Get().GetNormalExecutionAttribute()); // Allow the editor to keep track of this editor viewport. Because it's not inside of a normal tab, // we need to explicitly tell the level editor about it LevelEditor->AddStandaloneLevelViewport(VREditorLevelViewport.ToSharedRef()); VREditorWindow->SetContent(VREditorLevelViewport.ToSharedRef()); // NOTE: We're intentionally not adding this window natively parented to the main frame window, because we don't want it // to minimize/restore when the main frame is minimized/restored FSlateApplication::Get().AddWindow(VREditorWindow); VREditorWindow->SetOnWindowClosed(FOnWindowClosed::CreateUObject(this, &UVREditorMode::OnVREditorWindowClosed)); VREditorWindow->BringToFront(); // @todo vreditor: Not sure if this is needed, especially if we decide the window should be hidden (copied this from PIE code) } else { VREditorLevelViewport = ExistingActiveLevelViewport; if(bActuallyUsingVR) { // Switch to immersive mode const bool bWantImmersive = true; const bool bAllowAnimation = false; ExistingActiveLevelViewport->MakeImmersive(bWantImmersive, bAllowAnimation); } } this->VREditorLevelViewportWeakPtr = VREditorLevelViewport; { FLevelEditorViewportClient& VRViewportClient = VREditorLevelViewport->GetLevelViewportClient(); FEditorViewportClient& VREditorViewportClient = VRViewportClient; // Make sure we are in perspective mode // @todo vreditor: We should never allow ortho switching while in VR SavedEditorState.ViewportType = VREditorViewportClient.GetViewportType(); VREditorViewportClient.SetViewportType(LVT_Perspective); // Set the initial camera location // @todo vreditor: This should instead be calculated using the currently active perspective camera's // location and orientation, compensating for the current HMD offset from the tracking space origin. // Perhaps, we also want to teleport the original viewport's camera back when we exit this mode, too! // @todo vreditor: Should save and restore camera position and any other settings we change (viewport type, pitch locking, etc.) SavedEditorState.ViewLocation = VRViewportClient.GetViewLocation(); SavedEditorState.ViewRotation = VRViewportClient.GetViewRotation(); // Don't allow the tracking space to pitch up or down. People hate that in VR. // @todo vreditor: This doesn't seem to prevent people from pitching the camera with RMB drag SavedEditorState.bLockedPitch = VRViewportClient.GetCameraController()->GetConfig().bLockedPitch; if(bActuallyUsingVR) { VRViewportClient.GetCameraController()->AccessConfig().bLockedPitch = true; } // Set "game mode" to be enabled, to get better performance. Also hit proxies won't work in VR, anyway SavedEditorState.bGameView = VREditorViewportClient.IsInGameView(); VREditorViewportClient.SetGameView(true); SavedEditorState.bRealTime = VREditorViewportClient.IsRealtime(); VREditorViewportClient.SetRealtime(true); SavedEditorState.ShowFlags = VREditorViewportClient.EngineShowFlags; // Never show the traditional Unreal transform widget. It doesn't work in VR because we don't have hit proxies. VREditorViewportClient.EngineShowFlags.SetModeWidgets(false); // Make sure the mode widgets don't come back when users click on things VRViewportClient.bAlwaysShowModeWidgetAfterSelectionChanges = false; // Force tiny near clip plane distance, because user can scale themselves to be very small. // @todo vreditor: Make this automatically change based on WorldToMetersScale? SavedEditorState.NearClipPlane = GNearClippingPlane; GNearClippingPlane = 1.0f; // Normally defaults to 10cm (NOTE: If we go too small, skyboxes become affected) SavedEditorState.bOnScreenMessages = GAreScreenMessagesEnabled; GAreScreenMessagesEnabled = false; // Save the world to meters scale SavedEditorState.WorldToMetersScale = VRViewportClient.GetWorld()->GetWorldSettings()->WorldToMeters; if(bActuallyUsingVR) { SavedEditorState.TrackingOrigin = GEngine->HMDDevice->GetTrackingOrigin(); GEngine->HMDDevice->SetTrackingOrigin(EHMDTrackingOrigin::Floor); } // Make the new viewport the active level editing viewport right away GCurrentLevelEditingViewportClient = &VRViewportClient; // Enable selection outline right away VREditorViewportClient.EngineShowFlags.SetSelection( true ); VREditorViewportClient.EngineShowFlags.SetSelectionOutline( true ); } VREditorLevelViewport->EnableStereoRendering( bActuallyUsingVR ); VREditorLevelViewport->SetRenderDirectlyToWindow( bActuallyUsingVR ); if( bActuallyUsingVR ) { GEngine->HMDDevice->EnableStereo( true ); // @todo vreditor: Force single eye, undistorted mirror for demos const bool bIsVREditorDemo = FParse::Param( FCommandLine::Get(), TEXT( "VREditorDemo" ) ); // @todo vreditor: Remove this when no longer needed (console variable, too!) if( bIsVREditorDemo && GetHMDDeviceType() == EHMDDeviceType::DT_OculusRift ) { // If we're using an Oculus Rift, go ahead and set the mirror mode to a single undistorted eye GEngine->DeferredCommands.Add( FString::Printf( TEXT( "HMD MIRROR MODE %i" ), VREd::ForceOculusMirrorMode->GetInt() ) ); } } if( bActuallyUsingVR ) { // Tell Slate to require a larger pixel distance threshold before the drag starts. This is important for things // like Content Browser drag and drop. SavedEditorState.DragTriggerDistance = FSlateApplication::Get().GetDragTriggerDistance(); FSlateApplication::Get().SetDragTriggerDistance( 100.0f ); // @todo vreditor tweak } } // Setup sub systems { // Setup world interaction TSharedPtr<FEditorViewportClient> ViewportClient = VREditorLevelViewportWeakPtr.Pin()->GetViewportClient(); WorldInteraction->SetViewport( ViewportClient ); WorldInteraction->Activate( true ); WorldInteraction->OnHandleKeyInput().AddUObject( this, &UVREditorMode::InputKey ); // Motion controllers if(bActuallyUsingVR) { LeftHandInteractor = NewObject<UVREditorMotionControllerInteractor>( WorldInteraction ); LeftHandInteractor->SetControllerHandSide( EControllerHand::Left ); LeftHandInteractor->Init( this ); WorldInteraction->AddInteractor( LeftHandInteractor ); RightHandInteractor = NewObject<UVREditorMotionControllerInteractor>( WorldInteraction ); RightHandInteractor->SetControllerHandSide( EControllerHand::Right ); RightHandInteractor->Init( this ); WorldInteraction->AddInteractor( RightHandInteractor ); WorldInteraction->PairInteractors( LeftHandInteractor, RightHandInteractor ); } else { // Register an interactor for the mouse cursor MouseCursorInteractor = NewObject<UMouseCursorInteractor>( WorldInteraction ); MouseCursorInteractor->Init(); WorldInteraction->AddInteractor( MouseCursorInteractor ); } // Setup the UI system UISystem = NewObject<UVREditorUISystem>(); UISystem->SetOwner( this ); UISystem->Init(); VRWorldInteractionExtension = NewObject<UVREditorWorldInteraction>(); VRWorldInteractionExtension->Init( this, WorldInteraction ); // Setup teleporter TeleporterSystem = NewObject<UVREditorTeleporter>(); TeleporterSystem->Init( this ); // Setup autoscaler AutoScalerSystem = NewObject<UVREditorAutoScaler>(); AutoScalerSystem->Init( this ); } if(AvatarActor == nullptr) { SpawnAvatarMeshActor(); } bFirstTick = true; bIsActive = true; }