Exemplo n.º 1
0
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;
}