Jump to content
Sign in to follow this  
Atom

A Geometry Object For Each File On Disk

Recommended Posts

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")

 

Share this post


Link to post
Share on other sites

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.

Untitled-1.jpg

ap_instancefile.hipnc

Edited by Atom

Share this post


Link to post
Share on other sites

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.

  • Like 1

Share this post


Link to post
Share on other sites

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.

Untitled-1.jpg.7bf3eb9d11ae5616b20e55000740038d.jpg

 

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 by Atom

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×