Is it possible to get textures by name?

I’m building a system where I would like to be able to give a root name of a texture and then get several related textures by appending strings to their names. For example, MyTexture could be the base and then I also grab MyTexture_M and MyTexture_N.

Is this something that’s possible in either C++ or Blueprint?

I believe you can do this via code if you programmatically create a Material Instance asset and then set the Texture Parameters to the string path you’ve built. I’ve done similar things with python so I’m pretty sure you can do this in C++. Not sure about Blueprint though. You can also fully create the Material via code but once that’s created, I don’t know how you would update the material’s Texture Parameter components if you wanted to do this repeatedly.

Are you sure about that? I’m having a look at UMaterialInstanceDynamic and both of the SetTexture functions take a UTexture. I don’t see any function that takes a name. It also would mean that we’re passing a potentially unloaded asset to the material system, which doesn’t seem right.

That said, this did lead me down a different rabbit hole that I think answers my question. TLDR, I ended up looking around for uses of FSoftObjectPath, which seems to be the correct way to hold a string to an asset. UPaperImporterSettings is even doing something similar to what I want by adding suffixes to texture names. That stuff appears to be editor logic, though, which brings me to my next point.

The biggest issue I can see with this is that there will be no references to these textures which, from my understanding of the UE4 pipeline, means that those assets will not be included in the build. Can anyone confirm that my understanding is correct? Is there any way around that without creating a custom editor?

Alright, with some more digging, I’ve answered my questions. Despite FSoftObjectPath being the right thing, you actually can’t use it directly in the data asset for what I’m doing because the editor still forces you to use the asset selector rather than a string. Instead, you need to use a string, construct an FSoftObjectPath from that, and then construct a TSoftObjectPtr from that. Finally, you’ll need to load the asset as you normally do with a TSoftObjectPtr. If you want access to this data from blueprint, you’ll have to add UFUNCTION accessors.

As for the reference issue, this is indeed how it works. To get around this, you point the packaging system to directories where you always want to cook everything inside. Search for Additional Asset Directories to Cook in here:

This will ensure that your textures are included in the build even if they are not directly referenced.

I can’t comment on FSoftObjectPath but this is how I would do it in python. I manually made a simple material that just has one Texture Sample Parameter connected to the Base Color output. The python code creates a cube, creates a material instance of that material if it doesn’t exist, and then assigns that to the cube. For the texture, you would build the UE asset path string and then load the asset. This means the texture must already be imported into UE. If not, it is also possible to import files programmatically. Once the texture is loaded, that is what I use to update the texture parameter value.

import unreal

def spawnCube():
   cubeActor = unreal.EditorLevelLibrary.spawn_actor_from_class(unreal.StaticMeshActor, location=(0,0,200))

   mesh = unreal.load_object(None, '/Engine/BasicShapes/Cube')
   staticMeshComp = cubeActor.get_editor_property('static_mesh_component')

   return cubeActor

def updateMaterialInstance(actor):
   materialInstancePath = '/Game/Materials/MyMaterialInstance.MyMaterialInstance'
   materialInstanceAsset = unreal.EditorAssetLibrary.load_asset(materialInstancePath)
   if materialInstanceAsset is None: # create if doesn't exist
      factory = unreal.MaterialInstanceConstantFactoryNew()
      factory.set_editor_property('create_new', True)
      assetTools = unreal.AssetToolsHelpers.get_asset_tools()
      materialInstanceAsset = assetTools.create_asset('MyMaterialInstance', '/Game/Materials/', None, factory)

   parentMaterialPath = '/Game/Materials/MyMaterial.MyMaterial'
   parentMaterialAsset = unreal.EditorAssetLibrary.load_asset(parentMaterialPath)

   materialInstanceAsset.set_editor_property('parent', parentMaterialAsset)

   texturePath = '/Game/StarterContent/Textures/T_Brick_Clay_Beveled_D.T_Brick_Clay_Beveled_D'
   textureAsset = unreal.EditorAssetLibrary.load_asset(texturePath)
   unreal.MaterialEditingLibrary.set_material_instance_texture_parameter_value(materialInstanceAsset, 'Base Color Texture', textureAsset)

   unreal.EditorAssetLibrary.save_loaded_asset(materialInstanceAsset) # save asset
   return materialInstanceAsset

def setMaterialOnActor(actor, materialAsset):
   staticMeshComp = actor.get_editor_property('static_mesh_component')
   staticMeshComp.set_material(0, materialAsset)

cubeActor = spawnCube()
materialInstanceAsset = updateMaterialInstance(cubeActor)
setMaterialOnActor(cubeActor, materialInstanceAsset)