Jump to content


  • Content count

  • Donations

    0.00 CAD 
  • Joined

  • Last visited

Community Reputation

34 Excellent

About Alain2131

  • Rank

Personal Information

  • Name
  • Location

Recent Profile Visitors

1,222 profile views
  1. Switch if

    Hey Sam, The If syntax can be found here → https://www.sidefx.com/docs/houdini/expressions/if.html if(ch("../transform7/ty")>0.5, 1, 0) But for a simple comparison such as this, you could simply remove the if altogether. ch("../transform7/ty")>1 This will resolve to 1 if true, and 0 if false.

    Here's my take on it It's not polished or anything, it just works. Basically, it's a foreach loop, set to By Count, which is controlled by a frame range attribute. Within the loop is a timeshift tied to the loop's iteration, as well as an FBX Export with the same loop iteration in the filename. Finally, a python sop simply does a pressButton() on the FBX. (The execution is handled through the Stop Condition of the loop. The subnet's Export button sets it to 0, force cook, then sets it back to 1) Anyways here's the file (19.0.455), hopefully that helps. export_separate_fbx_per_frame.hiplc Cheers ! (I took that as a fun mini tool-building exercise, but sebkaine's link has a few other solutions that works just as well)
  3. I personally add a space then backspace after the first parenthesis, like so But the tooltip disappears as soon as we do anything. It's a bit annoying, considering the working tooltip in hscript..
  4. Hi Spyro, you'll want to use Python for any node network-related manipulation. The relevant functions are node.children() # Return a list of the children https://www.sidefx.com/docs/houdini/hom/hou/Node.html node.createNode() # Create a node https://www.sidefx.com/docs/houdini/hom/hou/Node.html node.setFirstInput() # Connect input to specified node https://www.sidefx.com/docs/houdini/hom/hou/SopNode.html node.setDisplayFlag() # Set the display flag https://www.sidefx.com/docs/houdini/hom/hou/SopNode.html node.setRenderFlag() # Set the render flag https://www.sidefx.com/docs/houdini/hom/hou/SopNode.html node.moveToGoodPosition() # Move the node under its connected parent https://www.sidefx.com/docs/houdini/hom/hou/SopNode.html The rest would be Python logic to decide what to do. subnet = hou.node("/obj/subnet2") # Get a reference to the subnet you want to do stuff to children = subnet.children() # Get all the children of the subnet for child in children: # Loop over each child # Make sure it's a geometry node, since we can have other stuff such as nulls and whatnot nodeType = child.type().name() if nodeType == "geo": # Get Display node displayChild = child.displayNode() # Create Node, parent, and some housekeeping newNode = child.createNode("null") newNode.setFirstInput(displayChild) newNode.setDisplayFlag(True) newNode.setRenderFlag(True) newNode.moveToGoodPosition() This parents the new node to the one that had its display flag set to it. Maybe that's not what you want # Instead of Get Display Node, here's two more options # Get last children # uses the Tree View order (for more info, head to the documentation) children_1 = child.children() lastChild = children_1[len(children_1)-1] # Get last created node # uses the CreationTime metadata # for all children, get the creationTime, and get the youngest one time = children_1[0].creationTime() # initialize time to the first node youngestChild = children_1[0] # initialize youngestChild to the first node for child_1 in children_1: time1 = child_1.creationTime() if time1 > time: # if the current node is younger time = time1 youngestChild = child_1 # And with that, you'd replace setFirstInput(displayChild) with either lastChild or youngestChild Anyways, it all depends on what exactly you want, I needed to do some guessing since we lack an example scene. Here's a scene with an example addSopToChildren.hipnc P.S. I used a weird method to get the script to run when I press a "button". There are much better ways to do it. Here's one → https://projectjulien.space/houdini-tutorials/2018/7/4/create-a-python-snippet-container-on-null-or-any-node
  5. Labs Maps Baker - auto fbx export??

    This is triggered using a Python script on the HDA itself. Here's the short version : To see the script, right click the HDA → Type Properties Go into the Scripts tab. There's some stuff in there, but we are looking for one keyword in particular, the name of the ROP FBX Output node. Lo and behold, line 20, there it is → a_node.node("low_export").render() That's all nice and well, but how did I get there (aka Reverse Engineering 101). I realize that some of the investigation steps may not be obvious to a beginner in Houdini, as there are a few hoops to jump through. So here's some more explanation. It is strongly recommended to note the name of the ROP FBX Output node and keep it in mind during our investigation. Dive inside the HDA and take a note of FBX node name. "low_export". Gotcha. So then, let's first start with the end. What triggers the fbx to export in the first place ? Why, it's the Render button on the HDA ! Let's start with this fella. Let's open Type Properties and see what happens when we press that Render button. Ah, there's a callback script on the button → kwargs['node'].hm().Render(kwargs['node']) What's all this then ? A callback script is a (generally) small script that executes when the button is pressed. You could print("Hello World") in it, set some parameter value, etc etc. Note that the icon on the right is the Python logo, which was manually changed from the default Hscript, meaning that this expects a Python script. kwargs is a dictionary containing some info about the node and parm it's called from. https://www.sidefx.com/docs/houdini/hom/locations.html#call (ctrl+f search for kwargs in the page) Breaking this down, kwargs['node'] means the current node (the HDA itself), hm() is the asset's Python module (that info is in the same page ↑ if you search "hm()"), and Render(kwargs['node']) is a custom function, taking the current node as a parameter. In not-so-layman's terms, the button calls a function from the Python Module on the HDA. So, let's go the the Scripts tab on the HDA, and take a look at the Render() function. (Look at the second picture, I won't copy it here again for the sake of not making this message any bigger than it is) Alright, it's doing a few stuff about frame mode and ranges, we don't care about that, something about cook (cook miiight sound interesting but it's for the foreach_end1, so let's skip that), and then aha ! "low_export" ! Interesting So, it's calling .render() on the low_export node. That sounds like the answer ! (because it is) One more nice step is to figure out where does this render() function comes from. What if we want more info about it, pass some arguments ? We can print stuff out to figure out stuff. We can take a look at the type() of a node with print(type(nodeName)) If we print(type(a_node.node("low_export"))), we'll see <class 'hou.RopNode'>. This will allows us to take a look at the documentation for the node (not for the FBX Output node specifically, but for the overarching node type, the "parent" from which the FBX Output inherits stuff from) https://www.sidefx.com/docs/houdini/hom/hou/RopNode.html And, right there, render(aBunchOfOptionalArguments) Taking a look at the description, we won't be surprised with it. That wasn't a necessary step in this context, but it is super useful to know how to find stuff out in the documentation. What can we do with a node, what is this or that argument, etc etc. Note that there is another solution for exporting the fbx, and that's the pressButton() function. https://www.sidefx.com/docs/houdini/hom/hou/Parm.html (search for pressButton). Those are equivalent. a_node.node("low_export").render() a_node.node("low_export").parm("execute").pressButton()
  6. Exporting FBX Animation

    Hi, it sounds like you're animating the meshes with a transform node and trying to export this to FBX ? That will result in a point cache. Maya will understand that (don't know about Blender, but I would think that it would work). But for other applications (especially game engines), point caches are not supported. For maximum compatibility, bones should be used. Here's three ways to animate something. Point Cache, "Geo Anim" (where the actual transform of the object is animated), and Bone Anim. Anytime you animate something from within a Geo node, it is a point cache. That green clock next to a node generally means Point Cache when exported (unless special care is applied !) Once you have something animated in this fashion, depending on the case it can go from slightly complicated to a very big pain to convert it to bones. Rigid bodies can be converted to bones (if packed. Unpacked can be harder), soft bodies without changing topologies can use Dem Bones Skinning Converter, and if you have changing topology, then I have some bad news for you.. Geo Anim is probably what you expected. If you animate a mesh in Maya, 3DS Max (and I'd imagine Blender as well), this is equivalent. Bone Anim is what you want. Thankfully, KineFX came in and made life much easier. We can animate bones in SOPs ! Getting to know how to work with it is a necessary step (I'm very much a beginner with it). (This is very probably not an ideal setup for KineFX, as I said I'm not used to it. Note that there are two bones, because with a single one, Maya was converting it to a group, and not understanding the skin.) The FBX exports for the point_cache and the bone_anim are in their geo node, and for geo_anim, look into the OUT context. Here's a scene with the three above examples (in Non Commercial - I tested on my work PC, and duplicated the setup on my home PC. You can do the same, back to Indie or whatever license that can export FBX). fbx_anim_export_examples.hipnc I hope I answered your question somewhere in there !
  7. Concave Geometries With RBD

    Hey, there is a friction parameter on the rbdconfigure Alternatively, you can change the "Material" type, which gives you a nice default for different types of concrete, glass, wood or metal. You can see the different values update as you change the preset. Keep in mind that the Ground Friction is also a factor (but it looks like you've already played with that, so nothing new here). You might want to increase the Solver Substeps at a certain point for more precision, if needed. 10 by default, I'd use 50 if I needed more precision (no need to go to 200 or whatever with this type of scene). The Substeps amount is dependent on a lot of factors (including how long you're willing to wait =P).
  8. Concave Geometries With RBD

    Hello ! So, by default, Bullet creates a Convex Hull for each piece of connected geometry. Because you Boolean'ed the plus model, it's a single piece, thus making a single Hull. You can visualize this with "Show Geometry Representation" in the Visualize tab of the rbdbulletsolver node. If we disable the boolean, the hull is replaced for something better-suited to your requirement. But we now have a problem. The rbdconfigure creates a piece for each "connected components", or "geometry islands". In this case, instead of one piece per plus, it's 3. You can see the Packed Fragment goes from 31 to 93 when we disable the boolean. Thus, when simulating, we'll have plusses falling apart as individual cylinders. Not quite what we want.. The solution is a name attribute. The default behavior of the rbdconfigure is to create a piece for each connected component, but if there is a name attribute on the geometry, the rbdconfigure will use that to create the pieces. We can (and will) use that feature to specify what cylinders refer to which plus. It's actually quite easy, since you copy the plus on some points. You can copy an attribute from the points to the plusses. So, we can add a different name attribute on each copy points, and transfer that name on the plusses. In an attribute wrangle, running over points → s@name = "piece_" + itoa(@ptnum); You'll have to specify to your Copy Stamp to copy the name attribute. After doing that, the rbdconfigure will go back to 31 pieces. You'll notice that the collision stays around the individual cylinders. Note, the "Show Geometry Representation" parameter slows down the viewport considerably, so only enable it when debugging stuff. As a recap, because we know how the solver generates the collision (a convex hull around each geometry islands, even in packed geometry), we utilize that fact and keep the tubes separated in the plus models. To "merge" them, instead of the boolean, we put a name attribute on them, leveraging the copy node as well as the rbdconfigure (since we know that it creates a piece for each name). I hope that makes sense. Sorry for the long-winded explanation. 201104_RBD_Examples2.hipnc P.S. Concave geometry is a recurring issue. We have to resort to multiple hacks to get around it. One trick I used in the past is fracturing the geometry in complex areas to force the solver to create a better representation of the geometry. This is the same base concept as your case, to separate the geometry in chunks to give the appease the solver's demands. There are multiple other possibilities as well. VDBtoSphere and use that as collision, providing a custom volume for the collision (when applicable)..
  9. Connect opposite points in vex

    Hello, In a Point Wrangle, make a for loop going over every points, and compute the direction it has with the "current one". Points that faces each other will match the Normal, and others won't. So, if some point match with the normal of the current point, then you know you need to connect those points together. As for the connecting, we can't really connect all points when they found a match, because you'd have two lines for each pair. What I propose is to only make the connection when the match has an id higher than the current point. So, say the match is 9 and 15. 9 matches with 15, 9 is lower than 15, so a line is created. 15 also match with 9, but 15 is higher than 9, so we don't connect. (In the example below, I dodge this issue altogether by not including points with a lower id than the current point in the loop) Here's untested code, to "visually" explain what I mean. This should go in an Attribute Wrangle, running over Points. // We loop over the other points. // We start at the current point +1, // so we know we will never have "double-match" for(int i=@ptnum+1, i<npoints(0); i++) { vector P1 = point(0, "P", i); // Get the position of the other point vector dir = normalize(P1 - @P); // Get the direction of the other point relative to the current one if(dir == v@N) // If the direction match with the normal { addprim(0, "polyline", @ptnum, i); // Add the polyline break; // Since we have a match, we know that we need to stop looking for other points. } } That's the idea anyways. Maybe the computed "dir" won't match perfectly with the Normal, and you'll need some threshold to compare dir and N (if equal within a margin of 0.001 or whatever). A quick way to do that is abs( a - b ) <= threshold. (I think that should work for vectors, but it might not be the ideal way to do it. Although it should be fine for your case) EDIT : I quickly recreated the scene, and it seems to work I had to use the "almost equal" trick, because it had connected only 2 out of the 9 pairs. Next time, please share a scene file. connect_by_dir.hipnc
  10. Hey, glad it worked out !
  11. Okay, here's what I understand, and I got some questions. On different datasets, you got a string attribute named "type" with different values. Let's say you import once. The tool auto populates B F C D. You now have a multiparm or whatever that you can control the color and whatnot as you want. But now, you import again, and now you got F X A S F is still there, but X A and S are new. What do you expect to happen then ? The easiest to do is on each new import, wipe the entire UI and start from scratch But this means that for different datasets, if you got the same value, you'll have to re-enter the parameter manually. Here is a mockup, where when you import a new dataset, it resets all parms and populate with default ones. dynamic_HDA_parms_mockup.hipnc Basically, with a button, it reads the unique values of the attribute "type" (this info was fetched from within VEX into the types detail attribute) and sets the multiparm to the amount of unique values in the attribute. node = hou.pwd() geo = hou.node("./OUT_script").geometry() # After the attribute wrangle fetching the unique values of the type attribute types = geo.stringListAttribValue("types") node.parm("multiparm").set(0) # Reset multiparm node.parm("multiparm").set(len(types)) # Set Name Parameter for i, type in enumerate(types): parm = hou.parm("name_" + str(i+1)) # parm starts at 1, not 0 parm.set(type) A few clarifications and details I input the data by wiring it, but this can be adapted to an input from a file. You said your data was on the prims, this is doable as well. If you want the values of the parameters from previous datasets to remain, I see two options. 1 - Instead of wiping the multiparm each time, we go through the current parameters, look if we got any new ones, and create only the missing ones. 2 - Have a file somewhere on disk containing the relevant values for the parameters, and after resetting the multiparm, auto-fill the parameters using those values. I think this will be harder to do, though.
  12. One-liner for point(0,"P",8).z in point wrangle

    You indeed need to specify that point will return a vector by encapsulating it with the vector keyword. @P.z = vector(point(0, "P", 8)).z;
  13. how to load .bgo.sc without creating file node?

    I suggest looking into TOPs (aka PDG) for this. It can most likely answer your needs. Super convenient to work on multiple assets at the same time and execute the same procedure onto them all. Each file will be a work item, onto which you can do basically any process you want (HDA, geometry network, etc etc).
  14. How to change the tangent direction

    I suggest transferring N from the base mesh to the curves as well, it should give the missing info to CopyToPoints.
  15. Spliting Kitbash pack

    Salut Davidt, Let's break down the error. "'method' object is not iterable" at line 7. What is line 7, and what do you do there ? for prim in geo.prims: You iterate on geo.prims But it says that geo.prims is not iterable. So geo.prims is the culprit. If you go see in the Geometry Help page and search for prims, you'll see that it has parenthesis after it. Simply put, you need to write geo.prims() and that should fix your issue. That is because geo.prims is the equivalent to hou.Geometry.prims, which is the method (a quick Google search will explain what is a method to you) that is used to return the data you want. But it's not the same as calling it. You need the parenthesis to do that. EDIT : A good way to test and poke around a bit is to print out the result of the stuff you try to do. node = hou.pwd() geo = node.geometry() print(geo.prims) Returns : <bound method Geometry.prims of <hou.Geometry frozen at 00000000E4000800>> But, with the parenthesis.. node = hou.pwd() geo = node.geometry() print(geo.prims()) Returns : (<hou.Polygon prim #0 of geometry frozen at 00000000E4003200>, <hou.Polygon prim #1 of geometry frozen at 00000000E4003200>, <hou.Polygon prim #2 of geometry frozen at 00000000E4003200>, <hou.Polygon prim #3 of geometry frozen at 00000000E4003200>, <hou.Polygon prim #4 of geometry frozen at 00000000E4003200>, <hou.Polygon prim #5 of geometry frozen at 00000000E4003200>) This is for a cube. Be aware that printing to the console is very slow, and can take a very long time depending on the amount of stuff to print. The Python Shell (Windows - Python Shell) is much faster to print to.