Solaris does not recognize Python-made Karma materials

   1633   10   2
User Avatar
Member
12 posts
Joined: July 2020
Offline
Hello. I have a Python script [github.com] which creates materials based on input textures. It creates a regular subnetwork and inside it creates all the MTLX node connections and whatnot automatically. Apparently, Solaris does not like those materials and will not import them no matter what. I have tried doing so using the `sceneimport` LOP node and also the `materiallibrary` LOP node. They never show up in the scene graph. If I manually create Material Builders for Karma, VEX and MTLX, those will be imported correctly, even though they are also just regular subnetworks with fancy tab filters (right? They are regular?). I can't find a way of creating those builders using Python.

Another funny discovery that I've made, but this might be my misunderstanding of Solaris: I could try and create my materials inside LOPs in a materiallibrary directly, so I won't need to import them. Again, Material Builder subnets will just show up in the scene graph. My python made, or for that matter, any `regual` subnetwork materials, have to be typed in manually and THEN Solaris recognizes them (view video [youtu.be]).

So my thesis is: VEX/Karma etc. Material Builder nodes are NOT regular subnetworks and there has to be more to it. I went so far to create two materials: A "Karma Material Builder" and a regular subnetwork. Deleted all nodes from within each and copied over all the parameters from the Karma Material Builder to the subnetwork so we work with the same base. I then used this handy script [www.sidefx.com] to extract the info for each node and compared them in the hope that I would find some difference which I could use to make Solaris recognize my materials. Nope, they are both the same apart from a few small details, which can't be the reason. But the Karma Material Builder one is being recognized by Solaris while the regular subnetwork does not. I've attached those files below if anyone wants to take a look.

I just don't understand how to make Solaris NOT discriminate my materials OR how to create the material builder nodes using Python, since that would be the easiest fix.
Edited by CrisDoesCG - May 13, 2024 10:16:51

Attachments:
regular_subnetwork.py (9.0 KB)
karma_material_builder.py (8.9 KB)

User Avatar
Member
2625 posts
Joined: June 2008
Offline
Wouldn't you need to create the materials inside a material library instead of the /mat context? Perhaps alter your hou.parent path to point to a Solaris-based node?

hou_parent = hou.node("/stage/materiallibrary1")
Edited by Enivob - May 13, 2024 10:05:39
Using Houdini Indie 20.0
Windows 11 64GB Ryzen 16 core.
nVidia 3050RTX 8BG RAM.
User Avatar
Member
12 posts
Joined: July 2020
Offline
Enivob
Wouldn't you need to create the materials inside a material library instead of the /mat context? Perhaps alter your hou.parent path to point to a Solaris-based node?

hou_parent = hou.node("/stage/materiallibrary1")

It doesn't matter where the materials are located, right? The point is, that they are not being recognized, no matter if they are in /mat or in a materiallibrary. As long as they are `regular` subnetworks they won't be seen, in comparison to the material builder nodes.
User Avatar
Member
2625 posts
Joined: June 2008
Offline
I'm no Solaris expert, but I think Solaris is built on pathing, so where things are located is highly important.
I altered your code, and it worked to create a material subnet inside a material library on stage. However, the subnet was empty in both test context. Maybe Solaris isn't recognizing it because there is no actual material in the subnet.
Using Houdini Indie 20.0
Windows 11 64GB Ryzen 16 core.
nVidia 3050RTX 8BG RAM.
User Avatar
Member
12 posts
Joined: July 2020
Offline
Enivob
I'm no Solaris expert, but I think Solaris is built on pathing, so where things are located is highly important.
I altered your code, and it worked to create a material subnet inside a material library on stage. However, the subnet was empty in both test context. Maybe Solaris isn't recognizing it because there is no actual material in the subnet.

Ah, I know what you mean. So the attached files are not MY script. I don't use them for generating materials. They are the output of a script that runs the
asCode()
function over each selected node and gives you the python definition printed as a .py file. I used it to compare a Karma Material Builder node to a regular subnetwork in the hopes that I could find what is different about them, since they are both supposed to be the
node.type().name() = "subnet"
but not both are being recognized by Solaris.
Edited by CrisDoesCG - May 13, 2024 11:15:11
User Avatar
Member
162 posts
Joined: Nov. 2016
Offline
Hi!

I haven't checked your code but, maybe you can explore the voptoolutils.py (located inside the python lib on the Houdini installation folder), you will find the code that is used to create the vanilla Karma Material Builder and the others based on subnet, maybe you can compare the code to see if something is missing in your version.

For example, this is the code use to create the Karma Material Builder subnet node:

import voptoolutils
mask = voptoolutils.KARMAMTLX_TAB_MASK
voptoolutils.createMaskedMtlXSubnet(kwargs, 'karmamaterial', mask, 'Karma Material Builder', 'kma')

Of course you will need to explore the functions inside
createMaskedMtlXSubnet()
and so on.

