Creating UTexture from data in code, making it a material, and adding it to a mesh

Hi again,

I’ve been using the ‘Procedural Mesh Generation’ tutorial to render meshes dynamically which seems to be working great, now I am up to the part where I want to put custom textures on it and have a question.

I noticed code in the tutorial to grab a default material:



// Grab material
Material = Component->GetMaterial(0);
if(Material == NULL)
{
    Material = UMaterial::GetDefaultMaterial(MD_Surface);
}


I set the UV’s correctly on my mesh and the default material renders fine, however I have my own texture data in an array in memory (which is in DXT1 compressed format) that I want to use.

How would I use this on my mesh? I was thinking I need to create a UTexture from my raw data array (hoping that UE4 supports DXT1 compressed textures), and then create a material from this UTexture, then retrieve it / apply it etc.

Is there any tutorials around that will show me how to achieve this?

Thanks!

Here is a snippet of code I use to dynamically load textures at runtime, I used the FDDSLoadHelper to actually load the DDS data (I may have made some changes to the FDDSLoadHelper class to allow for additional formats, but I can’t remember if I kept those changes).

Thanks for the function it does what I need it to do in terms of generating the UTexture from my data, but is there any tutorials around for creating a UMaterial using this texture and retrieving it to apply on my mesh?

Thanks again.

How I have done it, is by having a base material that I have created with a texture parameter. I then load this material in code using LoadObject<UMaterialInstance>. Then create a dynamic material instance from that using UMaterialInstanceDynamic::Create and then assign my dynamically loaded texture to the texture parameter.

This way at least the material can be tweaked in the editor. You can create a material in code via the editor functions (ie. SpeedTree does this when importing speedtree models). But I don’t believe you can during runtime, as the material nodes are compiled down to shader bytecode. You may be able to but would require a shaders to be recompiled during runtime.

Thanks , this has helped me a lot.

I created a base material in the editor for my project with a TextureSampleParameter2D assigned to it.
However I’m having an issue finding that material with a LoadObject call back in the code. I am referencing the material by the name I gave it when I created / saved it in the editor for my project:




// 'Material' is the default material interface like from the 'Generate Procedural Mesh' tutorial
// I am doing this for each generated mesh component

std::string ts = "TerrainMaterial";
std::wstring ws(ts.begin(), ts.end());
UMaterialInstance *terrainMaterialInstance = LoadObject<UMaterialInstance>(NULL, ws.c_str()); // Returns NULL ?
UMaterialInstanceDynamic *dynamicTerrainMaterialInstance = UMaterialInstanceDynamic::Create(Material, terrainMaterialInstance);
dynamicTerrainMaterialInstance->SetTextureParameterValue("TerrainTexture", Component->GetMeshTexture()); // This UTexture was created earlier and stored on the component


Any ideas would help, thanks.

It needs to be the full reference name. You can obtain this from the content browser, by right clicking on the asset and selecting “Copy Reference” it will have the following format: “Material’/Game/Materials/M_CobbleStone_Rough.M_CobbleStone_Rough’”

Thanks, apologies to keep posting issues like this, but I still cannot seem to get the texture rendering on my generated mesh. My code now looks like this:



// Where 'Component' is of type UGeneratedMeshComponent*

Material = Component->GetMaterial(0);
if (Material == NULL)
{
	Material = UMaterial::GetDefaultMaterial(MD_Surface);
}

std::string ts = "Material'/Game/Materials/TerrainMaterial.TerrainMaterial'";
std::wstring ws(ts.begin(), ts.end());
UMaterial *terrainMaterialInstance = LoadObject<UMaterial>(NULL, ws.c_str()); // I assume I can just use the master material here like this and not just an instance? It returns a valid result
UMaterialInstanceDynamic *dynamicTerrainMaterialInstance = UMaterialInstanceDynamic::Create(Material, terrainMaterialInstance); // This also seems to return a valid result
dynamicTerrainMaterialInstance->SetTextureParameterValue(FName("TerrainTexture"), Component->GetMeshTexture());
Component->SetMaterial(0, Material);


Everything compiles and runs fine, but I still seem to get a grey checkered default texture covering the mesh. That tells me that I at least have my UV’s correct.

The ‘TerrainMaterial’ that I setup in the editor is very basic, I just created a new material in the material editor, added a ‘MaterialExpressionTextureSampleParameter2D’ and called it ‘TerrainTexture’, and dragged its white pin onto the base colour of ‘TerrainMaterial’ and saved it, that’s it.

I’d tried using some of the stock materials as well but they didn’t display either so I assume there’s something wrong in what I’m doing above.

Still stuck on this, been working on other parts in the meantime, does anyone have any ideas why the above code wouldn’t apply the material on the mesh? I still get the default checkered grey texture, no matter how I tweak this code.

Thanks!

Have u tried using a material instance instead of a material? Just to test. Because I may have had issues using a material, as I am using only material instances to create my dynamic instances.

Hi mate, yeah I tried with and without referencing instances


UMaterial *terrainMaterialInstance = LoadObject<UMaterial>(NULL, ws.c_str());

or


UMaterialInstance *terrainMaterialInstance = LoadObject<UMaterialInstance>(NULL, ws.c_str());

With ‘ws’ pointing to an instanced material of the original.

