Multiple active cameras for 1 player

I’m starting a project soon will require different views of the scene to be displayed by different physical monitors, sometimes with asymmetrical split screens per physical monitor (e.g. 3 camera views in one monitor).

I’ve searched around and found some fragmented info regarding how to go about this (e.g. this thread here), which left me wondering if it is at all feasible with my current UE4 API knowledge (i.e. not great ;)).

Though not shy of these type of challenges, I’d like to know if anyone already implemented that and could at least outline the steps/methods they’ve followed so confused me can avoid some pains.

Thanks for your help!

Hi guys, any more news about this? I am also interested to output multiple cameras to create an imersive environment using beamers in a room. If you found a solution to this, I would appreciate any update.

cheers

Sorry guys, I never had the time to tackle this. It seems I’ll go back to Unity to finish the project, unfortunately.

Anyone solved this?

1 Like

Hey guys,
these days i got an early solution for a seperate window, which acts as a launcher for different maps. It shows a mouse-interactive Widget-Blueprint. (I just have guesses how about showing the 3D world with different camera in it.)
accordingly to the engine’s solution, i looked at the Init() function of UGameEngine to make this. with a minimalistic thought, i got a lot of very different exceptions, so this quite stable version looks a bit overcoded/dirty(?).

This functions are part of a UUserWidget derived class, so in the end, it adds itself to the new viewport. but in principle you can use the code in any uclass:


// MainUI.cpp
bool UMainUI::CreateMainUI()
{
	bMainUICreated = false;

	if( GIsClient )
	{
		//Initialize GameInstance of new viewport client
		UPXGameInstance* PXGameInstance = NewObject<UPXGameInstance>( GEngine, UPXGameInstance::StaticClass() );
		PXGameInstance->InitializeMainUIContext( GetWorld()->GetFirstPlayerController() );

		//Initialize Viewport Client
		// MainUI.h: UGameViewportClient* MainUIViewportClient;
		MainUIViewportClient = NewObject<UGameViewportClient>( GEngine, UGameViewportClient::StaticClass() ); 
		MainUIViewportClient->Init( *PXGameInstance->GetWorldContext(), PXGameInstance );

		PXGameInstance->GetWorldContext()->GameViewport = MainUIViewportClient;

		if( MainUIViewportClient )
		{
			//create and register a new Window
			TSharedRef<SWindow> Window = SNew( SWindow )
				.ScreenPosition( FVector2D( 100, 100 ) )
				.ClientSize( FVector2D( 1024, 512 ) )
				.SizingRule( ESizingRule::FixedSize )
				.Title( FText::FromString( TEXT( "Launcher" ) ) )
				.AutoCenter( EAutoCenter::None )
				.FocusWhenFirstShown( true )
				.UseOSWindowBorder( false )
				.CreateTitleBar( true )
				.SupportsTransparency( EWindowTransparency::PerWindow )
				.InitialOpacity( 0.9f )
				.SupportsMaximize( false );

			TSharedPtr<SWindow> TopWindow = FSlateApplication::Get().GetActiveTopLevelWindow();
			if( TopWindow.IsValid() )
			{
				FSlateApplication::Get().AddWindowAsNativeChild( Window, TopWindow.ToSharedRef(), true );
			}
			Window->SetWindowMode( EWindowMode::Windowed );
			Window->SetCursor( EMouseCursor::Default );

			// Attach the viewport client to a new viewport.
			TSharedRef<SOverlay> ViewportOverlayWidgetRef = SNew( SOverlay );
			ViewportOverlayWidgetRef->SetCursor( EMouseCursor::Default );

			TSharedRef<SGameLayerManager> LayerManagerRef = SNew( SGameLayerManager )
				.SceneViewport( MainUIViewportClient->GetGameViewport() )
				.Visibility( EVisibility::Visible )
				.UseScissor( false )
				.Cursor( EMouseCursor::Default )
				
					ViewportOverlayWidgetRef
				];

			TSharedPtr<class SViewport> Viewport = SNew( SViewport )
				.RenderDirectlyToWindow( true )
				.EnableGammaCorrection( false )
				.EnableStereoRendering( true )
				.Cursor( EMouseCursor::Default )
				
					LayerManagerRef
				];

			MainUIViewportClient->SetViewportOverlayWidget( Window, ViewportOverlayWidgetRef );
			MainUIViewportClient->SetGameLayerManager( LayerManagerRef );

			Window->SetOnWindowClosed( FOnWindowClosed::CreateUObject( this, &UMainUI::OnMainUIWindowClosed ) );

			// MainUI.h: TSharedPtr<FSceneViewport> MainUISceneViewport;
			MainUISceneViewport = MakeShareable( new FSceneViewport( MainUIViewportClient, Viewport ) );
			MainUIViewportClient->Viewport = MainUISceneViewport.Get();
			Viewport->SetViewportInterface( MainUISceneViewport.ToSharedRef() );

			FViewportFrame* ViewportFrame = MainUISceneViewport.Get();

			MainUIViewportClient->SetViewportFrame( ViewportFrame );

			Window->SetContent( Viewport.ToSharedRef() );
			Window->ShowWindow();

			this->Cursor = EMouseCursor::Default;
			this->AddToScreen( MainUIViewportClient, 0 );

			bMainUICreated = true;
		}
	}

	return bMainUICreated;
}

