UE4 : My Engine/C++ modifications

I did several changes in the UE4 engine, mostly personal changes. Some choices made by Epic doesn’t always fit my needs. I believe some people would also be interested those. Also this post will be useful for me too to track all my modifications. 🙂

Comments are open if you have questions !

I will keep this post updated when I will change my version or that I edit some other parts of the engine code.
Currently the code below was based on : UE4 4.14.0.

Summary :
1 – Disabling Unreal Launcher call when opening the editor
2 – Lock the editor to 60 FPS (slate included)
3 – Disable fill of PNG file during import
4 – Disable fade in/out of notifications
5 – Allow to animate skeletal meshes even when engine is paused
6 – Remove Fraps startup message
7 – Keep Temporal Anti-aliasing (TAA) updated during a pause
8 – Disabling tooltip overlays

1# Disabling Unreal Launcher call when opening the editor

This one can be annoying, especially when you open a project from the launcher and while the editor is loading you close the launcher, the editor will relaunch it.
So how to disable it ? Simply comment the call to the launcher in EditorEngine.cpp in the top of the function UEditorEngine::InitEditor(), it should look like this once disabled :

 

void UEditorEngine::InitEditor(IEngineLoop* InEngineLoop)
{
	// Call base.
	UEngine::Init(InEngineLoop);
	
    ////////////////////////////////////////////////////
    // FROYOK : Disable launcher call
    ////////////////////////////////////////////////////
	/*
	// Specify "-ForceLauncher" on the command-line to always open the launcher, even in unusual cases.  This is useful for debugging the Launcher startup.
	const bool bForceLauncherToOpen = FParse::Param(FCommandLine::Get(), TEXT("ForceLauncher"));

	if ( bForceLauncherToOpen ||
		( !FEngineBuildSettings::IsInternalBuild() &&
		!FEngineBuildSettings::IsPerforceBuild() &&
		!FPlatformMisc::IsDebuggerPresent() &&	// Don't spawn launcher while running in the Visual Studio debugger by default
		!FApp::IsBenchmarking() &&
		!GIsDemoMode &&
		!IsRunningCommandlet() &&
		!FPlatformProcess::IsApplicationRunning(TEXT("EpicGamesLauncher")) &&
		!FPlatformProcess::IsApplicationRunning(TEXT("EpicGamesLauncher-Mac-Shipping"))
		))
	{
		IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
		if ( DesktopPlatform != NULL )
		{
			FOpenLauncherOptions SilentOpen;
			DesktopPlatform->OpenLauncher(SilentOpen);
		}
	}
	*/

	// Create selection sets.
	PrivateInitSelectedSets();

    [...]

}

2# – Lock the editor to 60 FPS (slate included)

In UE4 you can already lock the FPS of the game and viewport to a desired framerate, which is convenient, however this limitation doesn’t apply to Slate. Remember that slate while still an interface is entirely rendered on the GPU, which can be very heavy especially if you need to run additional software on the side. I noticed that at least since UE4 4.5.0, when the editor doesn’t have the focus anymore, the engine is locked at 3/6 FPS (you can see at which speed the UI perform when you have DxTory or Fraps running). From that point I easily found the function that was doing that.

So what I did is that I modified a bit the original function to be sure that in the context of the editor the maximum tick rate will be 60. The function that define this value can be find in EditorEngine.cpp, it is called UEditorEngine::GetMaxTickRate. Here is what my code looks like :

