 Howdy, guys. Andy Pixel here. And in this video, what I wanted to do was cover some really cool Python techniques to basically dynamically create an FBX file that contains all of the chunks from your fractures that you do with like material fracture or void node, fracture nodes, because when you're using that stuff inside of the Houdini engine and you go and bake out your result, usually the chunks from the fracture all basically get combined into one mesh. And usually, if you want to do some sort of simulation, you want all those parts and pieces all separated out. So I have an example here of all of this stuff working. And so for the fracture, I'm currently just using a really basic material fracture node, just really simple setup, nothing crazy. And that's really just so I can get things rolling here. But the cool part about this is that inside of the HDA, we have this subnetwork. And inside of the subnetwork, we have these dynamically created geo nodes that are created by the Python, the code. And in each one of these geo nodes contains one of the fractured pieces. So when we go and actually export this out, we'll get a group basically per fractured piece. So what I wanted to do is walk through that whole process and how to set this all up. So let me hit Shift Z on the keyboard. I'm going to hide my example, HDA there. And let's start over. So what I'm going to do is go and create a subnetwork here. And I'm just going to call this fracture export parts or something like that, whatever you want to call it. And I'm going to dive inside. And let's go and create a geo node. Now this geo node is going to be responsible for doing the fracturing. So I'm just going to call this the fracture. And then we're going to create a subnet. And this is going to be where we place all of our dynamic nodes. So I'm just going to call this export. And then finally, we need to create a RAP network so we can access the FBX or the Filmbox FBX node right here. This will be responsible for exporting out everything out to the FBX file. All right, so with that all set up, that's your basic setup right there. So let's go and turn that into an HDA. So I'm just going to right click on this, create digital asset. And we're going to go and call it that's fine for now. You can go and put in your namespacing if you want. So if you wanted to do something like IndiePixel for the namespace, use the two colons there. And if you want to give it a version number, you can do 1.0 like so. Then just remove it from the label so it doesn't actually show up. So you do that if you want. So I'm just going to capitalize these guys. Cool, so I'm just going to save this into my documents folder. All right. And so I'm going to then hit Accept. And it's going to tell me that we have some references. That's fine. We'll fix all that stuff here in just a sec. All right, so let's get things set up here. So I'm going to go into the Parameters tab. And we're going to go and hide all of our default UI. And now this thing is coming from the actual FBX. And that is because we are referencing OBJ. So we just get rid of that. And now if I had to apply, we don't get that. Message anymore. Basically, it was an absolute reference to the OBJ world here. All right, so now we've got all that stuff set up. Let's go and make sure we save our node type. And let's first get our Fracture stuff set up. So I'm just going to do a really quick setup for this. Nothing fancy. It's not really about creating fancy fractures or anything like that. So I'm going to put in an Object Merge node. This way, we can assign any object to it. If we're working inside a Houdini Engine, we'll be able to assign any sort of object that we want to fracture to this. So I'm going to set the transform to into this object. So we import any transform values into this particular object. And now what we need to do is in order to get this working without having to open up Unity or Unreal so we can use the Houdini Engine, I'm going to go to my type properties here. So I'm just going to right-click this guy and go to my type properties. And we're going to promote the Object 1 path there. And I'll just call this some input object, something like that. Let's say input object, cool. And hit Apply. So now we can actually test this just straight up inside of Houdini. So I'm just going to drag and drop my test face. So this test face, I just went and created really basic curve. Clipped it off so we didn't have anything past the center of the world there. Then I resampled it with subdivision curves on. I revolved it. And then we just did a fuse, poly extrude, just to give it some thickness. And then just made sure it's sitting right on the grid there. All right, cool. So let's get back to our HDA. So now we have access to that. Let me actually hide everything else. So now we have access to that test base geometry. All right, so I'm going to throw down a material fracture node over here. All right, just wired into that first input. Now again, like I said, I'm not going to put into any of the details of how to use this particular node. I do want to turn on my chipping node because it gives a really nice, cool look to it. You get all these nice little parts and pieces in the corners there. Really cool stuff. All right, so with that, we now have the basics of our fracture. So the next thing that I want to do is just throw down a null node right here. We'll just call this out fracture so we can access that real easily. All right, and what we need to do now is we need to basically give ourselves some information about the existence of each of these parts and pieces. So currently, if we go over to our primitive attributes in our geometry spreadsheet here, you can see that the material fracture node actually outputs this name attribute. And that indicates all the primitives that belong to which chunk or which fractured piece. Now, you could totally use this name. I'm actually going to throw down an assemble node because it gives me a cleaner name. So if I just keep this create name attribute, you can see that it cleaned up the name beforehand. It has this p0-0-0, and in this case, I just want the attribute to be a little cleaner there. Not totally necessary, but just kind of a preference thing. So you can see we currently have 131 pieces. That's because we started index zero there. So what we need to do now is we need to create an array of all these names. I just need to get one off of these names. Now currently, on each primitive, there's quite a lot of primitives that have the same actual name attribute value. In this case, all these primitives have that same value, so p0. What we can do is we can use a little bit of vex here to help us out with this particular task. So I'm going to call this find unique values. There's a function inside of vex called in unique values, or unique evals. That's the one that I'm looking for. So let's actually put this here. And what I need to do is I need to store the result of this into an array. And we'll take a look at the help here in just a second. So this is going to be an array of strings because this name attribute is a string. So I'm going to say s array at names is equal to this unique evals function here. So if I put my cursor over this and hit F1 on the keyboard, it'll launch the help for this particular function, which is nice and handy. So you can see that it's returning a string array. And what it does is it allows us to find the set of unique values across all values in an inter string attribute. That's awesome because we can then basically loop through all these primitives here and find out which value is unique. All right, so piece zero would be unique. Then if we keep scrolling down, we'll have a piece one. Let me actually grab this guy over here. Let's go back to here, there we go. And we have piece two, piece three, you know, so on and so forth. So if I come over here, now I need to actually put this on detail because I'm going to send this to a detoch. I just need one array that holds all the unique value, name values, all right? So the way this works, we're gonna feed in the geometry that's being input into this first input right here. And then I wanna give it the type that the attributes or the component type that the attribute is sitting on. In this case, the attribute that we're looking for is on the primitives. So in here, if we look at our help again, it requires us to input one of these strings. So point, prim or vertex or detail, okay? So in this case, I have my attribute on the prim. So then I'm gonna go and pull that name attribute off of it. And there we go. So now if we go over to our detail attribute, you can see we have this array now that has unique value. So we have piece zero, piece one, piece two, piece three, piece four. And this is great because now we can use a little bit of Python to loop through this array and dynamically create a geo container node and then pull in all the geometry or all the primitives that belong to that particular piece, all right? So hopefully that makes sense. So really cool way inside of X2, basically find all the unique values inside of an attribute. All right, so with that all set up, we are good to go. So I'm gonna then launch the type properties window over here and we are then gonna go over to the scripts tab, okay? And what I wanna do is I'm gonna add some Python. So in order to do that inside of HDA, we need to go to the scripts tab and then we need to go down to this event handler and we wanna add a Python module. Now this is gonna be all the Python that goes with this particular HDA. All right, so to get things kicked off, let's do a new function. So I'm gonna say def and we're going to say export all parts, something like that, you can give it whatever name you want. And I always put in the, or I usually always put in this parent argument and that is basically sending in the parent. So when I say parent, we're sending in this node. And the reason why I wanna do that is because I wanna get access to all the parameters up here, okay? You'll see as we get more and more into the code here. So first things first, I always like to go in here and just say something like this is working just so I can get my button hooked up. All right, so with that in place, I'm gonna hit apply and we're gonna go over to the parameters tab and what I'm gonna do is create a button and this button basically is gonna fire off all the code that we write inside of our Python module. So I'm gonna go over to parameters tab and I'm gonna call this the export button. Say export all and I forgot a P there. All right, cool. So now what we need to do is we need to hook this button up now. All right, so let's actually get our UI up here and for our parameters all set up. So what I wanna do is I'm gonna navigate up to the top root here and I'm gonna split my pane left and right and then I'm gonna pin this one. That way I always have access to my HDA parameters right here and I could still work with the parameters on the geo nodes down here. All right, cool. So now I can easily test my button without having to navigate around my HDA too much. All right, so with that done, let's get this hooked up. So let's come into this callback script right here and what we need to do to get this hooked up to our function that we just wrote, right? Because I wanna print this out to this Python shell down here. I'm going to go and say who.pwd and that basically references this node. So this node right here, the fracture export parts node, that is that node. What we wanna do is we wanna access the HDA module on it which would be this thing right here, this Python module. All right, so we do that and then we say the name of the function. So export all parts. So let me just copy that. We'll go over here, paste that and then we need to make sure we send in the argument. So remember we put in this parent variable or argument. All right, and that parent is this node. So again, we just use the who.pwd. So who.pwd, awesome. So with that I'm gonna hit apply. We'll keep the window open because I still wanna add some more code but we can now go and test this. So if I hit the button export all, you can see this is working. So that means now we are firing off our function up here. All right, cool. So let's keep moving forward. The first thing that we wanna do is we wanna get access to all of these nodes right down here. All right, so we wanna make sure that we have the ability to access this particular node, this particular node and the RopNet here. So we can dive into an add nodes or manipulate parameters, stuff like that. Okay, so let's get rid of our print statement here and let's set that up. All right, so let's do a quick comment. We'll say get all nodes or all child nodes like so. And so let's first get the fracture node. So the way this works is we just type out a variable name. All right, so say frac underscore node is equal to our parent. Remember the parent is this fracture export nodes so we're passing it in right here. We say parent dot node and we're gonna give it the name of the node that we're looking for. In this case, it's gonna be the fracture node. So you just give it the name. All right, and then we wanna get the export node. So we say export node is equal to parent dot node and we look for the export node. Pretty simple stuff. So then we want the Rop node. All right, so we can fire off the export button. So basically we wanna automatically just hit this button or press this button right here. Okay, so I'm gonna say parent dot node and we're looking for Rop net one. So Rop net one. Awesome, so now we have access to all of the nodes that we need. And in this case, we could actually dive in a little bit further. We don't actually need to store the Rop network itself. Right, because we're not gonna hit these buttons. We're actually gonna be hitting this button. So rather than just store that or have two values there. Let's say dot node, we can change these together and we'll just get that result. So now we're basically in this Rop node variable here storing the film box FBX node. Awesome. Cool, so now we have access to all the nodes. So now we can do stuff with them. So the first thing that I wanna do is I want to get the detail attribute coming out of this out fracture node right down here. All right, so if we go back to our geometry spreadsheet I wanna get access to this particular value here. All right, so in Python, we have to do a couple steps to access the attributes that is sitting on geometry. You know, working with nodes is pretty easy creating, you know, deleting nodes or getting nodes is really easy. What we need to do is something a little different when we start to work with the geometry. So let's walk through that process now. And what I'm gonna do is start a comment here. So I'm gonna say get detail attribute. All right, so the first thing we need to do is we need to get access to the geometry. All right, and in order to do that, I need to basically actually get access to this particular node first. So let's actually get this node here. So let's say that the unique node is equal to our freck node dot node. And we put in this name right here like that. Awesome. So now down here, what I can do is I can get access to the geo. So I'm gonna create a new variable called geo. And this is gonna be equal to the unique node dot geometry. Easy peasy. So with that, we basically now have a copy or a reference to all the geometry at that particular node. So all the geometry at this particular node right here. Okay. So now what I wanna do is I wanna pull off the detail attribute. All right, and to do that, we need to utilize another Python function. All right, so let's go look for that. So I'm gonna say filter or not filter. I wanna do find global. And I believe that's in the geometry area. So if we go to geometry, so who dot geometry, it's one of the functions in there. So we're looking for, yeah, find global attribute or atrib. And all you need to do is give it a name. And you can see if we open up the help, it's looking up a global aka detail attribute by name. All right, and that returns this who dot attribute. All right, so let's pop that guy open. And what we're gonna need to do with this is we need to utilize the, we need to get a list from it basically. And I believe that was on the geometry as well. Yeah, there we go. So we want the string list attribute value. So we're gonna be using both these guys. So find global attribute, we'll get us the attribute. And then we can use that attribute to pull off all the values. All right, so let's do that. So let's go back to Houdini over here. And we're gonna say that our atrib is equal, that's just variable name. So it's equal to our geo dot find global atrib. And we wanna pass in that name and that name is names. All right, so that's what I'm looking for. So we wanna get names here. And then we want to create our own names variable inside of Python over here, which is gonna store the array of the names right here. So we're gonna say, we're gonna say geo dot list. Let's actually get the proper name. Because believe it or not, I do not memorize all these things. That's why I have the help open most of the time. All right, so we're gonna get the list or string list attribute value. And I'm gonna pass in that atrib that we just captured. That's the value, cool. So now we've got that array inside of this name variable right here. So let's just loop through it. So let's just loop through the array and do stuff. All right, so we're gonna say for name in names. All right, let's just print it for now. So we're gonna say print name, awesome. Let's hit apply. Let's go to our Python shell. And if all goes well, we should get a name. There we go. So I just went and hit the export all button and you can see we have access to all of those names. Awesome. So at this point, what I wanna do is I want to, now for each one of those iterations in our loop here, all right, I want to go and insert a new geo node into this export subnetwork here. All right, so let's do that. I'm gonna go and say, we're gonna say export node or export. Yeah, export node, that's the variable. Dot create node and the type of node that we wanna create is a geo node. So basically we wanna do this. All right, so we wanna create one of these geo nodes. Now, if you ever wanna find out the type that you type in here, so in this case I'm gonna type in geo. If you wanna find out the type, you just come over to this little info button here and there's the type, geo. All right, so let's also give it a name. In this case I'm just gonna use the name. So let's just put a comment there and we'll say name. So that'll be this name right here. All right, so let's hit apply and let's actually hit accept and just take a look and see what happens here when I go and hit the button. All right, let me actually get rid of this guy too. All right, so let's hit the button. Now if I go into my export node, you can see we have a ton of geo nodes. So we dynamically went and created a container for each one of these elements in that array. All right, we named it as well. So p0, p1, p2, p3, super cool stuff. Now, one thing that we need to do now that we're getting all this going is we actually need to make sure each time we hit this button we're not just adding more geo nodes, we actually need to clear it out. All right, so let's go back into our code over here. So let's go back to the type properties and basically before we go any further here, let's do this, let's clear out the subnet. All right, so the way to do this, we're basically gonna look at how many children are inside of this particular export node here. All right, and we will delete everything. So just to make sure that this actually is working, I'm gonna comment out the for loop. All right, now that we've got a bunch of nodes in here and I'm gonna go over here and I'm going to say children for a variable name. We're gonna say export underscore node.children. That'll return all the children that are inside of this particular subnetwork. All right, so now what we can do is we can say for child in children, we're just going to say child.destroy. So just delete it. All right, so let's hit apply and then we're gonna hit export all. Now look at that, we have nothing. So now we're clearing it out and then we'll come in here, turn our for loop back on, hit apply and we'll hit export all and there we go. So I have all the nodes created. All right, so I'm hitting L on the keyboard to automatically lay out. There are Python commands or functions that will do that for you too. So if you want it to be automatically laid out, it's there as well. All right, so now we've created a node for each one of those fractured pieces. What we really need to do now is we need to, for each one of these nodes, we need to set up another little tiny network inside of here. So what we need to do is we need to set up an object merge node and in this object merge node, we need to go and get the fracture geometry. All right, so we need to dynamically create this node and then populate this particular parameter. All right, and we could always set that. It's not totally necessary though because all this stuff basically exists inside of the node itself. And then we basically need to go and create a blast node. We need to wire this up dynamically and we need to make sure that we go and we grab the correct piece and then turn on this delete non-selected so we get just the one piece. All right, so we could do that all with Python. All right, so it's kind of good to go and I do it quite often, go and set up the network that you want to create first and then basically replicate it inside of your Python code over here. All right, so let's do that. Let's start with the object merge node. All right, so first let's store this new geo node that we have. So we're gonna call this new geo. That way we can reference it. All right, and then what I'm gonna do is I'm gonna call, I'm gonna create a new variable called merge node and I'm going to create a new node. So we're gonna say new geo node dot create node and this time we wanna create a object merge node. All right, so that's the name that we put in here. So object underscore merge like so and you can give it a name you don't have to. And then what I need to do is I need to create a blast node there. So we're gonna say blast underscore node is equal to new geo dot create node and we wanna do a blast and I believe it is just blast. Yeah, blast, very cool. So let's do that. So there we go. And so now we've got all those guys hooked up. All right, so with that all in place, let's come down here and let's take care of setting the path here. So I'm just gonna copy that because that's working perfectly already and that's exactly what we need to input there. What we need to do is we need to get access to this parameter and this parameter has a name and you can see if you hover over it, it's object path one. So what I need to do is I need to come down here and I need to say merge node dot palm. This is how we access the parameter, say dot palm. We give it the name, so obj path one and then we say set and I wanna set it to some value. In this case, I wanna set it to that path, that fracture path. All right, so let's test all this stuff out now because we did quite a bit there. All right, so I'm gonna jump up and out a couple of times there. Let's make sure I apply my changes, cool. And then let's hit the export button. You can see it takes a little bit longer because in each one of these nodes now, we went and created a blast node and an object branch node and we also set the input geometry, all dynamically. Really cool. All right, so what we need to do now is we actually need to hook these guys up. And the way that we do that is we come down here, let's actually make a couple more spaces here. The way we do that is we say blast node, right? Because I wanna hook the merge node into the blast node. So I'm gonna say blast node dot set input, like so. And let's actually look for this. So if we go to our help again, this will be in the who.node. So let me show you guys how to kinda work with the documentation up here. So I'm looking for who.node. Let's just type in node, it'll go a lot faster that way. You can always start with the stop node as well and then just traverse back up the class hierarchy. All right, so we are looking for inputs. So if we scroll, there we go. If we scroll all the way down, this is how, or all the functions that we have available to us to handle inputs and outputs. So the one I'm interested in here is set input. So first we want the input index because a lot of these nodes have more than just one index or input. The item to become the input, so that's the node and the output index. And in this case, this is real easy for the blast node because it only has one output. So I wanna hook up the first input, so that's gonna be zero. We wanna send in the merge node like so and then we wanna output on output zero like that. Very cool. All right, so let's take a look at that now. I'm going to jump up and out and hit export all and we'll jump back in, hit L on the keyboard to space it all out. And you can see now our object merge node is now set to the blast node. But what we need to do is we need to move the display flag down to the blast node. All right, let's do that. So let's go and say blast node dot set display flag and that is going to be equal to true. I just wanna turn it on. So let's hit apply and test that. All right, so I'm gonna hit export all, cool. And let's see the result. Awesome, so now we have the display flag on but the render flag now is turned on for this object merge node. So let's turn that off. All right, so what I'm going to do is say merge node dot set render flag to false. There we go, and apply, awesome. So let's test that out now. So you can see how you start to step through each of these processes. You wanna kind of break it down into manageable chunks as you're working through this. And it goes faster, you know, the more that you do it. All right, there we go. So we now have our final setup. The last thing we really need to do is set up all this stuff. All right, so we need to set the group parameter. Now, you know, if we wanna go and see what we actually need to type, we need to type this name and then give it the piece that we want. All right, and we wanna put that into the group parameter there. So let's do that. All right, so let's make a little more space down here. Cool, so I'm gonna say blast node dot parm. And we wanna access that group parameter. So type in group, and I wanna set this to a string. So we're gonna set this to at name equals plus the name. All right, so that's the name because that's coming in in our loop. So this is the name attribute or value. And we're just appending that or concatenating it onto this particular string. And then we wanna make sure that we set this parameter right here, which is called negate. All right, so we wanna say blast node dot parm. And we wanna look for negate. We want to set that to true. Just wanna toggle it on. Hit apply. And there we go. All right, so let's go and test this one more time. Hit export all. And if we jump inside here, you can see now in each one of these pieces, we've set up the network appropriately and dynamically to capture all the pieces and put it into its own geometry container. How cool is that? All right, so with that all done, we really just need to focus on getting this out to an FBX file, all right? So let's come into the RobNet and talk about this for a little bit here. So that's it for the Python code for now. So let's go and let's expose the output file here. So that's gonna be our output. So this is gonna be our output path. There we go. And what I need to do is I need to get access to the export node inside of this export. This is where the geometry or where we actually indicate which geometry we want to export. So a really quick way to kinda just debug this or understand what you need to type is just to go and select it from that node list there. So basically we need to get access to that export node. Now you might think right off the bat that let's actually do that. That you could do something like this and just select that, that won't actually work. That'll just export an empty FBX file. What we need to do is we need to get the full path. So to do that, I need the two back ticks and I need to utilize the opt full path function or hscript function, all right? And what we need to do is we need to give it the full path to our export node. So if I type in that same path and just do export like so and then middle mouse click on this label right here, you can see that it returns that full path. So this basically keeps it relative but gives us an absolute result. All right, if that makes sense. So that'll make the filmbox FBX node nice and happy. Okay, cool. So that's all set up, so that's ready to go. We have the ability to allow the user to set the path for the FBX file. The last thing we really need to do is once we're done with this loop here, all right, we really just need to fire off the render button, all right? So I want to automatically hit this save to disk button. All right, so let's do that. So we have access to the RAP node. That's that FBX node. So we can say RAP node.Parm. All right, and if we look at the parameter name for the button, it's execute. Most of them are actually called execute. All right, so we say parameter execute.PressButton. Just like that. And there we go, hit apply and accept. We are good to go. We just created a little node that automatically and dynamically goes and creates a geo container for every part in your fracture. And this will work inside of the Houdini engine as well. The one thing, if you are gonna use this in the Houdini engine, you just need to make sure that you go to the node area and you make that export node the editable node. That way we can dynamically add things to it. So I'm just gonna say that that export node is editable. All right, we'll hit apply. I'm gonna hit accept. And just to prove that this is still editable, I'm gonna go jump up and out. I'm gonna right click on my HDA and say match current definition. And then we're gonna double click it and you can see that the export node is not grayed out. So it's editable. Cool. There we go. Let's test this whole thing out. So I don't need this pin anymore. We're just gonna work at the top level here. And let's actually do one more thing. Currently I'm displaying two meshes here. So let's allow editing and contents. And I'm going to basically just turn off. Well, actually let's keep that one on in the fracture off. There we go. Alrighty. Let's, I think I also still have this guy displayed. Yeah, there we go. Let's turn you off again. There we go. Cool. So let me just throw this guy onto the desktop here. So I'm just gonna call this my vasefractor.fbx, like so. Cool. So now we've got our path. Now we've got an object that we're fracturing. Let's hit export all. And there we go. So now we should have gotten an fbx. So if I import my fbx now. So let's go to my desktop here. Let's say import. Look at that. We've got a really cool piece of geometry now that has all the different pieces in it. How cool is that? All right. That is what I'm talking about. And that works inside of the Houdini engine as well. All right. So I just wanted to make sure I showed you guys how to do that. It's really cool, really fun. Definitely really makes your HDs even more powerful. So thanks so much guys. I'll talk to you soon. Bye.