Hello,
I am using MDL Material Graph (provided by omni.kit.window.material_graph
extension) and I’d like to add custom categories and extended functionalities, very similar to creating custom OmniGraph nodes.
Currently, I can see the following categories and there are many nodes provided under these categories.
I was hoping to add custom nodes to handle some of the data I have as primvars and package these custom nodes in an extension. It looks like USD provides UsdPreviewSurface
and primvar readers, but it feels it might have some advantages to use MDL and include functions in a .mdl file.
I have a very simple function in an .mdl file, like this
mdl 1.8;
import ::anno::*;
using ::scene import data_lookup_float3;
export float3
[[
annotation_port_displayName("result")
]]
myLookup_float3
(
uniform string varname = "",
float3 fallback = float3(0.0f, 0.0f, 0.0f)
)
{
return data_lookup_float3(varname, fallback);
}
This is the exact code of the float3 data lookup nodes, I changed the function name to myLookup_float3
to see if it would show up on the material graph node editor when I put it under <my_extension>/mdl
directory. Unfortunately, it didn’t.
Is there a way to add custom nodes to the material graph editor?
Thanks in advance!
Hello,
Here is the documentation:
https://docs.omniverse.nvidia.com/extensions/latest/ext_material/ext_material-graph.html#custom-user-nodes
You can add nodes and extend mdl and the material graph. In order for this to work though you have to add the path to a user allow list, which can be found in the preferences under the Edit->Preferences->Material. It takes a regex. So you would put in something like C:/my_mdl_nodes/*
I restructured your node a little to make it easier to find. It will be added the material Graph list of nodes on the left side bar, in the case under a new “My_Nodes” tab, with the name “My_Float3”. I also did a quick example of returning a default with a math::max function, though you could do this in many different ways.
mdl 1.8;
import ::anno::*;
import ::math::*;
using ::scene import data_lookup_float3;
export float3 mylookup_float3 (
uniform string varname = "",
float3 result = float3(0.0f, 0.0f, 0.0f)
)
[[
anno::display_name("My_Float3"),
anno::in_group("My_Nodes")
]]
{
result = math::max(result, data_lookup_float3(varname));
return result;
}
1 Like
Thanks for the reply @canderson.
I am looking for a way to package the .mdl files in an extension and automatically activate/deactivate when the extension is loaded/unloaded (similar to custom OmniGraph nodes). I was unable to find this information in the documentation you shared. Is there a way to achieve this?
I was able to get to some point using the following script, it can be executed via script editor:
import carb.settings
settings = carb.settings.get_settings()
search_paths = settings.get("/materialConfig/searchPaths/custom")
if not search_paths:
search_paths = []
print(search_paths)
# I have only one .mdl file in this directory: aux_analysis.mdl
search_paths.append("D:/my_ov_extensions/my.extension.shaders/mdl")
settings.set("/materialConfig/searchPaths/custom", search_paths)
user_allow = settings.get("/materialConfig/materialGraph/userAllowList")
if not user_allow:
user_allow = []
print(user_allow)
# Instead of .*, I believe it is possible to use the individual file names
user_allow.append(".*")
settings.set("/materialConfig/materialGraph/userAllowList", user_allow)
However, I am getting the following error when I open the material graph editor.
2024-09-04 03:59:42 [835,835ms] [Error] [rtx.neuraylib.plugin] [MDLC:COMPILER] 1.0 MDLC comp error: C120 could not find module '::aux_analysis' in module path
2024-09-04 03:59:42 [835,836ms] [Warning] [rtx.neuraylib.plugin] Loading MdlModule to DB (default_scope) failed: ::aux_analysis
2024-09-04 03:59:42 [835,837ms] [Warning] [rtx.neuraylib.plugin] Loading transaction committed (this thread). MdlModule is NOT in the DB (default_scope): ::aux_analysis
2024-09-04 03:59:42 [835,841ms] [Warning] [omni.kit.window.material_graph.shader_registry.mdl_registry] Pymdl could not fetch module: aux_analysis.mdl from database
The aux_analysis.mdl
contains the above node only.
The current way to register a path has a little bit more needed to it. There are 3 layers for MDL extension to work properly in OV:
import carb
mdl_folder = <YOUR EXTENTION_MDL_ABSOLUTE_PATH>
settings = carb.settings.get_settings()
materialConfig = settings.get("/materialConfig") or {}
if "searchPaths" not in materialConfig:
materialConfig["searchPaths"] = {}
# also add graph user allow list
if "materialGraph" not in materialConfig:
materialConfig["materialGraph"] = {}
# manually set allow list for user for now
materialGraphConfig = materialConfig["materialGraph"]
if "userAllowList" not in materialGraphConfig:
materialGraphConfig["userAllowList"] = []
userAllowList = materialGraphConfig.get("userAllowList", ())
# allow ALL nodes here
# this is a regex string, so try not to use too aggresive search".*"
if ".*" not in userAllowList:
ulist = list(userAllowList)
ulist.append(".*")
materialGraphConfig["userAllowList"] = tuple(ulist)
# update local path
searchPaths = materialConfig.get("searchPaths", {})
if "local" not in searchPaths:
searchPaths["local"] = []
paths = searchPaths.get("local", [])
print(f"paths: {paths}")
if mdl_folder not in paths:
paths = list(paths)
paths.append(mdl_folder.replace("\\", "/"))
paths = tuple(paths)
materialConfig["searchPaths"]["local"] = list(paths)
if "custom" not in searchPaths:
searchPaths["custom"] = []
paths = searchPaths.get("cutsom", [])
print(f"paths: {paths}")
if mdl_folder not in paths:
paths = list(paths)
paths.append(mdl_folder.replace("\\", "/"))
paths = tuple(paths)
materialConfig["searchPaths"]["custom"] = list(paths)
# now set options
if "options" not in materialConfig:
materialConfig["options"] = {}
materialConfig["options"]["noStandardPath"] = False
print(f"materialConfig: {materialConfig}")
settings.set("/materialConfig", materialConfig)
Then you need to tell neuray where to look:
current_sps = settings.get("/app/mdl/additionalUserPaths") or []
if mdl_folder not in current_sps:
current_sps.append(mdl_folder)
settings.set_string_array(
"/app/mdl/additionalUserPaths",
current_sps
)
Then finally you need to tell Kit/OV:
import omni.client
file_url = omni.client.make_file_url(mfolder_path)
omni.client.add_default_search_path(file_url)
1 Like
Thank you very much for the detailed reply @canderson. I really appreciate it.
I haven’t tested the code you shared yet and I’ll definitely do it tonight. In the meanwhile, I have a question regarding the search paths. I see that your script adds the same path to the following settings variables:
/materialConfig/searchPaths/local
/materialConfig/searchPaths/custom
If I interpret the GUI correctly, /materialConfig/searchPaths/custom
populates the box under Preferences → Material → Material Search Path → Custom Paths (this is from the documentation you shared previously).
Do we need to add the path under the extension directory to local
and custom
search path variables at the same time to make the custom .mdl files work?
The following minimal example worked for me:
import carb.settings
import omni.client
settings = carb.settings.get_settings()
mdl_dir = "D:/ov_extensions/my.extension.shaders/mdl"
mdl_file = "aux_analysis.mdl"
search_paths = settings.get("/materialConfig/searchPaths/custom")
if not search_paths:
search_paths = []
search_paths.append(mdl_dir)
settings.set("/materialConfig/searchPaths/custom", search_paths)
user_allow = settings.get("/materialConfig/materialGraph/userAllowList")
if not user_allow:
user_allow = []
user_allow.append(mdl_file)
settings.set("/materialConfig/materialGraph/userAllowList", user_allow)
addt_user_paths = settings.get("/app/mdl/additionalUserPaths")
if not addt_user_paths:
addt_user_paths = []
addt_user_paths.append(mdl_dir)
settings.set("/app/mdl/additionalUserPaths", addt_user_paths)
file_url = omni.client.make_file_url(mdl_dir)
omni.client.add_default_search_path(file_url)
Adding the path to /materialConfig/searchPaths/custom
seems enough to see the node on the material graph editor.
It definitely needs some modifications to check existing directory paths and .mdl files in the config variables. In addition, disabling the extension should remove the paths from the configuration.