In both cases it retrieves a result (i.e. a non-null UMaterial* or UMaterialInstance* object respectively), so it seems to find them, just doesn’t apply them properly.

I thought it might be to do with how I set up the material with a MaterialExpressionTextureSampleParameter2D linked to the base colour, but even if I point the ‘ws’ string to a well known existing material (e.g. “Material’/Game/Materials/M_CobbleStone_Rough.M_CobbleStone_Rough’” ) and don’t set any texture parameter, it still shows the default texture, which tells me something more fundamental is wrong.

I’m wondering if I really should be parenting the UMaterialInstanceDynamic to ‘Material’ which is the material interface I get back from the GetDefaultMaterial call:



Material = UMaterial::GetDefaultMaterial(MD_Surface);

// ...

UMaterialInstanceDynamic *dynamicTerrainMaterialInstance = UMaterialInstanceDynamic::Create(Material, terrainMaterialInstance);

Or something else.

Ah sorry, didn’t even see that u were parenting it to Material, it should be parented to your terrainMaterialInstance. The way I call create is as follows: UMaterialInstanceDynamic::Create( <MaterialInstance>, NULL );

Ah that makes more sense, thanks heaps for these replies it really helps me learn a lot.

Just one piece of the puzzle left. The material now applies to the component mesh, however only with the texture I assigned it in the material editor (green moss, it wouldn’t let me compile the material without specifying some base texture for the parameter), but it doesn’t apply the custom texture I am passing through as a parameter in code, which I expected it to change to. I assumed the first parameter of the function needs to reference the ‘Parameter Name’ I gave it in the material editor:


dynamicTerrainMaterialInstance->SetTextureParameterValue(FName("TerrainTexture"), Component->GetMeshTexture());

My material is fairly basic and only links to the Base colour pin

To make sure it wasn’t an issue with the DXT1 texture I was using, I tried changing the parameter to a stock texture, but even that didn’t work (still shows the green moss one, and not the texture I retrieve and assign. ‘theTexture’ comes back as a valid UTexture*, so it must be the way I’m setting the parameter.



// As a test just retrieve a stock texture to assign as the parameter

std::string a = "Texture2D'/Game/Textures/T_Fire_Tiled_D.T_Fire_Tiled_D'";
std::wstring b(a.begin(), a.end());
UTexture *theTexture = LoadObject<UTexture>(NULL, b.c_str());

std::string ts = "MaterialInstanceConstant'/Game/Materials/TerrainMaterial_Inst.TerrainMaterial_Inst'";
std::wstring ws(ts.begin(), ts.end());
UMaterialInstance *terrainMaterialInstance = LoadObject<UMaterialInstance>(NULL, ws.c_str());
UMaterialInstanceDynamic *dynamicTerrainMaterialInstance = UMaterialInstanceDynamic::Create(terrainMaterialInstance, NULL);
dynamicTerrainMaterialInstance->SetTextureParameterValue("TerrainTexture", theTexture);
Component->SetMaterial(0, terrainMaterialInstance);


The only difference I can see between yours and mine now is, I used FName( TEXT( “TerrainTexture” ) ) instead of just “TerrainTexture” in the SetTextureParameterValue call.

Yeah it’s a bit of a mystery now, it seems like it should be setting the parameter fine. Thanks for your help .

Has anyone done similar before? Still hoping someone can spot what I’m doing wrong with the texture parameter.

Thanks.

Did anyone figure out how to get this to work?
I am trying to do the same thing! (just starting out with UE4)

Cheers

Well the code I provided above at the start does work, since that is still the same code I am using even in 4.5. But am unsure as to the problem Avidius was having, and we never got to the bottom of his particular problem

Ok. Only thing is I was more interested in the ‘attaching the texture to a shader’ bit. The bit that goes

dynamicTerrainMaterialInstance->SetTextureParameterValue(“TerrainTexture”, theTexture); I believe? That does not seem to work for me either and I can’t find any other examples. I have done NULL checks after all the LoadObject calls so I believe they are all good.

Is there another approach for attaching texture to a shader? (also eventually I will want to generate my own texture, just testing with an existing one for now)

Cheers

Have you had any success with this? I think I’ve run into the same problem: How to set a texture parameter in C++? - Rendering - Epic Developer Community Forums

EDIT: I think the problem might be with calling SetTextureParameterValue from a constructor. Any ideas why that might be?

Thanks for the code, can you help me make it work ?
I’m new to C++ in general, and i’v just pasted the code into my cpp class and added it’s signature to the header.



h.
/** Loads a texture from the data path */
UFUNCTION(BlueprintPure, Category = "Textures")
static UTexture2D* LoadTexture( FString TextureFilename );

.cpp
/** Loads a texture from the data path */
UTexture2D* UMyBlueprintFunctionLibrary::LoadTexture(FString TextureFilename)
{
...
}



And i get this error:


Error	1	error C2065: 'FDDSLoadHelper' : undeclared identifier	C:\OldSchoolNightmare\Source\OldSchoolNightmare\MyBlueprintFunctionLibrary.cpp	129	1	OldSchoolNightmare



How am i supposed to make it find this class, or did something changed in UE4.7 ?

Bump, i really need this thing to work for me, please help :expressionless: