How it is possible to draw 2D on editors viewport?

I want to render 2D on editor’s viewport not game viewport. (Just like other menus buttons in editor viewport) What should I do to reach this purpose? Can anyone proveide me a simple example code?

I had some success with using PrimitiveComponent and DebugRenderSceneProxy.

Here is the code for DebugRenderSceneProxy cpp file.

Based on info on internet I copied some code but my purpose was rendering 2D text on scren. Issue still remains. Only way to draw text is drawing on canvas and that crash the engine.

#include "BuildingDebugRenderSceneProxy.h"

void FBuildingDebugRenderSceneProxy::GetDynamicMeshElements(const TArray<const FSceneView*>& Views,
                                                            const FSceneViewFamily& ViewFamily, uint32 VisibilityMap,
                                                            FMeshElementCollector& Collector) const
{
	FDebugRenderSceneProxy::GetDynamicMeshElements(Views, ViewFamily, VisibilityMap, Collector);

	if (GEditor)
	{
		FViewport* Viewport = (FViewport*)GEditor->GetActiveViewport();
		FCanvas* DebugCanvas = Viewport->GetDebugCanvas();

			for (const auto& T : Texts)
			{
				if (GEngine)
             		{
				        if (Viewport)
             			{
             				if (DebugCanvas)
             				{
             					const UFont *uf=GEngine->GetSmallFont();
 
						FCanvasTextItem TextItem(FVector2D(160, 160), FText::GetEmpty(), uf, FLinearColor::White);
						TextItem.EnableShadow(FLinearColor::White);
						TextItem.Text = T.Text;
						TextItem.Scale = FVector2D(1, 1);
						DebugCanvas->DrawItem(TextItem);

					
						FCanvasBoxItem BoxItem(FVector2D(160, 160), FVector2D(160, 160));
						BoxItem.SetColor(FLinearColor::Green);
						BoxItem.LineThickness = 1.0f;
						DebugCanvas->DrawItem(BoxItem);
					}
				
				}
			}
		}
	}


	for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
	{
		if (VisibilityMap & (1 << ViewIndex))
		{
			const FSceneView* View = const_cast<FSceneView*>(Views[ViewIndex]);

			FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);

			/*for (const auto& T : Texts)
			{

			}*/
			for (const auto& C : Circles)
			{
				DrawCircle(PDI, C.Centre, C.X, C.Y, C.Color, C.Radius, C.NumSegments, SDPG_Foreground, C.Thickness, 0,
				           C.Thickness > 0);
			}

			// Draw Arcs
			for (const auto& C : Arcs)
			{
				::DrawArc(PDI,
				          C.Centre,
				          C.X, C.Y,
				          C.MinAngle, C.MaxAngle,
				          C.Radius, C.NumSegments,
				          C.Color, SDPG_Foreground);
			}
			// Draw Cylinders (properly! superclass ignores transforms)
			for (const auto& C : CylindersImproved)
			{
				::DrawWireCylinder(PDI,
				                   C.Centre,
				                   C.X,
				                   C.Y,
				                   C.Z,
				                   C.Color,
				                   C.Radius,
				                   C.HalfHeight,
				                   C.NumSegments,
				                   SDPG_Foreground);
			}
			for (const auto& C : CapsulesImproved)
			{
				::DrawWireCapsule(PDI,

				                  C.Location,

				                  C.X,
				                  C.Y,
				                  C.Z,
				                  C.Color,
				                  C.Radius,
				                  C.HalfHeight,
				                  16,
				                  SDPG_Foreground);
			}
		}
	}
}

FPrimitiveViewRelevance FBuildingDebugRenderSceneProxy::GetViewRelevance(const FSceneView* View) const
{
	// More useful defaults than FDebugRenderSceneProxy
	FPrimitiveViewRelevance Result;
	Result.bDrawRelevance = IsShown(View);
	Result.bDynamicRelevance = true;
	Result.bShadowRelevance = false;
	Result.bEditorPrimitiveRelevance = UseEditorCompositing(View);
	return Result;
}

Now, I can render geometric objects like circle,arc,box. It also renders text. However, I use DebugCanvas and when I use it that crashes the engine after a time also. I see flickering with drawing when I move cursor outside of viewport especially to content browser. Most probably I did something wrong.

Here is Crash Report:

LoginId:e2a8c4af4ab262c9f88ea8a0c438cbe4
EpicAccountId:25da9448a084490fb461536b67d743a5

Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x0000000000000000

UE4Editor_GomiPuzzle!FBuildingDebugRenderSceneProxy::GetDynamicMeshElements() [C:\Users\gomi\Documents\Unreal Projects\GomiPuzzle\Source\GomiPuzzle\Private\BuildingDebugRenderSceneProxy.cpp:25]
UE4Editor_Renderer!FSceneRenderer::GatherDynamicMeshElements() [Z:\UE_4.27.2\Engine\Source\Runtime\Renderer\Private\SceneVisibility.cpp:2926]
UE4Editor_Renderer!FSceneRenderer::ComputeViewVisibility() [Z:\UE_4.27.2\Engine\Source\Runtime\Renderer\Private\SceneVisibility.cpp:4110]
UE4Editor_Renderer!FDeferredShadingSceneRenderer::InitViews() [Z:\UE_4.27.2\Engine\Source\Runtime\Renderer\Private\SceneVisibility.cpp:4371]
UE4Editor_Renderer!FDeferredShadingSceneRenderer::Render() [Z:\UE_4.27.2\Engine\Source\Runtime\Renderer\Private\DeferredShadingRenderer.cpp:1455]
UE4Editor_Renderer!RenderViewFamily_RenderThread() [Z:\UE_4.27.2\Engine\Source\Runtime\Renderer\Private\SceneRendering.cpp:3742]
UE4Editor_Renderer!<lambda_cc1442c4bafed83188c182dd4b1a4ab0>::operator()() [Z:\UE_4.27.2\Engine\Source\Runtime\Renderer\Private\SceneRendering.cpp:4009]
UE4Editor_Renderer!TEnqueueUniqueRenderCommandType<FRendererModule::BeginRenderingViewFamily'::43’::FDrawSceneCommandName,<lambda_cc1442c4bafed83188c182dd4b1a4ab0> >::DoTask() [Z:\UE_4.27.2\Engine\Source\Runtime\RenderCore\Public\RenderingThread.h:183]
UE4Editor_Renderer!TGraphTask<TEnqueueUniqueRenderCommandType<FRendererModule::BeginRenderingViewFamily'::43’::FDrawSceneCommandName,<lambda_cc1442c4bafed83188c182dd4b1a4ab0> > >::ExecuteTask() [Z:\UE_4.27.2\Engine\Source\Runtime\Core\Public\Async\TaskGraphInterfaces.h:886]
UE4Editor_Core!FNamedTaskThread::ProcessTasksNamedThread() [Z:\UE_4.27.2\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:710]
UE4Editor_Core!FNamedTaskThread::ProcessTasksUntilQuit() [Z:\UE_4.27.2\Engine\Source\Runtime\Core\Private\Async\TaskGraph.cpp:602]
UE4Editor_RenderCore!RenderingThreadMain() [Z:\UE_4.27.2\Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp:373]
UE4Editor_RenderCore!FRenderingThread::Run() [Z:\UE_4.27.2\Engine\Source\Runtime\RenderCore\Private\RenderingThread.cpp:509]
UE4Editor_Core!FRunnableThreadWin::Run() [Z:\UE_4.27.2\Engine\Source\Runtime\Core\Private\Windows\WindowsRunnableThread.cpp:86]

That looks like it is about “Thread”.

I found a better soultion. Which looks better and does cause crash immediately.

for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
	{
		if (VisibilityMap & (1 << ViewIndex))
		{
			const FSceneView* View = const_cast<FSceneView*>(Views[ViewIndex]);

			FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);

			for (const auto& T : Texts)
			{
				if (GEditor->IsValidLowLevel())
				{
						FCanvas* Fc = GEditor->GetAllViewportClients()[1]->Viewport->GetDebugCanvas();
						if (Fc)
						{
							FCanvasTextItem TextItem(FVector2D(160, 160), FText::GetEmpty(), GEditor->GetMediumFont(),
							                         FLinearColor::White);
							TextItem.EnableShadow(FLinearColor::White);
							TextItem.Text = T.Text;
							TextItem.Scale = FVector2D(1, 1);
							Fc->DrawItem(TextItem);
							
							//FEditorModeTools().DrawHUD(GEditor->GetAllViewportClients()[1],GEditor->GetAllViewportClients()[1]->Viewport, View, Fc);
							FEditorModeTools().Render(View,GEditor->GetAllViewportClients()[1]->Viewport,PDI);
						}

				}
			}

//...
//...
//...
}

I am not sure but I noticed a few crashed. Additionally, that has a flickering drawing when cursor is in content browser. Last, “debugrendersceneproxy” class not drawing when its 3d location is out of screen. And sometimes flickering.

It looks like this and problems also visible: https://www.youtube.com/watch?v=ZMjO1Oo-Slo

I solved this problem. I ended up with modifying of Engine files. For this purpose I choosed ComponentVisualizer and an ActorComponent. Naturally, ComponentVisualizers are rendered only when associated Actor is selected. This is not something I was looking. I wanted to display visualizer even not selected.

I tracked engine files. I went to ComponentVisualizer.cpp and tracked DrawVisualizationHUD(…) function and found it is called in another file called UnrealEdEngine.cpp here engine has this function and orginal code looks like this:

void UUnrealEdEngine::DrawComponentVisualizersHUD(const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas)
{
	for(FCachedComponentVisualizer& CachedVisualizer : VisualizersForSelection)
	{
		CachedVisualizer.Visualizer->DrawVisualizationHUD(CachedVisualizer.ComponentPropertyPath.GetComponent(), Viewport, View, Canvas);
	}
}

Only selected actors’ visualizers are collected into array called VisualizersForSelection with function void UUnrealEdEngine::OnEditorSelectionChanged(UObject SelectionThatChanged)*. Now, we have nothing to do with selectionchanged. We need to modify DrawComponentVisualizersHUD

I modified code like this:

void UUnrealEdEngine::DrawComponentVisualizersHUD(const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas)
{
	const UWorld* World = GWorld;
	if( World )
	{
		const ULevel* Level = World->GetCurrentLevel();
		if( !Level )
		{
			Level = World->PersistentLevel;
		}
		check(Level);
		
		for (const AActor *actor :Level->Actors)
		{
			if (actor != nullptr)
			{
				TInlineComponentArray<UActorComponent*> Components;
				actor->GetComponents(Components, true);
		
				for (int32 CompIdx = 0; CompIdx < Components.Num(); CompIdx++)
				{
					UActorComponent* Comp = Components[CompIdx];
					if (Comp->IsRegistered())
					{
						// Try and find a visualizer
						TSharedPtr<FComponentVisualizer> Visualizer = FindComponentVisualizer(Comp->GetClass());
						if (Visualizer.IsValid())
						{
							FCachedComponentVisualizer(Comp, Visualizer).Visualizer->DrawVisualizationHUD(FCachedComponentVisualizer(Comp, Visualizer).ComponentPropertyPath.GetComponent(), Viewport, View, Canvas);
						}
					}
				}
			}
		}
	}
/*This can stay here it does not matter for now*/
	for(FCachedComponentVisualizer& CachedVisualizer : VisualizersForSelection)
	{
		CachedVisualizer.Visualizer->DrawVisualizationHUD(CachedVisualizer.ComponentPropertyPath.GetComponent(), Viewport, View, Canvas);
	}
}
//...
//...
//...
}

Now I can see drawing of visualizer even not selected. Any other drawing operation with canvas can be done in custom visualizer’s .cpp file.

1 Like