Is it possible render Gizmo in Runtime Game?

I tried to Inherits UGameViewport and rewrite UGameViewport::Draw method and replace GameViewDrawer with my own custom one.



class FGameGizmoViewDrawer : public FViewElementDrawer
{
public:
	/**
	* Draws debug info using the given draw interface.
	*/
	virtual void Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI)
	{
		UMaterial* AxisMaterialBase = GEngine->ArrowMaterial;

		UMaterialInstanceDynamic* AxisMaterialX = UMaterialInstanceDynamic::Create(AxisMaterialBase, NULL);

		FTransform Org = FTransform::Identity;
		Org.SetLocation(FVector(-351, -99, 236));

		DrawCylinder(PDI, Org.ToMatrixWithScale(), FVector(0, 0, 30), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), 30, 100, 16, AxisMaterialX->GetRenderProxy(false), SDPG_Foreground);
	}
};


I copied some codes from Editor codes. The rendering cylinder is still not the top most.
But the Cylinder still occuled by scene objects.
I wonder how the editor achieve the gizmo on the top most.
dba20c5eb35efe8c92f4569d91b6c10fbbffaad1.jpeg



// Fill out your copyright notice in the Description page of Project Settings.

#include "FPSCpp.h"
#include "VRGameViewportClient.h"
#include "BufferVisualizationData.h"
#include "EngineModule.h"
#include "EngineStats.h"
#include "SceneViewExtension.h"
#include "AudioDevice.h"
#include "IHeadMountedDisplay.h"
#include "Engine/Console.h"
#include "ContentStreaming.h"
#include "FXSystem.h"
#include "SubtitleManager.h"
#include "HitProxies.h"

DECLARE_CYCLE_STAT(TEXT("UI Drawing Time"), STAT_UIDrawingTime, STATGROUP_UI);

/** Util to find named canvas in transient package, and create if not found */
static UCanvas* GetCanvasByName(FName CanvasName)
{
	// Cache to avoid FString/FName conversions/compares
	static TMap<FName, UCanvas*> CanvasMap;
	UCanvas** FoundCanvas = CanvasMap.Find(CanvasName);
	if (!FoundCanvas)
	{
		UCanvas* CanvasObject = FindObject<UCanvas>(GetTransientPackage(), *CanvasName.ToString());
		if (!CanvasObject)
		{
			CanvasObject = NewObject<UCanvas>(GetTransientPackage(), CanvasName);
			CanvasObject->AddToRoot();
		}

		CanvasMap.Add(CanvasName, CanvasObject);
		return CanvasObject;
	}

	return *FoundCanvas;
}

struct HGameWidgetAxis : public HHitProxy
{
	DECLARE_HIT_PROXY(FPSCPP_API);

	EAxisList::Type Axis;
	uint32 bDisabled : 1;

	HGameWidgetAxis(EAxisList::Type InAxis, bool InbDisabled = false) :
		HHitProxy(HPP_UI),
		Axis(InAxis),
		bDisabled(InbDisabled) {}

	virtual EMouseCursor::Type GetMouseCursor() override
	{
		if (bDisabled)
		{
			return EMouseCursor::SlashedCircle;
		}
		return EMouseCursor::CardinalCross;
	}

	/**
	* Method that specifies whether the hit proxy *always* allows translucent primitives to be associated with it or not,
	* regardless of any other engine/editor setting. For example, if translucent selection was disabled, any hit proxies
	*  returning true would still allow translucent selection. In this specific case, true is always returned because geometry
	* mode hit proxies always need to be selectable or geometry mode will not function correctly.
	*
	* @return	true if translucent primitives are always allowed with this hit proxy; false otherwise
	*/
	virtual bool AlwaysAllowsTranslucentPrimitives() const override
	{
		return true;
	}
};

IMPLEMENT_HIT_PROXY(HGameWidgetAxis, HHitProxy);