void UMainUI::OnMainUIWindowClosed( const TSharedRef<SWindow>& WindowBeingClosed )
{

	MainUIViewportClient->CloseRequested( MainUISceneViewport->GetViewport() );
	MainUISceneViewport.Reset();

	MainUIViewportClient = NULL;
	MainUISceneViewport = NULL;

	UGameEngine* gameEngine = Cast<UGameEngine>( GEngine );
	if( gameEngine )
	{
		if( gameEngine->GameViewportWindow.IsValid() )
		{
			gameEngine->OnGameWindowClosed( gameEngine->GameViewportWindow.Pin().ToSharedRef() );
		}
	}

	bMainUICreated = false;
}

Because the WorldContext is inaccessable, the UGameInstance is inherited and this function overridden:


void UPXGameInstance::InitializeMainUIContext( AController* controller )
{
	WorldContext = &GetEngine()->CreateNewWorldContext( EWorldType::None );
	WorldContext->OwningGameInstance = this;

	// create a MainUI world
	UWorld* MainUIWorld = UWorld::CreateWorld( EWorldType::None, false, "MainUI" );
	MainUIWorld->SetGameInstance( this );
	GEngine->DestroyWorldContext( MainUIWorld ); //the world context is not destroyed inside this function, it gets removed from the Engine's World List (to avoid references to this dummy world)
	WorldContext->SetCurrentWorld( MainUIWorld );
	
	MainUIWorld->AddController( controller );

	Init();
}

In this last function you can see, that a dummy world is created for the new ViewportClient. maybe you can play around with passing the actual game world with a different controller.
Hope this helps.

Nice! Thank you for posting the code too. How do I go about adding this code to my project? Should I create a new class or add it to an existing class?

you should be able to add the first part to a UCLASS of your desire. these are the additional includes:
#include “Engine/GameEngine.h”
#include “SGameLayerManager.h”
#include “Slate/SceneViewport.h”
#include “PXGameInstance.h”
#include “GameViewportClient.h”

while the second part you inherit GameInstance (A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums)

but problems can surely occure since i do not understand the complete engine code :confused:
Meanwhile i investigate on the bottleneck that the complete game scene is rendered two times, which even is a desired behaviour if showing a 3D view from each window, but i didnt’ tested this out.
Play with it, i hope i can help :smiley:

Hi,
Is anybody can make this works?
show another 3D view besides the main playing view?

Hi, I Recreate your code and when i Execute Nothing Show in New Window… New Window is Black…

Im My Map i Create a New Widget MainUI and Add to ViewPort…

How I Send Widget in New Screen ???

You have a Example ?

When I Build to Final Package in Windows I Receive Crash …

https://drive.google.com/open?id=0BxObmjUEdjmiZm9mVmRJS1FaTTA <– MainUIWindow-Project

hey, I managed a empty project supporting a separate window, showing a userwidget. you can start a level, control it via the widget and also get a rendertarget streaming to the separate window.
magic happens in the MainUI.cpp, which is managing the slate/window-stuff, and is also derived from userwidget to be the actual displayed widget.
GameManager.cpp is handling the maingame-window, while MainUIGameInstance.cpp is for interaction between the “game”- and “ui”-levels.
the level blueprint of Base_Level brings it all up.

the project works fine at 4.10.4 and also packages and launches quite well on windows at me. input stucks some times a bit. but be aware, until this is not a supported feature of the engine, the solution may get destroyed by the next engine-update.

I am very sorry, I don’t have a clue about your packaging error. maybe you can check the code against the project I uploaded? upgrading from 4.9 to 4.10 required some code adaption. have you set the PrivateDependencyModuleNames in the .Build.cs -file?

