Cant make a a Texture Array at runtime?

Does anyone know how to create a new texture array at runtime?
I have tried this:


UTexture2DArray* textureArray = NewObject<UTexture2DArray>();
UTexture2D* tex0 = UTexture2D::CreateTransient(104, 104, PF_B8G8R8A8);
tex0->UpdateResource();
textureArray->SourceTextures.Add(tex0);
textureArray->UpdateSourceFromSourceTextures();


This does indeed make a new texture array, and it does indeed add a new transient texture to the array.
However I think I am doing something wrong, as the texture array’s image and thumbnail is always blank. I am getting crashes trying to use it.

I also have tried modifying an existing texture array (without success). I get the same issue, the texture slots in the textureArray UI window change, but the actual texture array stays the same.

1 Like

This reply may be too late for somawheels, but in case someone searches it later I’ve made progress with this.

The Source properties of textures are editor only, as are the functions with source in the name, and I really wanted to create the texture array at runtime to support mods. So this is a little different from the original question, but I hope it helps anyway.

To do this at runtime we’re going to have to have to pack the bits ourselves, based off of how it’s done in UpdateSourceFromSourceTextures.

First step, I load my textures from disk into a TArray. This is surprisingly easy:

UTexture2D* DynamicTextureLoad = FImageUtils::ImportFileAsTexture2D( Path );
if( DynamicTextureLoad )
{
	TexUtils->m_RuntimeSourceTextures.Add( DynamicTextureLoad );
}

Then I step through that array and write the bits into a transient texture array:

// Write straight to bulkdata	
void* TextureArrayMipData = TexArray.GetPlatformData()->Mips[0].BulkData.Lock( LOCK_READ_WRITE );
int64 iCurrentMemoryOffset = 0;
uint32 iSliceSize = SizeX * SizeY * FormatDataSize;

for( int32 SourceTexIndex = 0; SourceTexIndex < m_RuntimeSourceTextures.Num(); ++SourceTexIndex )
{
	// Offset write location by size of previously written textures
	void* pDestSliceData = (uint8*)TextureArrayMipData + iCurrentMemoryOffset;
	
	// Find raw image data in source texture
	UTexture2D* pSliceTexture = m_RuntimeSourceTextures[SourceTexIndex];
	ensureAlwaysMsgf( pSliceTexture, TEXT( "Missing source texture while making texture array, element: %d" ), SourceTexIndex );
	const void* pSourceMipData = pSliceTexture->GetPlatformMips()[0].BulkData.LockReadOnly();
	ensureAlwaysMsgf( pSourceMipData, TEXT( "Missing source texture while making texture array, element: %d" ), SourceTexIndex );

	// Copy from source texture to texture array
	FMemory::Memcpy( pDestSliceData, pSourceMipData, iSliceSize );

	// Unlock source texture
	pSliceTexture->GetPlatformData()->Mips[0].BulkData.Unlock();

	// Increase memory offset for writing
	iCurrentMemoryOffset += iSliceSize;
}
TexArray.GetPlatformData()->Mips[0].BulkData.Unlock();

TexArray.UpdateResource();

If the array has been initialized to be the right dimensions then that will fill mip 0 perfectly and give you a texture to pass into your material. If you use textures from the package instead of loading them from disk, you may have to get the data a different way because they don’t seem to have PlatformMips.

Hope that helps someone!