/**
* Draw debug info on a game scene view.
*/
class FGameGizmoViewDrawer : public FViewElementDrawer
{
public:
	/**
	* Draws debug info using the given draw interface.
	*/
	virtual void Draw(const FSceneView* View, FPrimitiveDrawInterface* PDI)
	{
		UMaterial* AxisMaterialBase = GEngine->ArrowMaterial;

		UMaterialInstanceDynamic* AxisMaterialX = UMaterialInstanceDynamic::Create(AxisMaterialBase, NULL);

		FTransform Org = FTransform::Identity;
		Org.SetLocation(FVector(-351, -99, 236));
		//DrawWireSphere(PDI, Org, FColor(255, 130, 0), 100, 32, SDPG_Foreground);

		//DrawSphere(PDI, Org.GetLocation(), FVector(100, 100, 100), 32, 32, AxisMaterialX->GetRenderProxy(false), SDPG_Foreground);

		PDI->SetHitProxy(new HGameWidgetAxis(EAxisList::Z, false));

		DrawCylinder(PDI, Org.ToMatrixWithScale(), FVector(0, 0, 30), FVector(1, 0, 0), FVector(0, 1, 0), FVector(0, 0, 1), 30, 100, 16, AxisMaterialX->GetRenderProxy(false), SDPG_Foreground);

		PDI->SetHitProxy(nullptr);
	}
};

