Jump to content


Popular Content

Showing most liked content on 09/30/2020 in all areas

  1. 1 point
    I created a short python script to help out with the excessive nodes that are presented when importing detailed FBX files. My example is a vehicle with 278 object nodes referencing 37 materials inside the materials subnet generated by the import. This script scans the materials subnet and creates a new object level geo for each material it finds. Inside this new geo node it creates an ObjectMerge node and populates the node with all object references to the FBX material. It assigns the new material to this new geo node that points to /shop instead of the FBX materials subnet. Then it reviews the FBX materials and creates a new Redshift material for each FBX material detected. It scans the FBX Surface node and extracts a few parameters like diffuse color, specular etc... The net result is that I only have 37 nodes to manage instead of 278 after running the script. Also my nodes have Redshift placeholder materials assigned so I can get right to rendering. Add this code to a new shelf button and adjust the paths, at the bottom of the script, to point to your FBX subnet. The texture path is not really used at this time. # Scan a FBX subnet for materials. # Create a geo object with an object merge for each object that references the material. # Create a place holder Redshift material by reviewing the FBX materials in the subnet. # Atom 08-22-2018 # 10-14-2018 import hou, 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 createRedshiftImageMapMaterial(passedSHOP, passedImageFilePath, passedName, passedDiffuse=[0,0,0], passedSpecular=[0,0,0], passedWeight=0.1, passedRoughness=0.23, passedIOR=1.0, passedOpacity=1.0): #print "->%s [%s] [%s]" % (passedSHOP, passedImageFilePath, passedName) rs_vop = hou.node(passedSHOP).createNode("redshift_vopnet",passedName) if rs_vop != None: rs_output = hou.node("%s/%s/redshift_material1" % (passedSHOP, passedName)) # Detect the default closure node that should be created by the redshift_vopnet. if rs_output != None: # Create. rs_mat = rs_vop.createNode("redshift::Material","rs_Mat") if rs_mat != None: # Set passed values. rs_mat.parm("diffuse_colorr").set(passedDiffuse[0]) rs_mat.parm("diffuse_colorg").set(passedDiffuse[1]) rs_mat.parm("diffuse_colorb").set(passedDiffuse[2]) rs_mat.parm("refl_colorr").set(passedSpecular[0]) rs_mat.parm("refl_colorg").set(passedSpecular[1]) rs_mat.parm("refl_colorb").set(passedSpecular[2]) rs_mat.parm("refl_weight").set(passedWeight) rs_mat.parm("refl_roughness").set(passedRoughness) if passedIOR ==0: # A zero based IOR means activate mirror mode for the reflection section. rs_mat.parm("refl_fresnel_mode").set("1") rs_mat.parm("refl_brdf").set("1") rs_mat.parm("refl_reflectivityr").set(0.961998) rs_mat.parm("refl_reflectivityg").set(0.949468) rs_mat.parm("refl_reflectivityb").set(0.91724) rs_mat.parm("refl_edge_tintr").set(0.998643) rs_mat.parm("refl_edge_tintg").set(0.998454) rs_mat.parm("refl_edge_tintb").set(0.998008) rs_mat.parm("refl_samples").set(128) rs_mat.parm("diffuse_weight").set(0) else: rs_mat.parm("refl_ior").set(passedIOR) rs_mat.parm("opacity_colorr").set(passedOpacity) rs_mat.parm("opacity_colorg").set(passedOpacity) rs_mat.parm("opacity_colorb").set(passedOpacity) rs_tex = rs_vop.createNode("redshift::TextureSampler",returnValidHoudiniNodeName("rs_Tex_%s" % passedName)) if rs_tex != None: # Wire try: rs_output.setInput(0,rs_mat) can_continue = True except: can_continue = False if can_continue: if passedImageFilePath.find("NOT_DETECTED")==-1: # Only plug in texture if the texture map was specified. rs_mat.setInput(0,rs_tex) # input #0 is diffuse color. extension = os.path.splitext(passedImageFilePath)[1] files_with_alphas = [".png",".PNG",".tga",".TGA",".tif",".TIF",".tiff",".TIFF",".exr",".EXR"] if extension in files_with_alphas: # Place a sprite after the rsMaterial to implment opacity support. rs_sprite = rs_vop.createNode("redshift::Sprite",returnValidHoudiniNodeName("rs_Sprite_%s" % passedName)) if rs_sprite != None: rs_sprite.parm("tex0").set(passedImageFilePath) # set the filename to the texture. rs_sprite.parm("mode").set("1") rs_sprite.setInput(0,rs_mat) rs_output.setInput(0,rs_sprite) #rs_mat.setInput(46,rs_tex) # input #46 is opacity color (i.e. alpha). rs_tex.parm("tex0").set(passedImageFilePath) # set the filename to the texture. # Remove luminosity from texture using a color corrector. rs_cc = rs_vop.createNode("redshift::RSColorCorrection",returnValidHoudiniNodeName("rs_CC_%s" % passedName)) if rs_cc != None: rs_cc.setInput(0,rs_tex) rs_cc.parm("saturation").set(0) # Add a slight bump using the greyscale value of the diffuse texture. rs_bump = rs_vop.createNode("redshift::BumpMap",returnValidHoudiniNodeName("rs_Bump_%s" % passedName)) if rs_bump != None: rs_bump.setInput(0,rs_cc) rs_bump.parm("scale").set(0.25) # Hard coded, feel free to adjust. rs_output.setInput(2,rs_bump) # Layout. rs_vop.moveToGoodPosition() rs_tex.moveToGoodPosition() rs_cc.moveToGoodPosition() rs_bump.moveToGoodPosition() rs_mat.moveToGoodPosition() rs_output.moveToGoodPosition() else: print "problem creating redshift::TextureSampler node." else: print "problem creating redshift::Material node." else: print "problem detecting redshift_material1 automatic closure." else: print "problem creating redshift vop net?" def childrenOfNode(node, filter): # Return nodes of type matching the filter (i.e. geo etc...). result = [] if node != None: for n in node.children(): t = str(n.type()) if t != None: for filter_item in filter: if (t.find(filter_item) != -1): # Filter nodes based upon passed list of strings. result.append((n.name(), t)) result += childrenOfNode(n, filter) return result def groupByFBXMaterials(node_path, rewrite_original=False): lst_geo_objs = [] lst_fbx_mats = [] s = "" material_nodes = childrenOfNode(hou.node("%s/materials" % node_path),["Shop material"]) #Other valid filters are Sop, Object, cam. for (name, type) in material_nodes: node_candidate = "%s/%s" % ("%s/materials" % node_path, name) n = hou.node(node_candidate) if n !=None: lst_fbx_mats.append(node_candidate) object_nodes = childrenOfNode(hou.node(node_path),["Object geo"]) #Other valid filters are Sop, Object, cam. for (name, type) in object_nodes: node_candidate = "%s/%s" % (node_path, name) n = hou.node(node_candidate) if n !=None: lst_geo_objs.append(node_candidate) # Make an object geo node for each material detected. # Inside the object will reside an object merge to fetch in each object that references the material. root = hou.node("/obj") if root != None: for mat in lst_fbx_mats: mat_name = os.path.basename(mat) shader_name = "rs_%s" % mat_name geo_name = "geo_%s" % mat_name ''' node_geo = root.createNode("geo", geo_name) if node_geo: # Delete the default File node that is automatically created as well. if (len(node_geo.children())) > 0: n = node_geo.children()[0] if n: n.destroy() node_geo.parm("shop_materialpath").set("/shop/%s" % shader_name) node_obm = node_geo.createNode("object_merge","object_merge1") if node_obm != None: p = node_obm.parm("objpath1") all_obj = "" for obj in lst_geo_objs: temp_node = hou.node(obj) if temp_node != None: smp = temp_node.parm("shop_materialpath").eval() if smp.find(mat_name) != -1: all_obj += "%s " % obj p.set(all_obj) node_obm.parm("xformtype").set(1) ''' # Make a place holder Redshift material by reviewing the FBX material. opacity = 1.0 ior = 1.025 reflection_weight = 0.1 reflection_roughness = 0.23 diffuse_color = [0,0,0] specular_color = [0,0,0] # Typically the FBX Surface Shader is the second node created in the FBX materials subnet. n = hou.node(mat).children()[1] if n != None: r = n.parm("Cdr").eval() g = n.parm("Cdg").eval() b = n.parm("Cdb").eval() diffuse_color = [r,g,b] sm = n.parm("specular_mult").eval() if sm > 1.0: sm = 1.0 reflection_weight = 1.0-sm if (sm==0) and (n.parm("Car").eval()+n.parm("Cdr").eval()==2): # Mirrors should use another Fresnel type. ior=0 r = n.parm("Csr").eval() g = n.parm("Csg").eval() b = n.parm("Csb").eval() specular_color = [r,g,b] opacity = n.parm("opacity_mult").eval() reflection_roughness = n.parm("shininess").eval()*0.01 em = n.parm("emission_mult").eval() if em > 0: # We should create an rsIncandescent shader, using this color, instead. r = n.parm("Cer").eval() g = n.parm("Ceg").eval() b = n.parm("Ceb").eval() # Try to fetch the diffuse image map, if any. tex_map = n.parm("map1").rawValue() if len(tex_map) > 0: pass else: tex_map = "%s/%s" % (texture_path,"NOT_DETECTED") createRedshiftImageMapMaterial("/shop", tex_map, shader_name, diffuse_color, specular_color, reflection_weight, reflection_roughness, ior, opacity) if rewrite_original: # Re-write the original object node's material reference to point to the Redshift material. for obj in lst_geo_objs: node_geo = hou.node(obj) if node_geo: m = node_geo.parm("shop_materialpath").eval() if len(m): mat_name = os.path.basename(m) shader_name = "/shop/rs_%s" % mat_name # To do this right, we need to add a material node to the end of the network and populate it with the shop_materialpath value. node_display = node_geo.displayNode() if node_display != None: node_mat = node_geo.createNode("material","material1") # Create new node. if node_mat != None: node_mat.parm("shop_materialpath1").set(shader_name) node_mat.setInput(0,node_display) # Wire it into the network. node_mat.setDisplayFlag(True) # Move the display flag to the new node. node_mat.setRenderFlag(True) # Move the render flag to the new node. node_mat.moveToGoodPosition() # Program starts here. texture_path = '/media/banedesh/Storage/Documents/Models/Ford/Ford_F-150_Raptor_2017_crewcab_fbx' #Not really used yet. fbx_subnet_path = "/obj/Container_Ship_Generic_FBX" groupByFBXMaterials(fbx_subnet_path, True)
  2. 1 point
    Hi Everyone, I "expanded" and changed the solution by Atom and Delda a bit The script now expect you to select the SHOP network node created by the fbx importer. It then creates MAT network at obj level with the same name as the selected SHOP network node and then I use ATOM function to create the redshift shaders inside it. I puposely do not append the 'rs_' to the name of the material so it is easy to replace the original material by simply changing base directory of where the object is searching for the materials. Basically the same materials name will reside in a different container. The reason why I took out the part of the script that appended the new materials to the object was because I thought that as it was written it may have problem with the structure in which my FBX where built. Basically multiple materials adresses inside a single material SOP. Anyway, As @Atom script was very helpful I thought in adding my bit by sharing the adaptation here and hoping the community find it useful also. Nico. fbxMat2RSMat.py
  3. 1 point
    Hey, I just wanted to add to this for those looking at this type of thing. For this to work correctly the setup is specific. Using wrangles is the easiest. Lets say there are 2 objects. A falling box and a floor sitting horizontally. Both of these need to have a "collisiongroup" assigned to them. So for this we will call them box1 and floor1. For box1 to ignore collision with floor1, both object streams need to have "collisionignore" specified to them. So more clearly, box1 would need collisionignore to be set to floor1 and floor1 would need to be set to box1. This way both objects are aware of the other. This is based on a setup where these objects are both being piped into a single rbdpackedobject node in the DOP. If box1 is set to floor1 but floor one has no setting for any collisonignore objects. The default will be a collision. A bit involved but it worked for me. s@collisiongroup = "floor1"; s@collisionignore = "box1"; s@collisiongroup = "box1"; s@collisionignore = "floor1"; The syntax for the objects added is similar to the syntax in the group field of most nodes. So space to separate multiple objects and "^" to exclude objects. Even wildcards will work, "box*". The docs describe this but until try to set it up you discover all of this. I hope this helps the next person.
  4. 1 point
    As usual, there are many ways of doing this. One would be setting the collisionignore with a wrangle. The collisionignore is a string attribute and works the same way most of Houdinis include/exclude with "*", "* ^except", "only". So for your example, just group the objects you want to ignore the collision and assign the collisionignore attribute with the desired value(for example "*" for ignoring all collisions). If you want to only exlude specific objects you have to use the DOP objects name. Quick example: if (@group_nocollide == 1) { s@collisionignore = "*"; } Take a look at the attached file. There is a simple setup showing what I mean. Hope that helps. Dennis collisionignore_setup_v01.hiplc
  5. 1 point
    Hi Saugaro, I answered your post lightly in the cgalk forums, but I'll continue here... The procedural model does in no way inhibit organic modeling. There are great tools for it, in fact - paint/sculpt/comb brush tools (like Artisan) and Edit operations with allow all manners of deformations to be rolled up into one node. The Edit tools (and several others) has an amazing "soft" mode which allows edits with a soft falloff. So, in short - the procedural model only helps, even with organic modeling. You'll find yourself moving up the node tree and doing a lot of fixes upstream in organic modeling too. Things can get tricky if you start blasting away polygons upstream that are depended on downstream, but this just errors those operations (doesn't blow them away) and you can easily shoot back downstream and reselect the new polys for those operations. There is a subdivide operation to subdivide your models by a specified number of times and there are neat techniques to modeling on the cage and watching the subd surface update - (look at the Template flag). Houdini itself doesn't support subd surfaces as a primitive type - i.e. it doesnt represent the limit surface in the viewer. Mantra (the renderer) does support subd natively though. Houdini will pass it the cage and mantra will give you the limit (infinitely subdivided) surface. In modeling? The UV tools in Houdini are (IMHO) better than mayas and it would take mere minutes to get good UVs out of Houdini for fairly complex models, but the pure modeling process will vary depending on your task, I reckon. Maya is probably quicker for doing operations to multiple objects simulataneously. Modeling a hero piece of geometry is probably very similar. Nope. But you can set up fur rendering - look in the Rendering forum. I hope this helps.. Jason.