Hope that helps,
Edited by cdordelly - May 14, 2024 19:19:59
User Avatar
Member
12 posts
Joined: July 2020
Offline
cdordelly
Hi!

I haven't checked your code but, maybe you can explore the voptoolutils.py (located inside the python lib on the Houdini installation folder), you will find the code that is used to create the vanilla Karma Material Builder and the others based on subnet, maybe you can compare the code to see if something is missing in your version.

For example, this is the code use to create the Karma Material Builder subnet node:

import voptoolutils
mask = voptoolutils.KARMAMTLX_TAB_MASK
voptoolutils.createMaskedMtlXSubnet(kwargs, 'karmamaterial', mask, 'Karma Material Builder', 'kma')

Of course you will need to explore the functions inside
createMaskedMtlXSubnet()
and so on.

Hope that helps,


Thanks Carlos!

Funny that you mention it, I have just discovered the voptoolutils.py file about two hours ago, and I have been playing around with it since then. I think I have it figured out. What I, embarrassingly, have missed, is that the render flag of the subnet has to be set to True
hou.node("/path/to/subnet").setMaterialFlag(True)
so Solaris can pick it up... could have sworn that I have tried this before and it didn't work... sigh. Maybe I didn't refresh it correctly or I should have recreated the `sceneimport` node... Whatever, the material will now show up in the scene graph... BUT it's gray, in comparison to regular Karma Material Builder nodes, which are green. No idea what that means. But this seems to be fixed if I copy over the parameters, so I just need to add some extra parameters to my material creating script and this should do it.

I'll post the code for the Karma Material Builder creation a bit later, one could just copy it from the voptoolutils.py, but its quite extensive there. I'll try to simplify it down a bit for casual use.

Case closed!
User Avatar
Member
199 posts
Joined: Nov. 2013
Offline
Regarding the color of text in the Scene Graph Tree: https://www.sidefx.com/docs/houdini/ref/panes/scenegraphtree.html#tree [www.sidefx.com]

Attachments:
SGT_legend.JPG (36.4 KB)

User Avatar
Member
12 posts
Joined: July 2020
Offline
Ok so for the solution on how to create a Karma Material Builder using Python, there are two ways:

1. Doing it yourself, something like this (sorry for the messy code):
import hou

INHERIT_PARM_EXPRESSION = '''n = hou.pwd()
n_hasFlag = n.isMaterialFlagSet()
i = n.evalParm('inherit_ctrl')
r = 'none'
if i == 1 or (n_hasFlag and i == 2):
    r = 'inherit'
return r'''

destination_path = "/PATH/TO/THE/MATERIAL/DESTINATION"
destination_node = hou.node(destination_path)

subnetNode = destination_node.createNode("subnet","NAME_OF_YOUR_MATERIAL")
subnetNode.moveToGoodPosition()
subnetNode.setMaterialFlag(True)                  

parameters = subnetNode.parmTemplateGroup()

newParm_hidingFolder = hou.FolderParmTemplate("mtlxBuilder","MaterialX Builder",folder_type=hou.folderType.Collapsible)
control_parm_pt = hou.IntParmTemplate('inherit_ctrl','Inherit from Class', 
                    num_components=1, default_value=(2,), 
                    menu_items=(['0','1','2']),
                    menu_labels=(['Never','Always','Material Flag']))


newParam_tabMenu = hou.StringParmTemplate("tabmenumask", "Tab Menu Mask", 1, default_value=["MaterialX parameter constant collect null genericshader subnet subnetconnector suboutput subinput"])
class_path_pt = hou.properties.parmTemplate('vopui', 'shader_referencetype')
class_path_pt.setLabel('Class Arc')
class_path_pt.setDefaultExpressionLanguage((hou.scriptLanguage.Python,))
class_path_pt.setDefaultExpression((INHERIT_PARM_EXPRESSION,))   

ref_type_pt = hou.properties.parmTemplate('vopui', 'shader_baseprimpath')
ref_type_pt.setDefaultValue(['/__class_mtl__/`$OS`'])
ref_type_pt.setLabel('Class Prim Path')               

newParm_hidingFolder.addParmTemplate(newParam_tabMenu)
newParm_hidingFolder.addParmTemplate(control_parm_pt)  
newParm_hidingFolder.addParmTemplate(class_path_pt)    
newParm_hidingFolder.addParmTemplate(ref_type_pt)             

parameters.append(newParm_hidingFolder)
subnetNode.setParmTemplateGroup(parameters)

You will obviously need to delete the nodes that are inside the network, create the outputs and the necessary nodes. But this gives at least a base that will be recognized by Solaris when trying to import it via `sceneimport` node. Also this will give you more control in comparison to



2. Following this video [youtu.be], this works inside a LOP network, but won't work inside any other regual material network. Here is the code:
import hou
import voptoolutils

mask = voptoolutils.KARMAMTLX_TAB_MASK

viewer = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)

kwargs = {
    "pane":viewer,
    "autoplace":True
}

