 Welcome back catch my scratch episode 10 topic today is going to be importing STL's This is a pretty big deal because once we can import an external model into our program We can do so many things in the future. We can calculate the volume surface area the weight the centroid We can calculate anything we can create a mesh we can convert the file type We can do so many things we can view it in our viewer So many options. So this is gonna be pretty big once we can finish it so We've edit five files today the main function for our test case and then both the output and the geometry sets of files So I have spent a lot of time so far on STL's I don't want to go over it in too much detail but there are two different kinds of STL's ASCII STL's and binary STL's and the difference between the two obviously is that binary STL's are in you know binaries and ASCII ones are an ASCII so you can actually read the ASCII one you can't really read the binary one As easily and to tell the difference, you know Just look at the first five bytes if the first five bytes are letters S. O. L. I. D. It's an ASCII if they're not it's a binary the first 80 bytes are Garbage in binary STL. So you can just determine that I will warn you though is that a lot of In our binary STL's we focus with zeros But most programs that you'll ever work with like Blender or you know solidworks or whatever They put a bunch of junk in their head or like they put the version of the program that created the STL so it's just a bunch of Bologna they put in here. So you can't just check for zeros. You have to actually check if it's Solid and if it's solid it's an ASCII else. It's a binary and I'll get into the actual algorithm later I have put around with this for a little bit. So Let's create that function down here. It's again struck body STL was it's a car star file type file name and so the first thing we should do obviously is just define that That body that we're gonna actually return so struck body equals Malik We're gonna malik the size of The structure. Alrighty, that's good so far Now we didn't do this before we probably should have the whenever you define the structure like that you should always set the parameters at the fields inside to You know something if you don't you may have so much junk in there It may not Be what you want to be and I had that problem earlier today when I was trying to play around for this video So we'll set these Sort of first pointers of the node list and faceless to null That should help us and then also I noticed that you know the algorithm I want to implement here It it's makes made more efficient if we can define The last face we're working on because if we can do that we can Kind of save our place and not have to iterate through the entire list of faces every time we want to create a new face because sometimes you have you know you know hundreds of thousands of faces if not more and You haven't to go through it over and over again for no reason. It's a waste of time So we'll define a that a pointer for this last face and we'll you know, we'll populate that later now to actually check if Let me talk about the algorithm first. So the algorithm is going to be check if you know binary or ASCII Then based off which is which I mean you have to do this twice Different ways for each you know type, but we're going to loop through the triangles and then for each triangle run to Through vertices and For each vertices you're going to see Check if vertex exists. I should say node node exists at Vertex coordinates if yes Point to it if no Create and point to it now. I'll say that's it the actual semantics of implementing this is not so easy, but We'll figure that out right now so To check if it's a binary or an ASCII again me to check the first five bytes. So we'll create a character Array, we'll call file type buffer size five and we're going to read in the first five Well, I didn't open the file yet. Did I let's open the file. So that's the you create a file pointer F and What do you do you do f open? File name is it this I can file name and We have to pass in this how you want to open it will read it as a binary that works for both ASCII and binary So we'll just do that Now into this buffer We will read so say F read into the address of the file type buffer five bytes one value from F and Now we'll check you know if if those match up with the word solid. We're now we're an ASCII. So let's check so if Men compare I pass in the two different options. So one is file type buffer. The other is the word solid We're checking five bytes If that evaluates to well, it's gonna evaluate to zero for the same. So not that's going to be one So this will be binary or sorry ASCII And I'll put that out myself later. That's a little bit more verbose for the actual Video, I'm just gonna put up the the the binary. So where is it else? And this is the so once we're in the binary a couple things So go back to the standard here. We just read the first five bytes for the letters Solid there's still 75 bytes of the header that we don't care about so we have to seek ahead to seek ahead It's not that hard. You just type in F seek we pass in the File how many bytes do you want to skip ahead and then from where and you can pass in seek curve to get the current You know location of where you are in the file. So we're going to go ahead 75 more bytes at that point the next thing in the In a file in the STL is the number of triangles, it's a four byte value So we're going to simply read that so F read Into the address Four bytes one value from F and So that will load us into num triangles, but we should at this point I think spend some time because you know you may have a model with hundreds of thousands or millions of faces So it may take a few seconds to load this and you should give some indicator to the user that you're loading up a huge file So I'm going to print that something just Loading model, I don't know and we'll probably keep track of this, you know in a couple Maybe in some way in the future But just just to show that you're loading the model So now we're going to do what we said here, we're gonna loop through the triangles. So for Inti equals zero I less than I plus plus You know what I'm gonna actually close it up right now I'm gonna close up a way to keep track of the the actual model. So I'll be right back in it in a second So what I did was I just did this like kind of not new line carriage return So now whenever we Go through, you know, 1% of the number of triangles in the model with this sort of modulo here I'm gonna update this this text loading file name model With I out of num triangles faces and then I'm gonna flush standard out because I know you don't have a new line You have to flush it To actually get it to render to the screen So That's fine. So once we you know, once we are looping through the number of triangles I hear I just defined a face. So this is a face with with three vertices Sorry, I should have num points here. I copied this from the geom.c So this should say three and At this point, I'm gonna set the face num nodes To three just so we don't have any weird numbers in there Or sorry, it's a zero and we'll come in it to three. I guess and At this point, we can literally just say f read Into the face normal four bytes three values from F. So we're literally just gonna load straight away the The normal value, which is a normal vector, which is you know, 12 bytes. So three times Float which is which is four That's a normal vector And we're gonna load that right away into the normal back the normal up, you know field of that structure So that's that's good right away and now we can look over the nodes So we'll say four into J equals zero J less than three J plus plus and now we're looping through the actual nodes and here's where the logic comes in to check if the nodes been You know Be created before So I Guess first things first, we'll just pull in the the the coordinate From the the stl file, so we'll say float chords size three And we will f read into there. So f read Into chords the same four three as before from file f and and now we have to actually look through the Look through the nodes in the model to see if we have got a node at these coordinates Yet and if yes, we're just gonna point to it. If not, we have to create a new one. So I'm gonna code this up and I'll be right back in a second Okay, I coded up this logic here. Basically the way it works is Um, we create an iterator starting at the first node in the body, which whether we should be null at the beginning And then I have another value here just to keep track of what was the previous one Um, oh, so by the way, I could have a couple more files a couple more headers at least So I got center of lib string bool and a c type will need that for later So Back to this so If we found a match that's this boolean. So basically the way it works is While we loop all we're iterating through the the nodes in the body that already been defined You know, they're not in all we check if is the x coordinate equal to the core that we just you know Pulled out of the stl file If they all match x y and z I say match strong is one of my breakout of this loop If not, we're iterating through Now if the match is found we're going to like I said before we're going to point to that You know node that iter To the jth index of the node array and increment the number of nodes in the face So once you find it you store that in the node array of that face Um else, so if it's not found you have to create it. So here we're creating a node We're making the node with you know those coordinates. We're going to say null for the next node But we're going to populate the next the the previous next node the previous nodes next with this node with this logic here So we're saying now if it's the first node in the body You know That's fine Else the previous nodes next node is this node if that makes sense And then again, we're storing that in the face a node array and incrementing the number of nodes in the face So that should work if I did it right and then next We will have to do something with the faces so If you recall we've just populated the the node array in the face we have to actually put the face in the face You know linked linked list. So we'll say Uh If the body face equals nauseous and if it if this is the first face What we do is we set Um body face equal to face and then we set the last face equal to this face. So keep track of where we are Now if this is not the first face, which is usually the case Unless you have a bunch of one face models that you want to run. Um, we have a different logic here So we'll say body I guess we'll say uh last face next Equals current equals the current face. So face and then Last face equals face. So we'll keep we'll keep this this current face as last face if that makes sense This is all getting very confusing and then now now that we've um gone through that. I want to go back to the sort of uh standard here If you look at the bottom there's actually two bytes of garbage at the end of every triangle. So we have to jump ahead um Two bytes so f seek f to seek currents And honestly that should be pretty much it. We have we have f we can f close now f and um What else should we do we have uh, I'm usually I put something print f Which what should we print um I guess we'll replace what we we just had so we had like This running constantly. Yeah, so we'll repeat that we'll say um print f loaded model this many faces This many nodes Yeah, that's good. And then we'll put a new line finally So we'll pass in what that says file name And then we can actually use our old functions. So count faces body Let's go away to error check as well and count nodes body So if the number of triangles is the matching count faces, we know something is wrong So that's that's good And that point we can just return the body and we're done. That's all this Why so many So that should be it for this um There's a binary one. I'm gonna code up the ASCII one and I'll be back in a few minutes Okay, here it is it's pretty much the same exact logic as the other one Except instead of f read we're using f gets and we're putting that into this very long character array here And um, I have this function from before trim leading white space, but just gets rid of all the tabs and stuff Oh, by the way, I just realized we messed up um Here where it says and loop We also better have an end face Yeah, and face it Right, let me check Yeah, and I messed that up. We didn't have that apparently in our In our right st. I'll ask you we've missed that for some reason didn't matter But yeah, that was a mistake. We should definitely have an end face it anyway, um So yeah, this ASCII thing is done. You can check on the on the repository basically I'm using um S scan f with these parameters here to pull in values instead of using f read Okay, so, um, yeah, it's pretty straight for everything else is pretty much the same Except the way we leave we leave with this I'm checking, um You know, if we ever get to a face normal if we're looking for face normal instead of we get end of file That basically means just okay. Yeah, we're done. So for now we loaded the model So that should be it for this Yeah, we got a binary and we got ASCII. Let me get rid of all this comments. I hate comments Minimal comments all day every day Let's close out of this. I put in the trim leading whitespace to our declaration here. Let's get rid of that Now, um, let's go into our main file So let's let's get rid of all this Yeah, let's get rid of all this Let's keep this part here prism it. So what let me show you what I did. So, um I got this hot air balloon stl for us to check if this works So let's just um Let's check on that. So file one equals hot air balloon stl And then we're going to just uh We're gonna say a struck body star balloon equals Read stl I actually passed in the address, right? I think you're passing the address file one And you know what to check that both of these work that both read Operations work. Let's actually write it out. So let's say So what I want to do is I want to check that both the binary and the ASCII versions of our functional work So to do that, I'm going to use our function for me from yesterday, which is to output a file So I'm going to say file two equals higher volume two to stl and we're going to say, um Write stl ASCII And we're going to pass in the The body and the address of the file so file two So now we'll basically we'll have open hot air balloon We'll have parsed it with our function We'll now be writing as an output as an ASCII stl that file At that point we're going to read it again Structured bodies now we have two read operations just to see if they both work balloon two equals read stl And file two So this is going to be our second read operation So if either of these failed it's going to break. So this is super high stakes Um, now we're going to use this look at stuff. We're going to actually render the Everything was it this is called draw body We're passing in I don't know. I think this is a pretty large, um large model So we'll have to pass in some large values for where we're looking looking at looking from We're passing in the body balloon two So when we look at look we have to be defined. So look at It's going to be a float size three Let's let's have an idea actually hold on that's that's going to be a size three and then Look from That's also going to be a size three. Let's just say 500 and then let's take the values of look at one and two And for one and two Actually one two or zero one and two for look at I actually want to make a new function. So let's say I don't know get body Notal centroid. I want to get like the the center of all the nodes in the body So I'm going to pass in These uh, I guess If I define this So let me go to our John.h Can we avoid Get body node centroid. Um, it's going to be a struct body star body and then a float I don't know. I guess output or something Doesn't matter I guess we'll call it centroid It's not a real centroid. It's kind of fake centroid Let's uh, copy this let's close the file Down to the end of the file Now in this function We have to just compute this it's pretty simple to compute. Let's say struct node star node Equals body node. We're just going to iterate through the nodes in the body We could call that iter I guess but that couldn't care less Let's let's calculate the number of nodes. We have a function for that But it turns it into let's do flow because I want to do float division in a second. So Cast as a float the count nodes in the body Now we can loop through the nodes While the node is not equal in all And now once once we're looping through the nodes, we can actually add to centroid. So we'll say centroid zero equals Rx would have plus equals node x divided by none now you might be saying well what's the point of Computing this number out of the loop make you just compute it in the loop and we could avoid this You know divide every every iteration of this loop Well, the reason is I don't know how many faces are going to be in this model and I don't want to overflow So if we can just Keep track of each you know independent nodes If you know a contribution to the centroid value, you know one by one that would be a little bit better So anyway, uh node equals node next Okay, we'll also have to do here. So This will populate this look at function. Let's look at you know, um Vector based off of the node centroid So we'll have a look at What else do we need anything or are we good? Maybe we could print it out first just to get an idea what we're dealing with. So I'll say print f nodal centroid percent f We'll put that in a parentheses We'll pass in a look at zero Look at one Look at two And this should work Wow That's extremely lucky. It's probably not gonna work though Hey Well, I did I did kind of practice this beforehand. So anyway, look at this. We got a Hot air balloon right here rendered So we at first we did we loaded um This many faces and nodes in our regular balloon stl then we wrote that as our own stl we loaded that So we loaded both a binary and an ascii stl Calculated the centroid and then rendered to the screen That's pretty impressive if you ask me Until next time Thanks for watching