float UEditorEngine::GetMaxTickRate( float DeltaTime, bool bAllowFrameRateSmoothing ) const
{
	float MaxTickRate = 0.0f;
	if( !ShouldThrottleCPUUsage() )
	{
		///////////////////////////////////////////
		// Froyok - Edit max tick rate to 60 max in Editor (and not in VR preview)
		//-----------------------------------------
		if( GIsEditor && !bUseVRPreviewForPlayWorld )
		{
			MaxTickRate = 60.0f;
			return MaxTickRate;
		}
		else
		{
			// do not limit fps in VR Preview mode
			if (bUseVRPreviewForPlayWorld)
			{
				return 0.0f;
			}
			const float SuperMaxTickRate = Super::GetMaxTickRate( DeltaTime, bAllowFrameRateSmoothing );
			if( SuperMaxTickRate != 0.0f )
			{
				return SuperMaxTickRate;
			}

			// Clamp editor frame rate, even if smoothing is disabled
			if( !bSmoothFrameRate && GIsEditor && !GIsPlayInEditorWorld )
			{
				MaxTickRate = 1.0f / DeltaTime;
				if (SmoothedFrameRateRange.HasLowerBound())
				{
					MaxTickRate = FMath::Max(MaxTickRate, SmoothedFrameRateRange.GetLowerBoundValue());
				}
				if (SmoothedFrameRateRange.HasUpperBound())
				{
					MaxTickRate = FMath::Min(MaxTickRate, SmoothedFrameRateRange.GetUpperBoundValue());
				}
			}

			// Laptops should throttle to 60 hz in editor to reduce battery drain
			static const auto CVarDontLimitOnBattery = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.DontLimitOnBattery"));
			const bool bLimitOnBattery = (FPlatformMisc::IsRunningOnBattery() && CVarDontLimitOnBattery->GetValueOnGameThread() == 0);
			if( bLimitOnBattery )
			{
				MaxTickRate = 60.0f;
			}
		}
	}
	else
	{
		MaxTickRate = 3.0f;
	}

	return MaxTickRate;
}


3# – Disable the fill of PNG file during import

When you import PNG files in UE4 and if they have black transparency (so fully transparent), the pixels will be modified. Basically what the import process do is that is remove the pixel and replace it with some stretching/padding. My guess is that Epic did that for foliage textures which rely on very clean borders near the transparency parts (mostly because of how mipmaps behave). Sadly this setting can’t be toggled and everybody has to live with it. I preferred to disable this behavior as I’m exporting my textures in PNG (much lighter than TGA and it’s a lossless format).

Fortunately the change is very easy to do, just go to EditorFactories.cpp then inside UTextureFactory::ImportTexture() and comment the call of the function named FillZeroAlphaPNGData(). It should look like this :