void UVRGameViewportClient::Draw(FViewport* InViewport, FCanvas* SceneCanvas)
{
	//Valid SceneCanvas is required.  Make this explicit.
	check(SceneCanvas);

	OnBeginDraw().Broadcast();

	FCanvas* DebugCanvas = InViewport->GetDebugCanvas();

	// Create a temporary canvas if there isn't already one.
	static FName CanvasObjectName(TEXT("CanvasObject"));
	UCanvas* CanvasObject = GetCanvasByName(CanvasObjectName);
	CanvasObject->Canvas = SceneCanvas;

	// Create temp debug canvas object
	static FName DebugCanvasObjectName(TEXT("DebugCanvasObject"));
	UCanvas* DebugCanvasObject = GetCanvasByName(DebugCanvasObjectName);
	DebugCanvasObject->Canvas = DebugCanvas;
	DebugCanvasObject->Init(InViewport->GetSizeXY().X, InViewport->GetSizeXY().Y, NULL);

	const bool bStereoRendering = GEngine->HMDDevice.IsValid() && GEngine->IsStereoscopic3D(InViewport);
	if (DebugCanvas)
	{
		DebugCanvas->SetScaledToRenderTarget(bStereoRendering);
		DebugCanvas->SetStereoRendering(bStereoRendering);
	}
	if (SceneCanvas)
	{
		SceneCanvas->SetScaledToRenderTarget(bStereoRendering);
		SceneCanvas->SetStereoRendering(bStereoRendering);
	}

	bool bUIDisableWorldRendering = false;
	FGameGizmoViewDrawer GameViewDrawer;

	// create the view family for rendering the world scene to the viewport's render target
	FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
		InViewport,
		GetWorld()->Scene,
		EngineShowFlags)
		.SetRealtimeUpdate(true));

	// Allow HMD to modify the view later, just before rendering
	if (GEngine->HMDDevice.IsValid() && GEngine->IsStereoscopic3D(InViewport))
	{
		auto HmdViewExt = GEngine->HMDDevice->GetViewExtension();
		if (HmdViewExt.IsValid())
		{
			ViewFamily.ViewExtensions.Add(HmdViewExt);
			HmdViewExt->SetupViewFamily(ViewFamily);
		}
	}

	if (bStereoRendering)
	{
		// Allow HMD to modify screen settings
		GEngine->HMDDevice->UpdateScreenSettings(Viewport);
	}

	ESplitScreenType::Type SplitScreenConfig = GetCurrentSplitscreenConfiguration();
	EngineShowFlagOverride(ESFIM_Game, (EViewModeIndex)ViewModeIndex, ViewFamily.EngineShowFlags, NAME_None, SplitScreenConfig != ESplitScreenType::None);

	if (ViewFamily.EngineShowFlags.VisualizeBuffer && AllowDebugViewmodes())
	{
		// Process the buffer visualization console command
		FName NewBufferVisualizationMode = NAME_None;
		static IConsoleVariable* ICVar = IConsoleManager::Get().FindConsoleVariable(FBufferVisualizationData::GetVisualizationTargetConsoleCommandName());
		if (ICVar)
		{
			static const FName OverviewName = TEXT("Overview");
			FString ModeNameString = ICVar->GetString();
			FName ModeName = *ModeNameString;
			if (ModeNameString.IsEmpty() || ModeName == OverviewName || ModeName == NAME_None)
			{
				NewBufferVisualizationMode = NAME_None;
			}
			else
			{
				if (GetBufferVisualizationData().GetMaterial(ModeName) == NULL)
				{
					// Mode is out of range, so display a message to the user, and reset the mode back to the previous valid one
					UE_LOG(LogConsoleResponse, Warning, TEXT("Buffer visualization mode '%s' does not exist"), *ModeNameString);
					//NewBufferVisualizationMode = CurrentBufferVisualizationMode;
					// todo: cvars are user settings, here the cvar state is used to avoid log spam and to auto correct for the user (likely not what the user wants)
					ICVar->Set(*NewBufferVisualizationMode.GetPlainNameString(), ECVF_SetByCode);
				}
				else
				{
					NewBufferVisualizationMode = ModeName;
				}
			}
		}

		//if (NewBufferVisualizationMode != CurrentBufferVisualizationMode)
		{
			//CurrentBufferVisualizationMode = NewBufferVisualizationMode;
		}
	}

	TMap<ULocalPlayer*, FSceneView*> PlayerViewMap;

	FAudioDevice* AudioDevice = GetWorld()->GetAudioDevice();

	bool bReverbSettingsFound = false;
	FReverbSettings ReverbSettings;
	class AAudioVolume* AudioVolume = nullptr;

	for (FLocalPlayerIterator Iterator(GEngine, GetWorld()); Iterator; ++Iterator)
	{
		ULocalPlayer* LocalPlayer = *Iterator;
		if (LocalPlayer)
		{
			APlayerController* PlayerController = LocalPlayer->PlayerController;

			const bool bEnableStereo = GEngine->IsStereoscopic3D(InViewport);
			int32 NumViews = bEnableStereo ? 2 : 1;

			for (int i = 0; i < NumViews; ++i)
			{
				// Calculate the player's view information.
				FVector		ViewLocation;
				FRotator	ViewRotation;

				EStereoscopicPass PassType = !bEnableStereo ? eSSP_FULL : ((i == 0) ? eSSP_LEFT_EYE : eSSP_RIGHT_EYE);

				FSceneView* View = LocalPlayer->CalcSceneView(&ViewFamily, ViewLocation, ViewRotation, InViewport, &GameViewDrawer, PassType);

				if (View)
				{
					if (View->Family->EngineShowFlags.Wireframe)
					{
						// Wireframe color is emissive-only, and mesh-modifying materials do not use material substitution, hence...
						View->DiffuseOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f);
						View->SpecularOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f);
					}
					else if (View->Family->EngineShowFlags.OverrideDiffuseAndSpecular)
					{
						View->DiffuseOverrideParameter = FVector4(GEngine->LightingOnlyBrightness.R, GEngine->LightingOnlyBrightness.G, GEngine->LightingOnlyBrightness.B, 0.0f);
						View->SpecularOverrideParameter = FVector4(.1f, .1f, .1f, 0.0f);
					}
					else if (View->Family->EngineShowFlags.ReflectionOverride)
					{
						View->DiffuseOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f);
						View->SpecularOverrideParameter = FVector4(1, 1, 1, 0.0f);
						View->NormalOverrideParameter = FVector4(0, 0, 1, 0.0f);
						View->RoughnessOverrideParameter = FVector2D(0.0f, 0.0f);
					}

					if (!View->Family->EngineShowFlags.Diffuse)
					{
						View->DiffuseOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f);
					}

					if (!View->Family->EngineShowFlags.Specular)
					{
						View->SpecularOverrideParameter = FVector4(0.f, 0.f, 0.f, 0.f);
					}

					View->CurrentBufferVisualizationMode = NAME_None;

					View->CameraConstrainedViewRect = View->UnscaledViewRect;

					// If this is the primary drawing pass, update things that depend on the view location
					if (i == 0)
					{
						// Save the location of the view.
						LocalPlayer->LastViewLocation = ViewLocation;

						PlayerViewMap.Add(LocalPlayer, View);

						// Update the listener.
						if (AudioDevice != NULL && PlayerController != NULL)
						{
							bool bUpdateListenerPosition = true;

							// If the main audio device is used for multiple PIE viewport clients, we only
							// want to update the main audio device listener position if it is in focus
							if (GEngine)
							{
								FAudioDeviceManager* AudioDeviceManager = GEngine->GetAudioDeviceManager();

								// If there is more than one world referencing the main audio device
								if (AudioDeviceManager->GetNumMainAudioDeviceWorlds() > 1)
								{
									uint32 MainAudioDeviceHandle = GEngine->GetAudioDeviceHandle();
									if (AudioDevice->DeviceHandle == MainAudioDeviceHandle /*&& !bHasAudioFocus*/)
									{
										bUpdateListenerPosition = false;
									}
								}
							}

							if (bUpdateListenerPosition)
							{
								FVector Location;
								FVector ProjFront;
								FVector ProjRight;
								PlayerController->GetAudioListenerPosition(/*out*/ Location, /*out*/ ProjFront, /*out*/ ProjRight);

								FTransform ListenerTransform(FRotationMatrix::MakeFromXY(ProjFront, ProjRight));
								ListenerTransform.SetTranslation(Location);
								ListenerTransform.NormalizeRotation();

								bReverbSettingsFound = true;

								FReverbSettings PlayerReverbSettings;
								FInteriorSettings PlayerInteriorSettings;
								class AAudioVolume* PlayerAudioVolume = GetWorld()->GetAudioSettings(Location, &PlayerReverbSettings, &PlayerInteriorSettings);

								if (AudioVolume == nullptr || (PlayerAudioVolume != nullptr && PlayerAudioVolume->Priority > AudioVolume->Priority))
								{
									AudioVolume = PlayerAudioVolume;
									ReverbSettings = PlayerReverbSettings;
								}

								uint32 ViewportIndex = PlayerViewMap.Num() - 1;
								AudioDevice->SetListener(ViewportIndex, ListenerTransform, (View->bCameraCut ? 0.f : GetWorld()->GetDeltaSeconds()), PlayerAudioVolume, PlayerInteriorSettings);
							}
						}
					}

					// Add view information for resource streaming.
					IStreamingManager::Get().AddViewInformation(View->ViewMatrices.ViewOrigin, View->ViewRect.Width(), View->ViewRect.Width() * View->ViewMatrices.ProjMatrix.M[0][0]);
					GetWorld()->ViewLocationsRenderedLastFrame.Add(View->ViewMatrices.ViewOrigin);
				}
			}
		}
	}

	if (bReverbSettingsFound)
	{
		AudioDevice->SetReverbSettings(AudioVolume, ReverbSettings);
	}

	// Update level streaming.
	GetWorld()->UpdateLevelStreaming();

	// Draw the player views.
	if (!bDisableWorldRendering && !bUIDisableWorldRendering && PlayerViewMap.Num() > 0) //-V560
	{
		GetRendererModule().BeginRenderingViewFamily(SceneCanvas, &ViewFamily);
	}

	// Clear areas of the rendertarget (backbuffer) that aren't drawn over by the views.
	{
		// Find largest rectangle bounded by all rendered views.
		uint32 MinX = InViewport->GetSizeXY().X, MinY = InViewport->GetSizeXY().Y, MaxX = 0, MaxY = 0;
		uint32 TotalArea = 0;
		for (int32 ViewIndex = 0; ViewIndex < ViewFamily.Views.Num(); ++ViewIndex)
		{
			const FSceneView* View = ViewFamily.Views[ViewIndex];

			FIntRect UpscaledViewRect = View->UnscaledViewRect;

			MinX = FMath::Min<uint32>(UpscaledViewRect.Min.X, MinX);
			MinY = FMath::Min<uint32>(UpscaledViewRect.Min.Y, MinY);
			MaxX = FMath::Max<uint32>(UpscaledViewRect.Max.X, MaxX);
			MaxY = FMath::Max<uint32>(UpscaledViewRect.Max.Y, MaxY);
			TotalArea += FMath::TruncToInt(UpscaledViewRect.Width()) * FMath::TruncToInt(UpscaledViewRect.Height());
		}

		// To draw black borders around the rendered image (prevents artifacts from post processing passes that read outside of the image e.g. PostProcessAA)
		{
			int32 BlackBorders = 0; //FMath::Clamp(CVarSetBlackBordersEnabled.GetValueOnGameThread(), 0, 10);

			if (ViewFamily.Views.Num() == 1 && BlackBorders)
			{
				MinX += BlackBorders;
				MinY += BlackBorders;
				MaxX -= BlackBorders;
				MaxY -= BlackBorders;
				TotalArea = (MaxX - MinX) * (MaxY - MinY);
			}
		}

		// If the views don't cover the entire bounding rectangle, clear the entire buffer.
		if (ViewFamily.Views.Num() == 0 || TotalArea != (MaxX - MinX)*(MaxY - MinY) || bDisableWorldRendering)
		{
			SceneCanvas->DrawTile(0, 0, InViewport->GetSizeXY().X, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
		}
		else
		{
			// clear left
			if (MinX > 0)
			{
				SceneCanvas->DrawTile(0, 0, MinX, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
			}
			// clear right
			if (MaxX < (uint32)InViewport->GetSizeXY().X)
			{
				SceneCanvas->DrawTile(MaxX, 0, InViewport->GetSizeXY().X, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
			}
			// clear top
			if (MinY > 0)
			{
				SceneCanvas->DrawTile(MinX, 0, MaxX, MinY, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
			}
			// clear bottom
			if (MaxY < (uint32)InViewport->GetSizeXY().Y)
			{
				SceneCanvas->DrawTile(MinX, MaxY, MaxX, InViewport->GetSizeXY().Y, 0.0f, 0.0f, 1.0f, 1.f, FLinearColor::Black, NULL, false);
			}
		}
	}

	// Remove temporary debug lines.
	if (GetWorld()->LineBatcher != NULL)
	{
		GetWorld()->LineBatcher->Flush();
	}

	if (GetWorld()->ForegroundLineBatcher != NULL)
	{
		GetWorld()->ForegroundLineBatcher->Flush();
	}

	// Draw FX debug information.
	if (GetWorld()->FXSystem)
	{
		GetWorld()->FXSystem->DrawDebug(SceneCanvas);
	}

	// Render the UI.
	{
		SCOPE_CYCLE_COUNTER(STAT_UIDrawingTime);

		// render HUD
		bool bDisplayedSubtitles = false;
		for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
		{
			APlayerController* PlayerController = *Iterator;
			if (PlayerController)
			{
				ULocalPlayer* LocalPlayer = Cast<ULocalPlayer>(PlayerController->Player);
				if (LocalPlayer)
				{
					FSceneView* View = PlayerViewMap.FindRef(LocalPlayer);
					if (View != NULL)
					{
						// rendering to directly to viewport target
						FVector CanvasOrigin(FMath::TruncToFloat(View->UnscaledViewRect.Min.X), FMath::TruncToInt(View->UnscaledViewRect.Min.Y), 0.f);

						CanvasObject->Init(View->UnscaledViewRect.Width(), View->UnscaledViewRect.Height(), View);

						// Set the canvas transform for the player's view rectangle.
						SceneCanvas->PushAbsoluteTransform(FTranslationMatrix(CanvasOrigin));
						CanvasObject->ApplySafeZoneTransform();

						// Render the player's HUD.
						if (PlayerController->MyHUD)
						{
							//SCOPE_CYCLE_COUNTER(STAT_HudTime);

							DebugCanvasObject->SceneView = View;
							PlayerController->MyHUD->SetCanvas(CanvasObject, DebugCanvasObject);

							PlayerController->MyHUD->PostRender();

							// Put these pointers back as if a blueprint breakpoint hits during HUD PostRender they can
							// have been changed
							CanvasObject->Canvas = SceneCanvas;
							DebugCanvasObject->Canvas = DebugCanvas;

							// A side effect of PostRender is that the playercontroller could be destroyed
							if (!PlayerController->IsPendingKill())
							{
								PlayerController->MyHUD->SetCanvas(NULL, NULL);
							}
						}

						if (DebugCanvas != NULL)
						{
							DebugCanvas->PushAbsoluteTransform(FTranslationMatrix(CanvasOrigin));
							UDebugDrawService::Draw(ViewFamily.EngineShowFlags, InViewport, View, DebugCanvas);
							DebugCanvas->PopTransform();
						}

						CanvasObject->PopSafeZoneTransform();
						SceneCanvas->PopTransform();

						// draw subtitles
						if (!bDisplayedSubtitles)
						{
							FVector2D MinPos(0.f, 0.f);
							FVector2D MaxPos(1.f, 1.f);
							GetSubtitleRegion(MinPos, MaxPos);

							const uint32 SizeX = SceneCanvas->GetRenderTarget()->GetSizeXY().X;
							const uint32 SizeY = SceneCanvas->GetRenderTarget()->GetSizeXY().Y;
							FIntRect SubtitleRegion(FMath::TruncToInt(SizeX * MinPos.X), FMath::TruncToInt(SizeY * MinPos.Y), FMath::TruncToInt(SizeX * MaxPos.X), FMath::TruncToInt(SizeY * MaxPos.Y));
							FSubtitleManager::GetSubtitleManager()->DisplaySubtitles(SceneCanvas, SubtitleRegion, GetWorld()->GetAudioTimeSeconds());
							bDisplayedSubtitles = true;
						}
					}
				}
			}
		}

		//ensure canvas has been flushed before rendering UI
		SceneCanvas->Flush_GameThread();
		if (DebugCanvas != NULL)
		{
			DebugCanvas->Flush_GameThread();
		}

		OnDrawn().Broadcast();

		// Allow the viewport to render additional stuff
		PostRender(DebugCanvasObject);

		// Render the console.
		if (ViewportConsole)
		{
			ViewportConsole->PostRender_Console(DebugCanvasObject);
		}
	}


	// Grab the player camera location and orientation so we can pass that along to the stats drawing code.
	FVector PlayerCameraLocation = FVector::ZeroVector;
	FRotator PlayerCameraRotation = FRotator::ZeroRotator;
	{
		for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
		{
			(*Iterator)->GetPlayerViewPoint(PlayerCameraLocation, PlayerCameraRotation);
		}
	}

	DrawStatsHUD(GetWorld(), InViewport, DebugCanvas, DebugCanvasObject, DebugProperties, PlayerCameraLocation, PlayerCameraRotation);

	if (GEngine->IsStereoscopic3D(InViewport))
	{
#if !UE_BUILD_SHIPPING
		if (GEngine->HMDDevice.IsValid())
		{
			GEngine->HMDDevice->DrawDebug(DebugCanvasObject);
		}
#endif
	}

	OnEndDraw().Broadcast();
}



Full c++ source codes.

Hello???

Hello??
aa

EngineShowFlags.CompositeEditorPrimitives = true; will resolve the problem.
Finally it rendered similar in Editor.

Thank you…

But I found that GetHitProxy won’t work…

Hi, I am having the same problem as you were: no hit proxy, did you finally find a solution?

I dont find your project named FPSCpp?? 304775992