Drawing 2d textures at a 3d depth

Happy Friday!

[apologies if this is a dupe - thought I’d entered the question yesterday but can’t find it now!]

I’m using UCanvas::DrawItem with a FCanvasTileItem. I’d like to be able to specify the distance from camera at which to draw this so I can control its clipping relative to 3d actors in the scene.

Setting the Depth parameter in the tile item doesn’t seem to help.

What’s the simplest way to approach this problem? I guess I could look into 3d drawing and project the 2d texture onto the plane I want it to be at.

Functionality important to me: specifying screen coords, specifying texture and UVs, specifying color.

Reading about Paper2D it seems like they have solved many of the same problems I’d have to and are way ahead of me on the development stage (talking about upcoming rendering by material optimizations etc) but significantly heavier weight than I currently need.

Question is however - how do you create paper2d sprites from UTexture and coordinates in C++? I don’t want to drive these via the editor.

So far failing on the sprite creation, though might be close.

#include "PaperSpriteActor.h"
void CreateTestSprite(UTexture *pTestTexture, UWorld *pWorld, AActor *pOwner)
{
	APaperSpriteActor* spawnedSprite = pWorld->SpawnActorDeferred<APaperSpriteActor>(APaperSpriteActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator, pOwner);
	if (spawnedSprite)
	{
		FSpriteAssetInitParameters initParams;
		initParams.SetTextureAndFill((UTexture2D*)pTestTexture->Resource);
		initParams.Dimension.Set(10.0f, 10.0f);
		UPaperSprite *pNewSprite = (UPaperSprite *)StaticConstructObject(UPaperSprite::StaticClass(), spawnedSprite);
		pNewSprite->InitializeSprite(initParams);
		spawnedSprite->GetRenderComponent()->SetSprite(pNewSprite);
	}
}

The code above is throwing an exception:

Ensure condition failed: (TextureWidth > 0) && (TextureHeight > 0) [File:D:\BuildFarm\buildmachine_++depot+UE4-Releases+4.7\Engine\Plugins\2D\Paper2D\Source\Paper2D\Private\PaperSprite.cpp] [Line: 817] 

When the call to SetTextureAndFill happens I can see that the width dimension in initParams goes to a large negative number so perhaps there’s something wrong with the texture passed in? I’m using it elsewhere though, without problems, via UCanvas::DrawItem.

Casting the wrong texture object… the following fixes the exceptions, now just to get something drawing!

APaperSpriteActor* spawnedSprite = pWorld->SpawnActorDeferred<APaperSpriteActor>(APaperSpriteActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator, pOwner);
if (spawnedSprite)
{
	FSpriteAssetInitParameters initParams;
	initParams.SetTextureAndFill((UTexture2D*)pTestTexture);
	UPaperSprite *pNewSprite = (UPaperSprite *)StaticConstructObject(UPaperSprite::StaticClass(), spawnedSprite);
	pNewSprite->InitializeSprite(initParams);
	spawnedSprite->GetRenderComponent()->SetSprite(pNewSprite);
}

A couple more steps and hey presto something draws!

void CreateTestSprite(UTexture *pTestTexture, UWorld *pWorld, AActor *pOwner)
{
	APaperSpriteActor* spawnedSprite = pWorld->SpawnActorDeferred<APaperSpriteActor>(APaperSpriteActor::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator, pOwner);
	if (spawnedSprite)
	{
		FSpriteAssetInitParameters initParams;
		initParams.SetTextureAndFill((UTexture2D*)pTestTexture);
		UPaperSprite *pNewSprite = (UPaperSprite *)StaticConstructObject(UPaperSprite::StaticClass(), spawnedSprite);
		pNewSprite->InitializeSprite(initParams);
		spawnedSprite->GetRenderComponent()->SetMobility(EComponentMobility::Movable);
		spawnedSprite->GetRenderComponent()->SetSprite(pNewSprite);
		spawnedSprite->SetActorScale3D(FVector(200.0f, 200.0f, 200.0f));
		spawnedSprite->OnConstruction(spawnedSprite->GetTransform());
		spawnedSprite->FinishSpawning(spawnedSprite->GetTransform());
	}
}

Or not… only in editor mode.

So its looking like Paper2D is only really usable through the editor and not procedurally?

error C2039: 'InitializeSprite' : is not a member of 'UPaperSprite'
MainFrameActions: Packaging (Windows (32-bit)): UnrealBuildTool:         c:\program files\unreal engine\4.7\engine\plugins\2d\paper2d\source\paper2d\classes\PaperSprite.h(43) : see declaration of 'UPaperSprite'

New solution, use another actor type I have with a static mesh and a MID applied that I can set the texture on. Paper2D looks awesome but not aimed at what I wanted out of it.

Hello.

Can you show the source code of the result? Now I’m struggling with the same problem.

Semi complicated as it’s wrapped up in blueprint, material and C++.

Basic approach:

  • Create a simple mesh that you can map a texture onto
  • Create a blueprint actor that has a C++ base based on AActor (called SpriteActor below)
  • Have the blueprint actor contain your simple static mesh
  • Assign a MID to the mesh
  • Have the MID reference a texture declared in SpriteActor
  • Render to this texture
  • Update the MID texture
  • Update any UV scaling and offsets needed
  • Work out positioning and scaling in world space to match screen coordinates (if needed) (see the Canvas DeProject function and project onto a plane at the right depth and orientation to the camera).

My solution is very much wrapped up in my code and grown organically but there are some snippets below that might help. Or confuse.

Creating a render target inside Sprite Actor

DiffuseRenderTexture = PCIP.CreateDefaultSubobject<UTextureRenderTarget2D>(this, TEXT("DiffuseRenderTexture"));

Blueprint event to allow MID to get updated with texture changes

UFUNCTION(BlueprintImplementableEvent, meta = (FriendlyName = "OnNewTexture"))
	virtual void BP_OnNewTexture();

Code to start and end rendering to your render target

FCanvas *UISpriteComponent::StartTextureRender(int iTextureSize, bool bAlphaTexture)
{
	auto pTexture = m_SpriteActor.IsValid() ? (bAlphaTexture ? m_SpriteActor->GetAlphaRenderTexture() : m_SpriteActor->GetDiffuseRenderTexture()) : 0;

	FLinearColor clearColor(0.0f, 0.0f, 0.0f, 0.0f);
		pTexture->InitAutoFormat(iTextureSize, iTextureSize);
		pTexture->ClearColor = clearColor;
		pTexture->UpdateResourceImmediate();

	FRenderTarget *pRT = pTexture->GameThread_GetRenderTargetResource();
		FCanvas *pCanvas = new FCanvas(pRT, NULL, smp_World, GMaxRHIFeatureLevel);
		return pCanvas;
}

void UISpriteComponent::EndTextureRender(FCanvas *pCanvasWillDestroy, bool bAlphaTexture)
{
		pCanvasWillDestroy->Flush_GameThread(true);

		delete pCanvasWillDestroy;