voptoolutils.createMaskedMtlxSubnet(kwargs,"karmamaterial",mask,"Karma Material Builder", "kma")

Thanks to u/EconomyAppeal1106 from this thread [www.reddit.com] who pointed me to their video.
Edited by CrisDoesCG - May 15, 2024 05:24:30
User Avatar
Member
1 posts
Joined: March 2022
Offline
CrisDoesCG
Ok so for the solution on how to create a Karma Material Builder using Python, there are two ways:

1. Doing it yourself, something like this (sorry for the messy code):
import hou

INHERIT_PARM_EXPRESSION = '''n = hou.pwd()
n_hasFlag = n.isMaterialFlagSet()
i = n.evalParm('inherit_ctrl')
r = 'none'
if i == 1 or (n_hasFlag and i == 2):
    r = 'inherit'
return r'''

destination_path = "/PATH/TO/THE/MATERIAL/DESTINATION"
destination_node = hou.node(destination_path)

subnetNode = destination_node.createNode("subnet","NAME_OF_YOUR_MATERIAL")
subnetNode.moveToGoodPosition()
subnetNode.setMaterialFlag(True)                  

parameters = subnetNode.parmTemplateGroup()

newParm_hidingFolder = hou.FolderParmTemplate("mtlxBuilder","MaterialX Builder",folder_type=hou.folderType.Collapsible)
control_parm_pt = hou.IntParmTemplate('inherit_ctrl','Inherit from Class', 
                    num_components=1, default_value=(2,), 
                    menu_items=(['0','1','2']),
                    menu_labels=(['Never','Always','Material Flag']))


newParam_tabMenu = hou.StringParmTemplate("tabmenumask", "Tab Menu Mask", 1, default_value=["MaterialX parameter constant collect null genericshader subnet subnetconnector suboutput subinput"])
class_path_pt = hou.properties.parmTemplate('vopui', 'shader_referencetype')
class_path_pt.setLabel('Class Arc')
class_path_pt.setDefaultExpressionLanguage((hou.scriptLanguage.Python,))
class_path_pt.setDefaultExpression((INHERIT_PARM_EXPRESSION,))   

ref_type_pt = hou.properties.parmTemplate('vopui', 'shader_baseprimpath')
ref_type_pt.setDefaultValue(['/__class_mtl__/`$OS`'])
ref_type_pt.setLabel('Class Prim Path')               

newParm_hidingFolder.addParmTemplate(newParam_tabMenu)
newParm_hidingFolder.addParmTemplate(control_parm_pt)  
newParm_hidingFolder.addParmTemplate(class_path_pt)    
newParm_hidingFolder.addParmTemplate(ref_type_pt)             

parameters.append(newParm_hidingFolder)
subnetNode.setParmTemplateGroup(parameters)

You will obviously need to delete the nodes that are inside the network, create the outputs and the necessary nodes. But this gives at least a base that will be recognized by Solaris when trying to import it via `sceneimport` node. Also this will give you more control in comparison to



2. Following this video [youtu.be], this works inside a LOP network, but won't work inside any other regual material network. Here is the code:
import hou
import voptoolutils

mask = voptoolutils.KARMAMTLX_TAB_MASK

viewer = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)

kwargs = {
    "pane":viewer,
    "autoplace":True
}

voptoolutils.createMaskedMtlxSubnet(kwargs,"karmamaterial",mask,"Karma Material Builder", "kma")

Thanks to u/EconomyAppeal1106 from this thread [www.reddit.com] who pointed me to their video.


Hi, you can try using the _setupMtlXBuilderSubnet function to configure the node in your specific target path in the LOP, it also works.

import hou
import voptoolutils

mask = voptoolutils.KARMAMTLX_TAB_MASK

sel = hou.selectedNodes()[0]
root = sel.parent()

materialLibrary = sel.createOutputNode("materiallibrary", "MaterialLibrary")
kmaPath = materialLibrary.createNode("subnet", "karmamaterial")
voptoolutils._setupMtlXBuilderSubnet(kmaPath, "karmamaterial", "karmamaterial", mask, "Karma Material Builder", "kma")
User Avatar
Member
18 posts
Joined: Sept. 2013
Offline
You could also us the _setupMtlXBuilderSubnet function from voptoolutils. set the subnet_node to None and give it a destination_node path.

import voptoolutils

# specify the matnet you want to create the karma builder node in
dst_node = hou.node('/obj/geo1/lopnet1/matnet1')

subnet_node = None
name = 'karmamaterial'
mask = voptoolutils.KARMAMTLX_TAB_MASK
folder_label= 'Karma Material Builder'
render_context = "kma"

mat_builder = voptoolutils._setupMtlXBuilderSubnet(subnet_node=subnet_node, destination_node=dst_node, name=name, mask=mask, folder_label=folder_label, render_context=render_context)
Edited by Petfactory - Sept. 2, 2024 06:41:53
  • Quick Links