the widget itself is set in MainUI.cpp, CreateMainUIWindow():
[FONT=Courier New]
TSharedPtr<SWidget> UserSlateWidget = TakeWidget();
…]
MainUIViewportClient->AddViewportWidgetContent( ViewportWidget, 10 );

you should be able to pass another widget into the function and replace the TakeWidget()

hope this helps, ask what you need!
greets

Hi Schlabbermampf,

Hi Schlabbermampf,

I’m testing here and found that when I change the parameter values in the new window as the Mouse Click event is initiated the level window enters a state of Pause … nothing happens while I do not finish to modify the parameter in the window HUD … you would know what I can change that when the mouse event occurs in the HUD window the Level not please pause ???

Where I would have to change to instead put a widget inside the window I put a camera somewhere I Level?

I would like to change the parameter of the level object does not enter into a pause state … I think if I put a viewport and then add a HUD interface in the viewport the game not enter into a pause state …

I’m sorry, since i this effect is even appearing in unreal editor (the game is paused during click/drag outside the PIE-viewport), this isn’t fixed easily. referencing to this post, there is a workaround to at least simulate correctly: track the system time during mouse-drag and add it to the next frames delta time. however, this does not unpause the viewport :confused:
i assume it is some engine code, configurating the game windows

do you mean, setting up the second window, to show the actual view of a second camera, just like the game-windows does? not just a widget. at the moment, i have no solution for that, but i think it is doable.
the core code of the window creation is taken from [FONT=Courier New]UGameEngine::Init(IEngineLoop* InEngineLoop) and sub-functions. one will take a look here, where the engine is setting the viewport of the actual game-window, and do this accordingly for the new window. maybe another player-controller has do be spawned and passed.

I understood…

Schlabbermampf, actually I’m trying to create an application within the Unreal Engine … and would need a new window to submit a interface where I could control all objects of Level being loaded … So that part I’ve got making reflect the screen objects by C ++.
What is being harder it is to find a way to extend the windows … I’ve done some tests in multiplayer mode and to work, more the problem is that in multiplayer mode I need to load into memory machine 2 Levels while every window and it would require a lot of processors …

So you can understand, I am creating a software that will act as projection interactive video with synchronized audio … A kind of Resolume http://www.resolume.com with Ableton Live http://www.ableton.com

Take a look at my development within the Unreal … https://www.youtube.com/watch?v=dkF2IwW40gI&list=PL8pfh-hf34lnyPDFjGBJr0nhjMIyIx65k&index=1

Now I wonder if you could or know someone who could make a Free-Lance just to assemble a window with HUD not to pause the game when a control is changed … I’m in Brazil … and could make payment PayPal … I am a student and also would be willing to pay the amount of $ 115 … what do you think?

A lot of work todo, but I will investigate in this from time to time and hope to find a clue. If I do, I let you know!
At best, Epic would add this as a common Engine feature :smiley:

Nice, very nice to know … If you have news of this type of implementation in future is going to help me a lot … Thanks …

i wondered if this can be solved by seperating things with threading…

Hello, where specifically I should create a thread in the code so that the screen is not frozen?

Do you might know how to attach a camera to a certain monitor?

1 Like

@CrashAngel: Sry, with the freeze, i still have no progress :confused: i also hope to get this fixed one day…

@: i worked with this to define the location and size of my windows:
[FONT=Courier New] int32 x, y, w, h = 0;
MyWindow.Pin()->GetNativeWindow()->GetFullScreenInfo( x, y, w, h );

		MyWindowLocation.X = x;
		MyWindowLocation.Y = y;
		MyWindowSize.X = w;
		MyWindowSize.Y = h;

		MyWindow.Pin()-&gt;ReshapeWindow( MyWindowLocation, MyWindowSize );
		FSystemResolution::RequestResolutionChange( MyWindowSize.X, MyWindowSize.Y, EWindowMode::WindowedFullscreen );

i didn’t try but i would try the following: build up your entire screen space from the top left corner: place the first window on (0,0) and size it accordingly to the screeninfo from GetFullScreenInfo(). put the next window on ((previous)WindowSize.X + 1, 0) and scale it by the new info from GetFullScreenInfo() (which gives now the screen info of the second monitor, because the window is placed there). go on like this, until the upper screen row is done and continue with the next screen row.

maybe something like this works xD but be carefull of your frames if rendering that much pixels