UTexture* UTextureFactory::ImportTexture(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn)
{

        [...]

		UTexture2D* Texture = CreateTexture2D( InParent, Name, Flags );
		if ( Texture )
		{
			Texture->Source.Init(
				PngImageWrapper->GetWidth(),
				PngImageWrapper->GetHeight(),
				/*NumSlices=*/ 1,
				/*NumMips=*/ 1,
				TextureFormat
				);
			Texture->SRGB = true;
			const TArray<uint8>* RawPNG = nullptr;
			if ( PngImageWrapper->GetRaw( Format, BitDepth, RawPNG ) )
			{
				uint8* MipData = Texture->Source.LockMip(0);
				FMemory::Memcpy( MipData, RawPNG->GetTypedData(), RawPNG->Num() );

				// Replace the pixels with 0.0 alpha with a color value from the nearest neighboring color which has a non-zero alpha

				////////////////////////////////////////////////////
				// Froyok - Disable PNG fill
				//--------------------------------------------------
				// FillZeroAlphaPNGData( Texture->Source, MipData );
			}

        [...]

}


4# – Disable fade in/out of notifications

The UE4 Editor can sometimes display by overlay some notifications, such as “Building lighting : xx%” and so on. The problem is that those notifications divide by 2 the framerate of Editor on my computer. So running the Editor at 60 FPS will result in being at 30 FPS while a notification is displayed. No need to say how annoying this when you have the feeling the application suddenly freeze/lag. My theory is that the way those notifications are displayed is that they are drawn on top of the buffer used by Slate, resulting in a huge overdraw for the GPU. The only bug I found so far with this change is that the message “imported texture has been converted to normal map” will stop to show up since this one only fade when called (but I can live with that).

Note : tooltips also have this problem, check out point #8 to see how to disable them.

So while I wanted to keep most of those notifications, I also wanted to remove the fade in/out because :
– It reduce the time the notification appears on screen, therefore the FPS drop last less
– Those fades don’t really have a purpose, in my opinion the notifications are often too big and hide the screen and seeing them fading feels like a waste of time.

So to disable them I just used an ugly trick : I override the time used to fade the notification (which can change depending of the message) and set it to zero ! The change can be done in the file SNotificationList.cpp via the class SNotificationExtendable. In this class I modified 3 functions like this (most of the time I just set the fade duration, which is the second input of the function AddCurve() ) :

	virtual void ExpireAndFadeout() override
	{
		// FROYOK : Disable fade in/out of pop ups to avoid performances drops
		FadeAnimation = FCurveSequence();
		// Add some space for the expire time
		FadeAnimation.AddCurve(0.0f, 0.0f);
		// Add the actual fade curve
		FadeCurve = FadeAnimation.AddCurve(0.f, 0.0f);
		FadeAnimation.PlayReverse();
	}

        /** Begins the fadein of this message */
	void Fadein( const bool bAllowThrottleWhenFrameRateIsLow )
	{
		// Make visible
		SetVisibility(EVisibility::Visible);

		// FROYOK : Disable fade in/out of pop ups to avoid performances drops
		// Play Fadein animation
		FadeAnimation = FCurveSequence();
		FadeCurve = FadeAnimation.AddCurve(0.f, 0.0f);
		FadeAnimation.Play();

		// Scale up/flash animation
		IntroAnimation = FCurveSequence();
		ScaleCurveX = IntroAnimation.AddCurve(0.2f, 0.0f, ECurveEaseFunction::QuadOut);
		ScaleCurveY = IntroAnimation.AddCurve(0.f, 0.0f);
		GlowCurve = IntroAnimation.AddCurve(0.5f, 0.55f, ECurveEaseFunction::QuadOut);
		IntroAnimation.Play();

		/*
		// When a fade in occurs, we need a high framerate for the animation to look good
		if( FadeInDuration.Get() > KINDA_SMALL_NUMBER && bAllowThrottleWhenFrameRateIsLow && !ThrottleHandle.IsValid() )
		{
			if( !FSlateApplication::Get().IsRunningAtTargetFrameRate() )
			{
				ThrottleHandle = FSlateThrottleManager::Get().EnterResponsiveMode();
			}
		}
		*/
	}

	/** Begins the fadeout of this message */
	virtual void Fadeout() override
	{
		// FROYOK : Disable fade in/out of pop ups to avoid performances drops
		// Start fade animation
		FadeAnimation = FCurveSequence();
		FadeCurve = FadeAnimation.AddCurve(0.f, 0.0f);
		FadeAnimation.PlayReverse();
	}

5# – Allow to animate skeletal meshes even when engine is paused

In UE4 it is possible to keep some actors ticking while the engine is paused. This is very useful for materials of any logic (like a custom menu). However by default, skeletal meshes can’t be set to stay animated even if the game is paused. This can be changed by editing the function “ShouldTickPose()” in “SkeletalMeshComponent.cpp

bool USkeletalMeshComponent::ShouldTickPose() const
{
	// When we stop root motion we go back to ticking after CharacterMovement. Unfortunately that means that we could tick twice that frame.
	// So only enforce a single tick per frame.

	//Froyok - Allow to unpause skeletal mesh
	const bool bAlreadyTickedThisFrame = PoseTickedThisFrame() && !(PrimaryComponentTick.bTickEvenWhenPaused && GetWorld()->IsPaused()); 
	
	
	return (Super::ShouldTickPose() && IsRegistered() && AnimScriptInstance && !bAutonomousTickPose && !bPauseAnims && GetWorld()->AreActorsInitialized() && !bNoSkeletonUpdate && !bAlreadyTickedThisFrame);
}

6# – Remove Fraps startup message

I don’t recommend this operation, especially for shipping a game. If the message is here, it’s for a good reason. However when developping, I found it very annyoing because first it stops the engine to start until to press “ok” in the dialog (either in game or editor mode) and second it will lose the focus, so the mouse won’t be stolen by the game. When doing quick iterations it’s therefore slowing me down. Also, as long I don’t capture videos, Fraps works fine with the engine and never made the editor crash in my case.

It’s really easy to disable the check for Fraps, just find the code dedicated to it and comment it. The code can be found in WindowsDynamicRHI.cpp. This should look something like this :

	else if (bForceD3D12)
	{
		DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("D3D12RHI"));

		if (!DynamicRHIModule->IsSupported())
		{
			FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("WindowsDynamicRHI", "RequiredDX12", "DX12 is not supported on your system. Try running without the -dx12 or -d3d12 command line argument."));
			FPlatformMisc::RequestExit(1);
			DynamicRHIModule = NULL;
		}
		//Froyok - Disable Fraps
		/*
		else if (FPlatformProcess::IsApplicationRunning(TEXT("fraps.exe")))
		{
			FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("WindowsDynamicRHI", "UseExpressionEncoder", "Fraps has been known to crash D3D12. Please use Microsoft Expression Encoder instead for capturing."));
		}
		*/
	}
	else
	{
		DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("D3D11RHI"));

		if (!DynamicRHIModule->IsSupported())
		{
			FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("WindowsDynamicRHI", "RequiredDX11Feature", "DX11 feature level 10.0 is required to run the engine."));
			FPlatformMisc::RequestExit(1);
			DynamicRHIModule = NULL;
		}
		//Froyok - Disable Fraps
		/*
		else if (FPlatformProcess::IsApplicationRunning(TEXT("fraps.exe")))
		{
			FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("WindowsDynamicRHI", "UseExpressionEncoderDX11", "Fraps has been known to crash D3D11. Please use Microsoft Expression Encoder instead for capturing."));
		}
		*/
	}

