Atom Posted July 18, 2017 Share Posted July 18, 2017 Hi All, I put together a small shelf tool that populates a subnet with an object for each .bgeo.sc file found in a supplied folder path. The end goal was to leverage s@instance for file based objects. s@instance can only reference objects, not disk files. In this hard coded example file I am fetching 144 debris shards into 144 geometry objects which instancing can reference. # Populates an existing subnet with a geometry node for each file found in the supplied folder. # The File node inside the default geometry node is populated with the filename from the folder. # The end result is you get an object for each disk object in a folder. import os, re def returnValidHoudiniNodeName(passedItem): # Thanks to Graham on OdForce for this function! # Replace any illegal characters for node names here. return re.sub("[^0-9a-zA-Z\.]+", "_", passedItem) def returnFilesLike(passedFolderName, passedFileExtension = ".obj"): result = [] for file in os.listdir(passedFolderName): if file.endswith(passedFileExtension): result.append(os.path.join(passedFolderName,file)) return result def createSubnetOfFileNodes(passedPath, passedType): root = hou.node("/obj/geo_debris_container") # Make sure it exists before running, choose your own name. if root != None: lst_files = returnFilesLike(passedPath, passedType) if len(lst_files): for i,file in enumerate(lst_files): # Create a file node for each geometry in our list. local_name = os.path.basename(file) local_name_only = os.path.splitext(local_name)[0] geo_name = returnValidHoudiniNodeName("geo_%s" % local_name_only) geo_name = geo_name.replace(".","_") node_geo = root.createNode("geo", geo_name) if node_geo: # The first child is a default File node. n = node_geo.children()[0] if n: # Assign this filename as the filepath for this file node. n.parm("file").set(file) else: print "No subnet container found to contain generated results." createSubnetOfFileNodes ("F:/Keep/Models/Rocks/debris_sharp",".bgeo.sc") Quote Link to comment Share on other sites More sharing options...
Stalkerx777 Posted July 20, 2017 Share Posted July 20, 2017 Why not using s@instancefile attribute? Quote Link to comment Share on other sites More sharing options...
Atom Posted July 21, 2017 Author Share Posted July 21, 2017 (edited) Hmm..never heard of that attribute. Do you know how to use it? I have a working instance that is successfully instancing objects from within the scene using s@instance. When I change to s@instancefile and reference valid geometry path on my disk I get no output at all? I tried both full and fast point instancing. ap_instancefile.hipnc Edited July 21, 2017 by Atom Quote Link to comment Share on other sites More sharing options...
Stalkerx777 Posted July 22, 2017 Share Posted July 22, 2017 It's been awhile since I did instancing in Houdini last time, but it was working back then. Maybe there something else you have to toggle on. 1 Quote Link to comment Share on other sites More sharing options...
Atom Posted November 14, 2017 Author Share Posted November 14, 2017 (edited) After exploring @instancefile a bit more I discovered that it is useful for instancing Redshift proxies directly from the disk. The only problem is it can be tedious to setup a .rs proxy file for each standard .obj or .bgeo.sc file that you might already have located somewhere on a disk. This script will scan a folder on the disk and make an object in a subnet for each object it finds in the folder. The code also installs a Python node after the File node that runs code to scan the imported geometry looking for unique @shop_materialpath. The code then creates a unique material named like what the geometry references. These materials are created in the Redshift format, but a Mantra implementation should not be the hard to do as well. The result is that a default material with a random color will exist in the scene for every primitive that references a unique material name. The objects are stored inside the subnet in a sequential fashion named like the folder they reside within. EXAMPLE: Folder is named trees, objects inside the subnet will be named geo_trees_01, geo_trees_02 etc... This allows for easy proxy output from the /out context using the standard Geometry node or the Redshift Proxy Output node. Place this code in a shelf tool button and alter the path to point to your folder of .obj files (debris, grass, trees, rocks etc..) # Populates an existing subnet with a geometry node for each file found in the supplied folder. # The File node inside the default geometry node is populated with the filename from the folder. # The end result is you get an object for each disk object in a folder. import os, re def code (): """ import random node = hou.pwd() geo = node.geometry() lst_materials = [] # Scan primitives for material assignments. for prim in geo.prims(): s = prim.attribValue("shop_materialpath") if s not in lst_materials: lst_materials.append(s) # Create materials if they do not exist. shop_path = "/shop" for (i,s) in enumerate(lst_materials): n = hou.node(s) if n == None: # Material missing, need to create. ary = s.split("/") l = len(ary)-1 rs_vop = hou.node(shop_path).createNode("redshift_vopnet",ary[l]) if rs_vop != None: # Detect the default closure node that should be created by the redshift_vopnet. rs_output = hou.node("%s/%s/redshift_material1" % (shop_path, ary[l])) if rs_output != None: # Create. rs_mat = rs_vop.createNode("redshift::Material","rs_Mat") if rs_mat != None: # Assign a random color. random.seed(i) rs_mat.parm("diffuse_colorr").set(random.random()) rs_mat.parm("diffuse_colorg").set(random.random()) rs_mat.parm("diffuse_colorb").set(random.random()) # Wire to closure. rs_output.setInput(0,rs_mat) # Layout. rs_vop.moveToGoodPosition() rs_mat.moveToGoodPosition() rs_output.moveToGoodPosition() else: print "No mat." else: print "No closure." else: print "No VOP." else: print "Skipping [%s], it already exists." % s """ def returnValidHoudiniNodeName(passedItem): # Thanks to Graham on OdForce for this function! # Replace any illegal characters for node names here. return re.sub("[^0-9a-zA-Z\.]+", "_", passedItem) def returnFilesLike(passedFolderName, passedFileExtension = ".obj"): result = [] for file in os.listdir(passedFolderName): if file.endswith(passedFileExtension): result.append(os.path.join(passedFolderName,file)) return result def createSubnetOfFileNodes(passedPath, passedType): root = hou.node("/obj/folder_of_models") # Make sure it exists before running, choose your own name. if root == None: # Create new object to contain internal geometry objects. root = hou.node('/obj').createNode('subnet', "folder_of_models") if root != None: lst_files = returnFilesLike(passedPath, passedType) if len(lst_files): for i,file in enumerate(lst_files): # Create a file node for each geometry in our list. path = os.path.split(file)[0] last_folder = os.path.split(path)[1] node_name = "%s_%03d" % (last_folder,i) geo_name = returnValidHoudiniNodeName("geo_%s" % node_name) geo_name = geo_name.replace(".","_") node_geo = root.createNode("geo", geo_name) if node_geo: node_geo.moveToGoodPosition() # The first child is a default File node. n = node_geo.children()[0] if n: # Assign this filename as the filepath for this file node. n.parm("file").set(file) # Create a python node. node_python = node_geo.createNode("python") if node_python != None: # Install code that will detect and create materials from shop_materialpath. node_python.parm("python").set(code.__doc__) node_python.setInput(0,n) node_python.setDisplayFlag(True) n.moveToGoodPosition() node_python.moveToGoodPosition() else: print "No subnet container found to contain generated results." folder_path = r"D:\Documents\Models\Plants\Bushes\bushes" createSubnetOfFileNodes (folder_path,".obj") Edited November 14, 2017 by Atom 1 1 Quote Link to comment Share on other sites More sharing options...
Diorn Posted November 20, 2020 Share Posted November 20, 2020 On 11/14/2017 at 7:22 PM, Atom said: After exploring @instancefile a bit more I discovered that it is useful for instancing Redshift proxies directly from the disk. The only problem is it can be tedious to setup a .rs proxy file for each standard .obj or .bgeo.sc file that you might already have located somewhere on a disk. This script will scan a folder on the disk and make an object in a subnet for each object it finds in the folder. The code also installs a Python node after the File node that runs code to scan the imported geometry looking for unique @shop_materialpath. The code then creates a unique material named like what the geometry references. These materials are created in the Redshift format, but a Mantra implementation should not be the hard to do as well. The result is that a default material with a random color will exist in the scene for every primitive that references a unique material name. The objects are stored inside the subnet in a sequential fashion named like the folder they reside within. EXAMPLE: Folder is named trees, objects inside the subnet will be named geo_trees_01, geo_trees_02 etc... This allows for easy proxy output from the /out context using the standard Geometry node or the Redshift Proxy Output node. Place this code in a shelf tool button and alter the path to point to your folder of .obj files (debris, grass, trees, rocks etc..) # Populates an existing subnet with a geometry node for each file found in the supplied folder. # The File node inside the default geometry node is populated with the filename from the folder. # The end result is you get an object for each disk object in a folder. import os, re def code (): """ import random node = hou.pwd() geo = node.geometry() lst_materials = [] # Scan primitives for material assignments. for prim in geo.prims(): s = prim.attribValue("shop_materialpath") if s not in lst_materials: lst_materials.append(s) # Create materials if they do not exist. shop_path = "/shop" for (i,s) in enumerate(lst_materials): n = hou.node(s) if n == None: # Material missing, need to create. ary = s.split("/") l = len(ary)-1 rs_vop = hou.node(shop_path).createNode("redshift_vopnet",ary[l]) if rs_vop != None: # Detect the default closure node that should be created by the redshift_vopnet. rs_output = hou.node("%s/%s/redshift_material1" % (shop_path, ary[l])) if rs_output != None: # Create. rs_mat = rs_vop.createNode("redshift::Material","rs_Mat") if rs_mat != None: # Assign a random color. random.seed(i) rs_mat.parm("diffuse_colorr").set(random.random()) rs_mat.parm("diffuse_colorg").set(random.random()) rs_mat.parm("diffuse_colorb").set(random.random()) # Wire to closure. rs_output.setInput(0,rs_mat) # Layout. rs_vop.moveToGoodPosition() rs_mat.moveToGoodPosition() rs_output.moveToGoodPosition() else: print "No mat." else: print "No closure." else: print "No VOP." else: print "Skipping [%s], it already exists." % s """ def returnValidHoudiniNodeName(passedItem): # Thanks to Graham on OdForce for this function! # Replace any illegal characters for node names here. return re.sub("[^0-9a-zA-Z\.]+", "_", passedItem) def returnFilesLike(passedFolderName, passedFileExtension = ".obj"): result = [] for file in os.listdir(passedFolderName): if file.endswith(passedFileExtension): result.append(os.path.join(passedFolderName,file)) return result def createSubnetOfFileNodes(passedPath, passedType): root = hou.node("/obj/folder_of_models") # Make sure it exists before running, choose your own name. if root == None: # Create new object to contain internal geometry objects. root = hou.node('/obj').createNode('subnet', "folder_of_models") if root != None: lst_files = returnFilesLike(passedPath, passedType) if len(lst_files): for i,file in enumerate(lst_files): # Create a file node for each geometry in our list. path = os.path.split(file)[0] last_folder = os.path.split(path)[1] node_name = "%s_%03d" % (last_folder,i) geo_name = returnValidHoudiniNodeName("geo_%s" % node_name) geo_name = geo_name.replace(".","_") node_geo = root.createNode("geo", geo_name) if node_geo: node_geo.moveToGoodPosition() # The first child is a default File node. n = node_geo.children()[0] if n: # Assign this filename as the filepath for this file node. n.parm("file").set(file) # Create a python node. node_python = node_geo.createNode("python") if node_python != None: # Install code that will detect and create materials from shop_materialpath. node_python.parm("python").set(code.__doc__) node_python.setInput(0,n) node_python.setDisplayFlag(True) n.moveToGoodPosition() node_python.moveToGoodPosition() else: print "No subnet container found to contain generated results." folder_path = r"D:\Documents\Models\Plants\Bushes\bushes" createSubnetOfFileNodes (folder_path,".obj") Just wanted to thank you for this very clever approach to making a tool 1 Quote Link to comment Share on other sites More sharing options...
Diorn Posted November 20, 2020 Share Posted November 20, 2020 Trying to get this tool working on H18.5, would really need it for my current project with lots of imports. Unfortunately i'm stuck with this error: Traceback (most recent call last): File "geofolder-to-RsProxy-Import", line 108, in <module> File "geofolder-to-RsProxy-Import", line 91, in createSubnetOfFileNodes IndexError: tuple index out of range Any idea how i might fix this? Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.