Was able to remedy this for now with a temporary python script added to the projects init_unreal.py. The MI overrides don’t work because the Toon Profile assigned to the Master Material is not being loaded at all on startup. The script essentially makes the dummy changes and then clears the dirty flags.
You need to enable Unreal’s Python Editor Script Plugin, Add your actual master material paths to the top and paste the script into your init_unreal file (YourProject/Content/Python/init_unreal.py) or create one there if you don’t have.
Script:
import unreal
TOON_PROFILE_REFRESH_MATERIALS = (
“/Game/Path/To/M_YourMasterMaterial”,
“/Game/Path/To/M_AnotherMasterMaterial”,
“/Game/Path/To/M_AdditionalMasterMaterial”,
)
TOON_PROFILE_REFRESH_DELAY_TICKS = 180
_toon_profile_refresh_tick_count = 0
_toon_profile_refresh_tick_handle = None
def _refresh_material_toon_profile(material_path):
material = unreal.load_asset(material_path)
if material is None:
unreal.log_warning(
"[ToonProfileRefresh] Material not found: {}".format(material_path)
)
return False
toon_nodes = [
expression
for expression in unreal.MaterialEditingLibrary.get_material_expressions(
material
)
if isinstance(
expression,
unreal.MaterialExpressionSubstrateToonBSDF
)
]
node_profiles = [
(node, node.get_editor_property("toon_profile"))
for node in toon_nodes
if node.get_editor_property("toon_profile")
]
if not node_profiles:
unreal.log_warning(
"[ToonProfileRefresh] No assigned Toon Profile found in {}".format(
material_path
)
)
return False
# Clear the Toon Profiles and compile once.
try:
for node, _profile in node_profiles:
node.set_editor_property("toon_profile", None)
unreal.MaterialEditingLibrary.recompile_material(material)
finally:
# Always restore the original Toon Profiles.
for node, profile in node_profiles:
node.set_editor_property("toon_profile", profile)
# Compile again with the restored profiles.
unreal.MaterialEditingLibrary.recompile_material(material)
# The material finishes in its original state, so remove the artificial
# dirty flag without saving the asset.
editor_assets = unreal.get_editor_subsystem(
unreal.EditorAssetSubsystem
)
editor_assets.set_dirty_flag(material, False)
unreal.log(
"[ToonProfileRefresh] Refreshed {} Toon node(s) in {}".format(
len(node_profiles),
material_path
)
)
return True
def _run_toon_profile_startup_refresh():
refreshed_count = 0
for material_path in TOON_PROFILE_REFRESH_MATERIALS:
try:
refreshed_count += int(
_refresh_material_toon_profile(material_path)
)
except Exception as error:
unreal.log_error(
"[ToonProfileRefresh] Failed for {}: {}".format(
material_path,
error
)
)
unreal.log(
"[ToonProfileRefresh] Complete: {}/{} materials".format(
refreshed_count,
len(TOON_PROFILE_REFRESH_MATERIALS)
)
)
def _on_toon_profile_startup_tick(_delta_seconds):
global _toon_profile_refresh_tick_count
global _toon_profile_refresh_tick_handle
_toon_profile_refresh_tick_count += 1
if _toon_profile_refresh_tick_count < TOON_PROFILE_REFRESH_DELAY_TICKS:
return
unreal.unregister_slate_post_tick_callback(
_toon_profile_refresh_tick_handle
)
_toon_profile_refresh_tick_handle = None
_run_toon_profile_startup_refresh()
_toon_profile_refresh_tick_handle = unreal.register_slate_post_tick_callback(
_on_toon_profile_startup_tick
)
unreal.log(“[ToonProfileRefresh] Scheduled startup refresh”)