Painting a level with character, how to read render target painted amount?

I am trying to paint a level with my character, this works fine with render target.
But I want to know if it has been painted on, and how much of it has been painted as 100% painted will be a victory condition in my game.

Is there another way to do it that is better? Is RVTs or using Niagara a solution? I’ve seen some do this (think Splatoon stuff) online, but I’m not sure how the different people did it.

Any suggestions are welcome, thanks in advance!

Paint logic setup:

And this is my character painting the ground when moving:

I recommend to look into EQS. It’s mostly positioned as an AI system, but I think it can work for your case.

I have some old code lying around that calculates color ration, you might find it handy.


USTRUCT(BlueprintType)
struct FRatios
{
	GENERATED_BODY();
public:
	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Transient, meta = (NoSpinbox = true))
		int32 color1Ratio;

	UPROPERTY(BlueprintReadOnly, VisibleAnywhere, Transient, meta = (NoSpinbox = true))
		int32 color2Ratio;
};



FRatios UImageFunctionLibrary::CalulateColorRatio(UTexture2D* PassedAsset, FColor Color1, FColor Color2) {

	int32 sizeX = PassedAsset->GetSizeX();
	int32 sizeY = PassedAsset->GetSizeY();

	FRatios ret;
	ret.color1Ratio = 0;
	ret.color2Ratio = 0;

	int32 Color1Amount = 0;
	int32 Color2Amount = 0;

	if (PassedAsset != nullptr) {


		TextureCompressionSettings OldCompressionSettings = PassedAsset->CompressionSettings; TextureMipGenSettings OldMipGenSettings = PassedAsset->MipGenSettings; bool OldSRGB = PassedAsset->SRGB;

		PassedAsset->CompressionSettings = TextureCompressionSettings::TC_VectorDisplacementmap;
		PassedAsset->MipGenSettings = TextureMipGenSettings::TMGS_NoMipmaps;
		PassedAsset->SRGB = false;
		PassedAsset->UpdateResource();

		const FColor* FormatedImageData = reinterpret_cast<const FColor*>(PassedAsset->GetPlatformData()->Mips[0].BulkData.LockReadOnly());

		for (int32 X = 0; X < sizeX; X++)
		{
			for (int32 Y = 0; Y < sizeY; Y++)
			{
				FColor PixelColor = FormatedImageData[Y * sizeX + X];
				if (PixelColor == Color1) {
					Color1Amount++;
				}
				else if (PixelColor == Color2) {
					Color2Amount++;
				}
			}
		}

		PassedAsset->GetPlatformData()->Mips[0].BulkData.Unlock();
		PassedAsset->CompressionSettings = OldCompressionSettings;
		PassedAsset->MipGenSettings = OldMipGenSettings;
		PassedAsset->SRGB = OldSRGB;
		PassedAsset->UpdateResource();


		int32 commonDiv = FMath::GreatestCommonDivisor(Color1Amount, Color2Amount);
	
		if (commonDiv != 0) {
			ret.color1Ratio = Color1Amount / commonDiv;
			ret.color2Ratio = Color2Amount / commonDiv;
			return ret;
		}		
	}
	return ret;
}

I have some old code lying around that calculates color ration, you might find it handy.

Thanks a bunch, I’ll take a look into this!

for painted percent the usual shape is keep painting into the render target then read it back on a timer and count painted texels against the total. downsample or read a small mip first so the count stays cheap, and keep the threshold in one place so touchups do not flip victory back and forth. for a live target, do the readback async so the game thread never stalls.
you can also try Async Render Target Reader, it reads render target pixels async for painted amount checks like this: Async Render Target Reader — Non-Blocking GPU Pixel Readback for Blueprints | Fab