How do I tell UE4 Engine to use my custom viewport class?

Update3:


Special thanks to Nick Penwarden,


I’ve now been able to create my own custom screenshot saving system with my custom gameviewport client.

I’ve posted a tutorial for the community here where I give the entire code I"m using for my auto-filename generating screenshot saving feature.

http://forums.epicgames.com/threads/972861-TUTORIALS-C-for-UE4-gt-gt-New-VIDEO-Entire-Projectile-Class-Code-Accel-amp-Bounces?p=31676249&viewfull=1#post31676249


Update2: Ahhhh

baseengine.ini
[/Script/Engine.Engine]
ConsoleClassName=/Script/Engine.Console
GameViewportClientClassName=/Script/Engine.GameViewportClient

Update: All I need to know now is how to use a custom viewport class with my project :slight_smile: Is that in a config file somewhere?

Dear Friends at Epic,

Hi there!

I am trying to save an image of the viewport to a FColor array to later add it to a save file for “snap shots” of save games.

I am sure the buffer I’m allocating is large enough, as I am only trying to pixels for a small part of the screen (0,0,500,500) compared to buffer size of 1280 x 720

I dont know if Rocket is running DX11 or not, and if that has something to do with the error.

I am calling this functions outside of the Editor, via the right click context menu play game option. (crash occurs if called from PIE as well)

Here is my code:

header file

TArray RV_ColorBuffer;

I have tested that the viewport is being found correctly because the ClientMessage returns correct viewport size (x= 1280 y= 720),
which I then use to allocate the FColor array.

void Thefunction()
{
//~~~ Found Viewport Client? ~~~
	if(!VictoryViewPortClient) return;
	
	//~~~~ Viewport client ~~~	
	if (!VictoryViewport) return;
	
	
	//get viewport size
	FVector2D viewportSize;
	VictoryViewPortClient->GetViewportSize(viewportSize);
	
	//output text
	FString text = "x= ";
	text += FString::FromInt(viewportSize.X);
	text += "y= ";
	text += FString::FromInt(viewportSize.Y);
	ClientMessage(text);
	
	//~~~ Invalid View Port Size ~~~
	if (viewportSize.X <= 0 || viewportSize.Y <= 0) return;
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	//~~~ Empty Color Buffer ~~~
	RV_ColorBuffer.Empty();
	//~~~~~~~~~~~~~~~~~~~~~~~~

	
	
	//~~~ Reallocate Buffer ~~~
		//add uninitialized does not call constructor on the TArray type
		//do not use with  non-simple data types
	RV_ColorBuffer.AddUninitialized( viewportSize.X * viewportSize.Y );
	//~~~~~~~~~~~~~~~~~~~~~~~~


	//Get Screen Shot!
	if (!VictoryViewport->ReadPixels(RV_ColorBuffer, FReadSurfaceDataFlags(), FIntRect(0, 0, 500, 500)))
	{
		ClientMessage("Failed to ReadPixels");
		return;
		//~~~~~~~~~~~~~~~~~~~~~~~~~
	}
}

Here is the error message:

PS: using AddZeroed() instead of uninitialized did not cause any change, and it is definitely just the ReadPixels part that is causing crash, buffer allocates just fine :slight_smile:

PSS: ReadFloat16Pixels() yields same behavior of thread exception, even with properly (as far as I know) pre-initialized buffer being supplied

You could try calling GetViewportScreenShot which is what we use to capture screenshots. The code is very similar to what you have pasted. I will see if we have a crash report here which will provide me with more information.

Please do check out both readpixels and also GetViewportScreenshot

I tried both and I get crashes every single time unfailingly, even if I supply a file name

here’s a screens hot of using GetViewportScreenshot:

PS: again I am sure that VictoryViewport is working correctly as it returns the size of the Viewport exactly correctly, even after resizing

and the buffer itself is initializing fine

it just just readpixels and getViewportScreenShot that are not working friendly in this Rocket build far as I can tell

:heart:

In UE3 I had to pause the Render thread to do this. I hope I’m not breaking a forum rule here but this is how I did it in UE3, it may be related.

	SCOPED_SUSPEND_RENDERING_THREAD(FSuspendRenderingThread::ST_RecreateThread);

	FViewport* Viewport = GEngine->GetAViewport();
	// save content to disk into the screenshots folder
	FIntPoint Extent;

	Extent.X = Viewport->GetSizeX();
	Extent.Y = Viewport->GetSizeY();
	const FSurfaceRHIRef& VisTextureSurface = Viewport->GetRenderTargetSurface();

	if(IsValidRef(VisTextureSurface) && Extent.X && Extent.Y)
	{
		FReadSurfaceDataFlags ReadDataFlags;

		TArray Bitmap;

		Bitmap.Init(Extent.X * Extent.Y);
		

		Viewport->ReadPixels(Bitmap,ReadDataFlags);

Thanks !

I already tried freezing the render thread before taking the screen shot, perhaps I should try using a timer to wait a bit after freezing the render before trying to

Doing so isn’t necessary in UE4. We enqueue a command for the rendering thread to back the surface, write to your array and have the game thread block on the rendering thread. This is perfectly safe to do but will cause a hitch as you are synchronizing the game and rendering threads.

well let me know what you find out about the crash, since it’s happening with both functions, readpixels and the snapshot one :slight_smile:

And do let me know, Friends at Epic, when you figure this out, I cant add my in-game snapshot for save game system until I can get one of these two functions working :slight_smile:

:heart: :heart: :heart: :heart: :heart:

:slight_smile:

Sorry for the delay in getting back to you. I think it is only valid to call ReadPixels on a Viewport while it is drawing, otherwise the render target or backbuffer for the viewport may no longer be valid.

I’d suggest moving the call to Viewport->ReadPixels in to the Draw function in your game’s ViewportClient class.

Thanks Nick!

This sounds quite lovely to me, but I could really use a few pointers :wink: on how to get my game to use my custom view port client

I see the draw function in UnrealClient.h

class FViewportClient
{
public:
	virtual ~FViewportClient(){}
	virtual void Precache() {}
	virtual void RedrawRequested(FViewport* Viewport) { Viewport->Draw(); }
	virtual void RequestInvalidateHitProxy(FViewport* Viewport) { Viewport->InvalidateHitProxy(); }
	virtual void Draw(FViewport* Viewport,FCanvas* Canvas) {}

I see I can override it

but

when I do

How do I tell my game to use my custom FViewportClient as the default viewport client?

Thanks!

:slight_smile:

PS: I will be writing tutorial for community about this, after I get it working, for the goal of taking screen shots to use as save-game snapshots and save that to hard disk

I’d be really interested in reading the tutorial you wrote, unfortunately, it tells me I don’t have permission to acess the page- maybe it’s only acessible to UE3 devs? Is there any way you can change the permissions, or perhaps post it somewhere else (such as the wiki, or new unrealengine forums), if it’s not too much trouble?

Thanks

Hi there, I tried to access your tutorial link at the top but it says I don’t have permission. I would really need to know how to create a custom game viewport client

Not to worry, figured it out. For anyone else that might want to know it’s simple: File->AddCodeToProject then select ‘show all classes’ check box and search for GameViewportClient then add class. To use the class in the editor go to Edit->ProjectSettings->GeneralSettings and select your class from Game Viewport Client drop-down menu

Hello,

This is a question from the beta version of the engine. We are marking this answered for tracking purposes. If you are experiencing an issue similar to this please post a new question.

Thank you.