Material instance is not reflected as a subclass of the reflected base material

Summary

Material instance is not reflected as a subclass of the reflected base material.

Please select what you are reporting on:

Unreal Editor for Fortnite

What Type of Bug are you experiencing?

Other

Steps to Reproduce

If you create a m_base material and then create an instance of it mi_instance, the mi_instance will reflect as a subclass of material class instead of being a subclass of the reflected m_base material subclass.

Expected Result

mi_instance should be a subclass of m_base, not a direct subclass of material.

- m_base<scoped { ... }> := class<final><public>(material):
+ m_base<scoped { ... }> := class<public>(material):
- mi_instance<scoped { ... }> := class<final><public>(material):
+ mi_instance<scoped { ... }> := class<public>(m_base):

Observed Result

mi_instance is reflected as a direct subclass of material.

m_base<scoped { ... }> := class<final><public>(material):
mi_instance<scoped { ... }> := class<final><public>(material):

Platform(s)

UEFN (v34.00)

FORT-871009 has been created and its status is ā€˜Unconfirmed’. This is now in a queue to be reproduced and confirmed.

We’re getting someone to take a look!

Hi @Velocity_Zero

I am not 100% sure what you are trying to do.
In Unreal Engine, both material and material instance use Material Interface as their common parent class.
In UEFN, both use the parent class ā€œmaterialā€ therefore you do get a different name.

To understand your issue better and maybe help you on the way:

I guess you want to create a new material from Verse code?
Why are you trying to create a new (material) class? (You cannot in UEFN.)
Try not to create a class when you actually want an object. (your new material/instance)
If you want to create a new material, do it through the editor as it will show up in the material digest.

I have no intention of creating a custom material class in code. That’s what the editor will reflect for me from the material assets.

If you wanted to collect multiple different material objects into an array for example and expect the exact same properties, the base material should be a supertype of a reflected material instance. That way you could iterate over them all simultaneously and update them as needed. Right now a material instance is being a subtype of material instead of its base material which I create an asset for.

MaterialsAsBase: []m_base = array {
  m_base {},
  mi_instance {}
}

for (Material: MaterialsAsBase) {
  set Material.Parameter = 1.0
}

Something like this is not possible as mi_instance does not share type relationship with m_base, but it should as mi_instance is actually m_base, but with potentially overridden parameters. Right now their common supertype will be only material, but it should be m_base.

Here’s an analogy in pure verse code.

m_pseudo_material_base := class {
  var SomeParameter: float = 0.0 
}

mi_pseudo_material:= class(m_pseudo_material_base) {
  var SomeParameter<override>: float = 1.0 
}

Right now here’s what the type relation is being reflected into:

What I personally expect it to be is this though:


Imagine this scenario. Many different meshes (mesh_components) use several different material instances which all originate from the same base material.

Right now you would need to cast each material slot to the concrete reflected material subtype in order to update a single parameter.

This should be much easier. All you have to do is to cast every material instance to the base material and update the parameter of interest on all different material instances.

The code right now:

# Reflected materials
m_my_material := class<final><public>(material):
  @editable
  var Saturation<public>: float = external {}

  @editable
  var Contrast<public>: float = external {}

mi_my_material_red := class<final><public>(material):
  @editable
  var Saturation<public>: float = external {}

  @editable
  var Contrast<public>: float = external {}

mi_my_material_green := class<final><public>(material):
  @editable
  var Saturation<public>: float = external {}

  @editable
  var Contrast<public>: float = external {}

mi_my_material_blue := class<final><public>(material):
  @editable
  var Saturation<public>: float = external {}

  @editable
  var Contrast<public>: float = external {}

# Reflected meshes
my_mesh_red := class<final><public>(mesh_component):
  @editable
  var MyMaterial<public>: material = external {} # uses `mi_my_material_red`

my_mesh_green := class<final><public>(mesh_component):
  @editable
  var MyMaterial<public>: material = external {} # uses `mi_my_material_green`

my_mesh_blue := class<final><public>(mesh_component):
  @editable
  var MyMaterial<public>: material = external {} # uses `mi_my_material_blue`
MyMeshRed := ...
MyMeshGreen := ...
MyMeshBlue := ...

# One need to update the materials individually and you have to know every concrete material type.
if (MaterialObject := mi_my_material_red[MyMeshRed.MyMaterial]) {
  set MaterialObject.Saturation = 0.0
}

if (MaterialObject := mi_my_material_green[MyMeshGreen.MyMaterial]) {
  set MaterialObject.Saturation = 0.0
}

if (MaterialObject := mi_my_material_blue[MyMeshBlue.MyMaterial]) {
  set MaterialObject.Saturation = 0.0
}

Versus the code how it should be:

# Reflected materials
m_my_material := class<public>(material):
  @editable
  var Saturation<public>: float = external {}

  @editable
  var Contrast<public>: float = external {}

mi_my_material_red := class<public>(m_my_material):

mi_my_material_green := class<public>(m_my_material):

mi_my_material_blue := class<public>(m_my_material):

# Reflected meshes
my_mesh_red := class<final><public>(mesh_component):
  @editable
  var MyMaterial<public>: material = external {} # uses `mi_my_material_red`

my_mesh_green := class<final><public>(mesh_component):
  @editable
  var MyMaterial<public>: material = external {} # uses `mi_my_material_green`

my_mesh_blue := class<final><public>(mesh_component):
  @editable
  var MyMaterial<public>: material = external {} # uses `mi_my_material_blue`
MyMeshRed := ...
MyMeshGreen := ...
MyMeshBlue := ...

# One need to update the materials individually and you have to know every concrete material type.
Materials: []material = array {
  MyMeshRed.MyMaterial,
  MyMeshGreen.MyMaterial,
  MyMeshBlue.MyMaterial
}

# Dynamically cast to `m_my_material` each material instance as in this example
# they correctly share `m_my_material` as its commons supertype.
if (Material: Materials, MaterialObject := m_my_material[Material]) {
  set MaterialObject.Saturation = 0.0
}

Since material instances can also have a parent material instance, this should also be reflected in verse as well.

The current reflection type relationship is not the same like the parent child relationship is of material and material instance assets is.

It is pretty self explanatory, is it not? Material Instances aren’t new Materials, they are just instances of their parent Material. As such, instances should reflect as subclasses of their parent material class, not just :material.

ie.
M_Prop_PARENT - parent material
MI_Prop_01, MI_Prop_02, etc… - material instances of M_Prop_PARENT applied to various meshes

UpdateMyProperty(InMaterial:material, NewValue:float):void=
   if(CastMat:=M_Prop_PARENT[InMaterial]):
      set CastMat.MyProperty = NewValue

This will only work for M_Prop_PARENT and none of its instances, even though they are all the same material.

Working with MIDs in UE is easier, as there are a couple of functions to set param values by name.

1 Like

Also honestly, it should be totally fine to create code based material subtypes of a reflected material.

mi_my_material_custom := class<public>(m_my_material):
  @editable
  var Saturation<override>: float = 0.0

What would speak against code based subtypes that override the parameters in code rather than in an asset?

The current reflection behavior and restriction has no real value to it other than creating unnecessary friction in the code base.

Thank you guys for your passion and feature requests.
We certainly will take them into consideration and I will make sure this is shared with the team.

As of right now, UEFN does not maintain parent/child relationships in our Material reflection logic to Verse. This might change in the future as we use parameter interfaces more commonly.

1 Like