#7 – Keep Temporal Anti-aliasing (TAA) updated during a pause

By default when the player press “pause” the game logic will freeze. This is often done to display an intermediate menu screen for example. However, while the game logic is frozen, the rendering of the engine still operate while some features are not updated to maintain the illusion. This is the case for the Temporal Anti-Aliasing and the Motion Blur. In some situation, being able to move during a pause could be very handy for specific gameplay situations. For example to create an intermediate pause during a top-down game, giving the time for the player to plan decisions. This can be done by enabling the boolean “bTickEvenWhenPaused” of the necessary actors, like the player itself or its camera.

Unfortunately, the engine consider that during a pause, the camera of the player will not move in order to keep TAA and Motion Blur as they were. This means that if you move the camera during the pause, the motion will start to get off (because the motion vector are not updated anymore) and the TAA will introduce artifacts in the image.

This can be solved by telling to the engine that even during a pause, the camera is still updated by the player. This can be done with one line of code in the BeginPlay of any actors in the scene :

void AExedrePlayerController::BeginPlay()
{
	Super::BeginPlay();

	if( GetWorld() != NULL )
		GetWorld()->bIsCameraMoveableWhenPaused = true;
}

The side effect of setting this boolean to true, is that the Motion Blur effect will “stop” (as the motion vector will stay updated, but the moving objects will freeze in place).

8 – Disabling tooltip overlays

As mentioned in the fade in/out notification (see #4), it is also possible to disable the tooltips. This can be easily done by disablign the creation of the tooltips in the Slate class. Just look for SToolTip::Construct() and set everything as comment. The function can be found in “Slate\Private\Widgets\SToolTip.cpp”.

void SToolTip::Construct( const FArguments& InArgs )
{
	// Froyok - Disable tooltips
	/*
	TextContent = InArgs._Text;
	bIsInteractive = InArgs._IsInteractive;
	Font = InArgs._Font;
	ColorAndOpacity = InArgs._ColorAndOpacity;
	TextMargin = InArgs._TextMargin;
	BorderImage = InArgs._BorderImage;

	SetContentWidget(InArgs._Content.Widget);
	*/
}

This little part of code will disable tooltip mostly everywhere (sliders, material editor, etc.) but not in the content browser however.
An extra part of code as to be commented to disable the mouse overlay happening in the content browser. Go into the class file “Editor\ContentBrowser\Private\AssetViewWidgets.cpp” and find the SAssetViewItem::Construct() function. In this function look for the line that create the tooltip and simply comment it :

void SAssetViewItem::Construct( const FArguments& InArgs )
{
	[...]
	
	//Froyok - Disable content browser tooltip
	// Set our tooltip - this will refresh each time it's opened to make sure it's up-to-date
	//SetToolTip(SNew(SAssetViewItemToolTip).AssetViewItem(SharedThis(this)));
	
	[...]
}