github.com/Calvinatorr/CommercialHDAs/blob/master/FBX_sops.hda
To install just point your houdini.env file to the hda/folder of your hdas, or use the install hda feature.
I developed this tool because the current import FBX sop isn’t very procedural as it’s only accessed through a menu (though you can call it from Python, which is what I am doing essentially).
Said tool generates an objnet consisting of a series of nodes which represents the hierarchy of the FBX – this can be a pain to work with though, especially compared to the File sop which just pulls in geo as is.
This tool just wraps up that tool in a sop context and pulls all references in to the local HDA level, and builds the subsequent networks to output and organsie the geometry.
How it works
As this tool is just a sop wrapper which calls the FBX API already exposed, it should be maintainable going forward as all the heavy lifting is already done.
This is also useful as it builds the necessary matnets, transforms, hiearchies etc which I can utilise – I just move this whole objnet to an editable objnet inside my HDA context.
My tool then builds 2 objmerge networks to pull in the geo from the objnet.
All the work in this tool is in the Python module attached to the HDA, where the LoadFBX() method is called by a callback on the load button callback, and the text field callback.
I’m doing this with this very simple line to be able to call my methods from a Py module on the current node context we’re executing from.
hou.phm().LoadFBX()
And this is the Python module.
Edit 06/01/2020: Updated with import transform functionality, vertex colour preservation (prevent mismatch of Cd attribute), added ClearGeometry() method, & added suite of import options
import os, sys def ClearNetwork(network): for child in network.children(): child.destroy() def SetupMergeNetwork(network): result = {"network":network} ClearNetwork(network) result["merge"] = network.createNode("merge", "merge") result["merge"].setDisplayFlag(True) result["merge"].setInput(0, result["network"].indirectInputs()[0]) return result def ClearGeometry(): with hou.undos.group("Clearing geometry loaded from '" + hou.parm("file").eval() + "'"): ClearNetwork(hou.node("IMPORT")) ClearNetwork(hou.node("GEO_MERGE")) ClearNetwork(hou.node("COLLISION_MERGE")) def LoadFBX(): # Get file path file = hou.parm("file").eval() # Evaluate path file = os.path.normpath(file) if not os.path.exists(file): raise hou.NodeError("File doesn't exist") return # Find import objnet (editable) objnet = hou.node("IMPORT") ClearNetwork(objnet) # Import FBX #hou.hscript("fbximport {}".format(file)) materialMode = hou.fbxMaterialMode.FBXShaderNodes if hou.parm("materialMode").eval()==1 else hou.fbxMaterialMode.VopNetworks compatabilityMode = hou.fbxCompatibilityMode.FBXStandard if hou.parm("compatabilityMode").eval()==1 else hou.fbxCompatibilityMode.Maya rawFbx = hou.hipFile.importFBX(file, import_animation=hou.parm("importAnimation").eval(), import_joints_and_skin=hou.parm("importJointsAndSkin").eval(), resample_animation=hou.parm("resampleAnimation").eval(), resample_interval=hou.parm("resampleInterval").eval(), import_materials=hou.parm("importMaterials").eval(), material_mode=materialMode, compatibility_mode=compatabilityMode) fbx = rawFbx[0].copyTo(objnet) rawFbx[0].destroy() # Link import transform fbx.parm("xOrd").set(hou.parm("xOrd")) fbx.parm("rOrd").set(hou.parm("rOrd")) fbx.parm("tx").set(hou.parm("tx")) fbx.parm("ty").set(hou.parm("ty")) fbx.parm("tz").set(hou.parm("tz")) fbx.parm("rx").setExpression('ch("../../rx") + if(ch("../../axis")==1, 90, 0)', language=hou.exprLanguage.Hscript) # Link scale fbx.parm("ry").set(hou.parm("ry")) fbx.parm("rz").set(hou.parm("rz")) fbx.parm("sx").set(hou.parm("sx")) fbx.parm("sy").set(hou.parm("sy")) fbx.parm("sz").set(hou.parm("sz")) fbx.parm("px").set(hou.parm("px")) fbx.parm("py").set(hou.parm("py")) fbx.parm("pz").set(hou.parm("pz")) fbx.parm("prx").set(hou.parm("prx")) fbx.parm("pry").set(hou.parm("pry")) fbx.parm("prz").set(hou.parm("prz")) fbx.parm("scale").setExpression('ch("../../importScale") * ch("../../scale")', language=hou.exprLanguage.Hscript) # Link scale # Build merge networks # Clear networks first geo = SetupMergeNetwork(hou.node("GEO_MERGE")) collision = SetupMergeNetwork(hou.node("COLLISION_MERGE")) # Generate objmerge nodes for child in fbx.children(): if not child.type().name() == "geo": continue # Skip this iteration isCollider = any(s in child.name() for s in {"UBX", "UCP", "USP", "UCX"}) mergeNet = collision if isCollider else geo objMerge = mergeNet["network"].createNode("object_merge", child.name()) # Create objmerge node # Set-up parameters objMerge.parm("xformtype").set("local") objMerge.parm("createprimstring").set(True) objMerge.parm("pathattrib").set("path") objMerge.parm("objpath1").set(mergeNet["merge"].relativePathTo(child)) objMerge.parm("createprimgroups").set(True) objMerge.parm("primgroupprefix").set(objMerge.name() + "_node") objMerge.parm("suffixfirstgroup").set(False) mergeNet["merge"].setInput(len(mergeNet["merge"].inputConnections()), objMerge) # Connect to merge objMerge.moveToGoodPosition() geo["merge"].moveToGoodPosition() collision["merge"].moveToGoodPosition() # Select this node to bring parameter panel back hou.pwd().setSelected(True, True, True)
What’s next?
FBX Export sop of course!
An FBX rop exists of course, but I would like to simplify the process of building a hiearchy from an objnet level, to a sop level and be data-driven to make proceduralism easier.
I am still thinking of my approach to this, but at the simplest level I believe it’d be at least useful to include a collision pin and allow my sop to build the relevant hiearchy and node names, and perhaps some helper sops to categorise collisions.. we will see!
Thanks for sharing man!
LikeLike