Texture from uasset

Hello comrades!
I’m trying to implement a system that would automatically load textures from my working scene folder. This is necessary because there are a lot of textures, and I don’t want to process them manually every time.

Thanks to the plugin that gives control over folders and files, I can access files and pick up textures. However, their format is written as .uasset

Help me, even though I get these files, I don’t know how to convert them into readable Texture2D format. I planned to store these textures in an array and specify them for dynamic material, where I need it.

image

I know there is a node that can accept png and produce Texture 2D . But, in the project, of course, they are stored differently.
Maybe you can recommend a plugin

Got it working.

Just replace YOUR_API with the your project api

.h

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "ImageFunctionLibrary.generated.h"

/**
 * 
 */
UCLASS()
class YOUR_API UImageFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()

		UFUNCTION(BlueprintCallable, BlueprintPure)
		static TArray<UTexture2D*> ImportAsTexture2DArray(const FString directory, const bool fullPath, const FString onlyFilesStartingWith, const FString onlyFilesWithExtension, bool skipRecursionSelf);
	
		UFUNCTION()
			static TArray<FString> GetAllFilesInDirectory(const FString directory, const bool fullPath, const FString onlyFilesStartingWith, const FString onlyFilesWithExtension, bool skipRecursionSelf);
	
		UFUNCTION(BlueprintCallable, BlueprintPure)
			static TArray<UTexture2D*> ImportTexture2DUAssetArray(const FString directory, const FString onlyFilesStartingWith, bool IncludeSubdirectories);

		UFUNCTION()
			static  FString filterDirectory(FString &path);
};

.cpp

#include "ImageFunctionLibrary.h"
#include "ImageUtils.h" 
#include "HAL/FileManagerGeneric.h" 
#include "Misc/LocalTimestampDirectoryVisitor.h" 


TArray<UTexture2D*> UImageFunctionLibrary::ImportAsTexture2DArray(const FString directory, const bool fullPath, const FString onlyFilesStartingWith, const FString onlyFilesWithExtension, bool skipRecursionSelf = true)
{
	FString directoryCut = directory;	
	filterDirectory(directoryCut);
	TArray<UTexture2D*> filesArray;	
	TArray<FString> files = GetAllFilesInDirectory(directoryCut, fullPath, onlyFilesStartingWith, onlyFilesWithExtension, skipRecursionSelf);

	for (int32 i = 0; i < files.Num(); i++) {
		UTexture2D*  f = FImageUtils::ImportFileAsTexture2D(files[i]);
		filesArray.Add(f);
	}		
	return filesArray;
}


TArray<UTexture2D*> UImageFunctionLibrary::ImportTexture2DUAssetArray(const FString directory, const FString onlyFilesStartingWith, bool IncludeSubdirectories)
{
	TArray<UTexture2D*> returnFiles;
	FString directoryCut = directory;
	filterDirectory(directoryCut);

	FString contentPath = FPaths::ProjectContentDir();		
	FString fullPath = FPaths::ConvertRelativePathToFull(contentPath);
	FString fullProjPath = fullPath + directory;
	TArray<FString> files = GetAllFilesInDirectory(fullProjPath, true, onlyFilesStartingWith, "uasset", !IncludeSubdirectories);
			
		for (int i = 0; i < files.Num(); i++) {

		files[i].RemoveFromStart(fullPath);

		files[i].RemoveFromEnd(".uasset");		
		//FString filePath = "/Game/" + directory + "/" + files[i];		
		FString filePath = "/Game/" + files[i];
		FSoftObjectPath TexturePath(TEXT("Texture2D'" + filePath +  "'"));
		UTexture2D* texture2d = Cast<UTexture2D>(TexturePath.ResolveObject());
		if (texture2d == nullptr)
		{
			texture2d = CastChecked<UTexture2D>(TexturePath.TryLoad());
			returnFiles.Add(texture2d);
		}		
	}

	return returnFiles;
}

FString UImageFunctionLibrary::filterDirectory(FString &path)
{		
	path.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive);
	path.ReplaceInline(TEXT("//"), TEXT("/"), ESearchCase::CaseSensitive);
	path.RemoveFromStart(TEXT("/"));
	path.RemoveFromEnd(TEXT("/"));
	FPlatformMisc::NormalizePath(path);		
	path.RemoveFromEnd(FPaths::GetExtension(path, true));
	return path;
}


TArray<FString> UImageFunctionLibrary::GetAllFilesInDirectory(const FString directory, const bool fullPath, const FString onlyFilesStartingWith, const FString onlyFilesWithExtension,bool skipRecursionSelf = true)
{
	FString directoryCut = directory;
	filterDirectory(directoryCut);

	// Get all files in directory
	TArray<FString> directoriesToSkip;
	TArray<FString> directoriesToSkipRecurse;
	if (skipRecursionSelf == true) {
		directoriesToSkipRecurse.Add(directoryCut);
	}
	IPlatformFile& PlatformFile = FPlatformFileManager::Get().GetPlatformFile();
	FLocalTimestampDirectoryVisitor Visitor(PlatformFile, directoriesToSkip, directoriesToSkipRecurse, false);
	PlatformFile.IterateDirectory(*directoryCut, Visitor);
	TArray<FString> files;

	for (TMap<FString, FDateTime>::TIterator TimestampIt(Visitor.FileTimes); TimestampIt; ++TimestampIt)
	{
		const FString filePath = TimestampIt.Key();
		const FString fileName = FPaths::GetCleanFilename(filePath);
		bool shouldAddFile = true;

		// Check if filename starts with required characters
		if (!onlyFilesStartingWith.IsEmpty())
		{
			const FString left = fileName.Left(onlyFilesStartingWith.Len());

			if (!(fileName.Left(onlyFilesStartingWith.Len()).Equals(onlyFilesStartingWith)))
				shouldAddFile = false;
		}

		// Check if file extension is required characters
		if (!onlyFilesWithExtension.IsEmpty())
		{
			if (!(FPaths::GetExtension(fileName, false).Equals(onlyFilesWithExtension, ESearchCase::IgnoreCase)))
				shouldAddFile = false;
		}

		// Add full path to results
		if (shouldAddFile)
		{
			files.Add(fullPath ? filePath : fileName);
		}
	}

	return files;
}

edit: found a bug is now fixed. Function renamed to “ImportTexture2DUAssetArray”

added folder filter / sanitizer

stop scanning for uasset files, that is the trap here. uassets on disk are editor side files, at runtime the cooked game only sees what went through the cooker, so resolving them by path breaks the moment you package.

keep your source pngs in a plain folder and import those instead. FImageUtils ImportFileAsTexture2D takes an absolute path and gives you a UTexture2D you can put into an array and feed to a dynamic material, which is the setup the reply above already sketched. run it once at startup and rescan whenever you need fresh files.

two things that bite people with hot folders: the watch event can fire while a file is still copying, so retry the import once if it fails, and dont run the import on the game thread for big images or you get a hitch.

you can also try Runtime Directory Watcher, a runtime plugin for Unreal Engine that turns any folder into a hot folder with Blueprint events for new files and async png jpg loading: Runtime Directory Watcher — Hot-Folder File Events & Async Texture Loader | Fab