 Hey guys, welcome back skidstone series episode 25 topic today is STL files What they are how they work importing them exporting them? It's a very pervasive CAD file format at least for like 3d printing and very simple geometries It's very simple, but it's also a very kind of inefficient So there are mixed opinions on this file format, but we're gonna use it just for basic IO in this series So what are STL files? Basically they describe a triangulated surface is a body a set of bodies there's a geometry in space that's comprised of triangles essentially and There's two kinds of STL files There's ASCII one so that the actual definition of every single triangle is written out literally, you know with ASCII characters, of course, you know 55.312 is you know an ASCII Implementation of a number so you have to be able to parse that so it's a little bit larger. It's harder to parse for our program But it is easy to read you can open it in a text editor You can you know see what the values are inside and change them on your own binary files, obviously that's just the encoding of all the triangles with zeros and ones So it's not easy to read in a text editor, but of course we don't care. We're going to edit them with our software And obviously they're going to be smaller and easier to parse on our end. So that's one major advantage so For that binary STL format, how does it look? Well, basically there's an 80-byte header, which I believe everything Out there ignores completely Then you have a four byte encoding of the number of triangles in total So if you have 10 triangles, you'd write the number 10 in a four byte number there Then you just loop through every single triangle and write 50 bytes worth of data And that's broken up into 48 bytes of valuable information and two bytes of nonsense Which is just going to be zeros. So of that 48 bytes, there's four basically sets of three single precision affording point numbers those being the XYZ coordinates of Certain things so the first XYZ Vector that you're going to encode is the normal vector of the triangle So you have to compute that somehow and you write that there again in this series We do all pretty much double precision math So we're gonna have to convert our eight byte floats into four byte floats for this to make sense So yeah, we're gonna write a Basically the XYZ components of the normal vector with four byte floats Then you write the actual vertex Information the XYZ coordinates of vertex one vertex two vertex three again with the four byte floats And then lastly the two bytes of zeros So it's very straightforward of a format and you can kind of imagine the inefficiencies of this because Unless you have a scatter field of triangles as what you want to draw or what you want to print out You're gonna have duplicated information because typically a triangulated surface whatever you have It's going to have a boundary between triangles and those two vertices Are the same vertices, but this format is encoding them Separately so you're gonna have duplicated for text information if you use the stl file format. It's just how it's going to be Okay, so exporting this format out is very straightforward if you recall we have this sample geometry This kind of reminiscent of what we're doing in the series We have this data structure here that we've been using for rendering stuff the past couple of videos call it a face structure and it encodes the number of points in our Geometry that we want to draw number of faces in a geometry They want to draw as well as pointers to the arrays for the actual point data and the face data The point data is just literally a list of the XYZ coordinates of every single vertex So vertex zero is the first three Double precision floats here. So XYZ of vertex zero XYZ of vertex one Vertex two vertex three etc, right the face Array basically encodes the indexing into the vertex array. So face zero consists of a triangle with vertices at these points vertex zero vertex two and vertex one and Then it's colored red. This is the r value. This is the g value. This is the b value This is the opacity and this is just the orientation of our bitmap So it's going to be a red triangle with those three vertices and then the second face is going to be Vertex zero vertex three and vertex two as you can see We have duplicated the information for vertex zero and for vertex two And so STL format is less efficient than our internal format for carrying this geometry around rendering it changing it Whatever we're doing with this geometry in our own program Okay, so how do we export this this kind of structure out as an STL? Straightforward opening up the file right that nonsense 80 byte header write the face count. That's this value here Out in a four byte number then you look through the faces first thing we have no Representation of the normal of the vertex well of the triangle normal in our face structure So you're gonna have to compute that right we're not encoding the normal for each face We could that'd be a waste of space though, so we'll have to basically for for face one You have to grab those three triangles vertex with vertex zero vertex two vertex one Compute the normal and the normal is the right-handed rule So take the vector between vertex zero and two and the vertex zero and one Cross product that that points in your normal direction and you can get the normal out of that Only caveat there is everything that we've done is with these quad words, so 8 byte floats Whereas this format uses You can see here real 32. That's a four byte float So you have to basically convert our 8 byte floats and the 4-byte flows, which is not hard There's actually an instruction that you can use to do that So it's not bad at all And then you have to write out that value so write the normal out and you look loop through it right all the vertex data out And then you put that last two bytes of zeros and you loop through that for every single face Obviously, you'd want to buffer that you won't don't want to write two bytes at a time. That would be very slow You'd like to basically put all these things in a single buffer and then write them out at the end of the day That's what we do in this in this series So Importing is basically just the opposite unless you want to be fancy, which we do So basically you'll open up your input file that stl You'll throw out that garbage header 80 bytes then you'll read that face count that four byte face count value You read that in let's say it's 10 You have to then allocate enough bytes for your Face array and your point array structures, right? So you're not gonna have this Yet you've just opened an stl file. You want to create this kind of a geometry So you have to allocate this many bytes for points this many points for the faces, of course We were clever and we Reused points vertex two and one or vertex two and zero I should say We're duplicated across these two faces. There's no idea of that in stl So you'd have to allocate even more memory than you think you'd need If you'd like to just encode everything directly as is written in the stl file so yeah I'll get that many bytes for your two structures then loop through the faces Read that 50 byte data in grab the 12th byte normal just throw it away who cares about that value We don't use that value then look through the vertices and grab each of those 12 byte Groupings of the three four byte numbers convert them into into double precision numbers and write them to our point structure as we talked about before and then you just throw it the last two bytes of zeros and Lastly that will handle all your point information But you will also still have to handle the this stuff down here. I mean this is easy You just grab that you know from your 50 bytes right here. You just grab those values But how do you get the the values here? Well because you're reading this file and you have new vertex every single time you go through right? This is a new vertex. This is a new vertex. This is a new vertex every single time is a new vertex You can just increment by one every time right? So your face structure will basically just say zero one two three four five six seven eight nine ten eleven twelve, but you know Etc. So it's very easy to create this and then if you want to add a color you add a color I think I leave it to be white But you can change whatever color or I think black you can change whatever color you want though obviously Okay, so that's pretty much it now of course before I get into the code We're gonna actually be a little bit more advanced stuff than this we're gonna go through and we're gonna get rid of we're gonna cull out all of the Duplicated vertices which takes a little bit of thinking about how that works. I'm not gonna go through it in this Slide it's a little bit more complex, but you can imagine just going through this array Checking if a vertex has already been used. Hey, I have this point somewhere at point five point five zero in space Oh, hey another point at point five point five zero. It's the same point So I won't use this new point I'll just use the old point instead every instance of this new point. I'll replace with the old point Right, so that's pretty simple Okay, yeah the code now pretty quick We have two examples one is export one is import. Let's make sure you can see You can So exporting makes sense first. So we go into example a I'll remove the binary. I'll remove the cross stl so you can see this thing works So we have code. I'll go into what it does in a second. I'll run it first So we've assembled and executed that Set of instructions. We now have a cross stl I can't show it to you here. I mean I could I could render it in the next example But I'll show it to you on github because they have a rendering tool for Geometries like this you can see here. This is that same Location on this repository And here's that cross stl file. We've had a similar actually the exact same file in a previous Couple of videos, so it should not be you know a new thing for you But you can see here. This is the exact Structure that we've been using previously So yeah, it clearly works. We can export our internal representation of this Geometry into an stl file for use in other programs. You could 3d print this for example, right? Okay, that's cool. How does that work? So let's open up. I Do have I think? Yeah, so this is what's running. It's the actual instructions for that particular executable Headers from before the only new include that we have is this export stl function Which I also have open in the other tab there I won't go through that in a second. But basically all we're doing is we're opening our file output With read write and create permissions. So it's way Those to make the file does doesn't exist already We then run our export stl file with an input of a face structure that we've used in previous videos And then we just exit the program. So what is this face structure? You can see here It's just a definition of the points and faces that comprise that cross and so all these points on the cross All these faces on the cross almost different colors on the cross as well Of course stl files don't contain color information at least not usually So that's all lost when we export and then here's the file name down here cross stl And then it's a null terminated string. So yeah, that's basically what's running Now what's going on behind the scenes in this export stl? function, so let's see This function exports the face structure. Oh Yeah exports the face structure that you pass in to that file that you've already opened and One thing it does do is it uses that same print buffer. I mentioned before that you don't want to be Writing two bytes at a time to your output file So we use our same print buffer from our previous, you know way back video But of course to do that you have to make sure that your print buffer is empty at the beginning So either you have to flush everything out or just set it back to zero And so we just set the print buffer length to zero At the start of this program to basically indicate that everything else that we Had already put in our pen buffer Maybe some debug information or whatever we want to print out is now null and void and we're using that from buffer to export Our new stl file Okay, so you pass in that pointer to that face structure into the program and What does it do? So first off it prints 80 bytes of that header. So what is the header? Well, I just put nonsense in there. You put whatever you'd like. Here's my nonsense then you pass in the four byte value for the number of triangles so you grab the triangle count from your Array your face count and then you just convert that to a four byte number And read it out to your file. So we've done that here. Lastly, you loop through all the vertices and you can see here We're just grabbing the address of those different coordinates for the three vertices and then writing them out to the To a basically well first of we compute the normal from those vertices We have a function called the triangle normal That's we can actually evaluate those vertices and then to do the normal which is great But then next You write that normal information out to the file as well as the three vertices of information here So x y and z values for all that and this instruction here this CVTSD to SS that converts a Scalar double into a scalar single as far as floating point is concerned. That's your 8 byte to 4 byte conversion Okay, and then it prints that out so you can see we have an intermediate buffer that we're using Before our print buffer. So we're basically dropping that normal information those 12 bytes plus 12 bytes of each Vertex information into our triangle buffer and then lastly We actually write that triangle buffer out and that trying buffers down at the bottom I imagine right here the trial over 50 bytes all zeros initialized okay Pretty simple that works as intended. We have the ability to export stl files from our internal geometric representation example be This one was the exact opposite and I will show you the code before I show you the actual Running of the of the program. So how does this work? This is the opposite. This takes an stl file and renders it So it's a little bit more advanced. I would say let me also open up the Import stl Function So yeah, what's going on in this in this program? Basically, we have to render to the screen our geometry to see what we've just imported So we have to include those functions as well as The import stl program and then also function and then also I have this random integer Include just to give us some random face colors for our geometry because I said before there's no color data Past it with stls. So we have to pick our own colors. And so I just picked random colors for every single face So how does this work? We have our cursor definition here as usual then we have Very simple program so open that input file import the stl that generates a face structure you can see here And then we Color the face randomly with this random integer function and then we Have to actually render our geometry because of this before in multiple videos so that requires a prospective geometry as well as the Linked list of geometries that you want to actually draw in this case just one body non-convex body So yeah, and of course you could allocate this member this structure on the heap as well I've defined it here memory just to keep things simple, but you can just as easily have this be done on on the heap Or on the stack potentially So how does that import stl function work? I won't get into too many details because this involves the more advanced stuff of calling out Duplicated vertices you can see here in this case. We have to actually have the ability to Do we even need this? We do I don't think we need that I'll get rid of that right now So yeah, we have to have the ability to allocate and free stuff on the heap in order to Create our data structures for the point and the face information so basically you just Undoing what you just did before so we skip that useless a by the headboard you can see here We use the L seek system call That's the system call that allows you to basically put your cursor Somewhere else in a file. So we basically move the cursor in our file 80 bytes to skip over the header Then we grab that four byte trial account and we use that trial account to allocate our Arrays for the point data and face data. So that's what we do here then we oh We at this point we have enough information to populate that face structure right because we have I mean ideally at this point We allocated this array already. So we have that address. We know what this address is going to be Also this address so we can drop those two things into our face structure But we also have the number of points and faces once you read the number of faces from the file an STF out You know that there's three vertices per face and so this number is just three times this number So that's very straightforward And then you loop through everything and you basically undo what we just did and so in this case you can see here we are looping through we're grabbing 50 bytes at a time from the input file into our Intermediate buffer here called triangle buffer, then we are reading Basically each part of that intermediate buffer. So the first 12 bytes is ignored. It's the normal and then we're grabbing the three the 12 bytes for the first vertex second vertex and third vertex and then you can see we're just converting that value from a Scalar single to a scalar double and then putting that basically in memory affolocation in our point array So very simple there now down here We're actually going through so you could end the program there. That's that's sufficient to make things Work you could just return at this point and you'll have to you basically have everything you need you'll have all this Here you'll have your point structure and your face structure everything will work as intended however, you'll have a bunch of those Duplicated points and so at this point there's some logic actually quite a bit of logic to go through and call out all the duplicated vertices and Make sure you have a correct structure. So I will now show you this program running So it's Let's remove the binder so you can so that it works. Yes, run it as sudo because it has to render things to the Brain buffer Here you can see the cross that we had before Now rendered now you'll see that we've taken the stl file and yeah It works for the most part. You do see some visual glitches. Those are nothing to do with our stl importing That's because this is a non convex shape and you kind of can see We have no idea of which face to draw first for that you have to either one or two things deconstruct this geometry into constituent convex hulls or whatever you call them or Have a depth buffer neither of which I'm doing right now Maybe in the future will implement a depth buffer so you can get rid of this kind of a visual artifact But for now, I think it gets a point across we can basically import an stl file from The internet and get that file in our own internal CAD representation Easy So we can use that for Any kind of engineering purpose you want to compute the the CG of an object you want to compute the volume of an object You want to can do all this kind of different math? You can do it Very easily now, so it's a big milestone for us being able to interact with Outside programs so we can write stl files to then slice up and print on a printer We can make a file in in solid works or in free cat or whatever and then import it into our program So a lot of doors are now open for us So I wanted to cover this topic at this point in the series, so I hope you enjoyed this video If not, that's okay, too. I'll see you in the next one. Thanks for watching