This tutorial demonstrates how to create a custom OBJ parser in Blender's Geometry Nodes by parsing OBJ file data stored as strings, splitting the data into vertex and face lists, converting string coordinates to vectors, and assembling triangles from vertex indices to reconstruct 3D models like Suzanne, Stanford bunny, and Stanford dragon directly within the node system.
Deep Dive
Prerequisite Knowledge
- No data available.
Where to go next
- No data available.
Deep Dive
OBJ is just a really long list - Geometry Nodes
Added:In geo nodes, we already have primitives like the cube, the sphere, the cone, etc. But why should we stop there when we can have baked in primitives like Suzanne or we can have the Stanford bunny or you know the Stanford dragon.
Importantly, this isn't like a import OBJ node. The info is baked into the node. All the information of the dragon is stored in this giant string node.
Whereas something like the import OBJ node takes in a path, but you can't store the data in here. In this video, I want to talk about OBJ parsing and how we can have very nice primitive nodes right in geo nodes. This video is sponsored by Squarespace and we're going to talk about that later. Imagine we had any model, like for example, a monkey.
I'm going to triangulate it just so we can assume every single face has three vertices. Of course, this can be generalized to quads or whatever. And for this object, I'm going to export OBJ. Call it Suzanne. I don't need the normals or the UVs, I guess. Also, no materials. And export that OBJ. This gives us a standard OBJ file, but what you might not know is that it's very readable. I'm going to open this up in a text editor. What you're going to see is we have O Suzanne, which stands for the object Suzanne. And then we have a list of V and then a bunch of numbers. This is saying the first vertex has this 3D position. The second vertex has this position. This goes on for like 500 or so vertices. The next thing other than this S, which we can ignore. We have these uh face entries. Unlike the vertices, this doesn't have raw position data. It's actually saying the first face has three vertices because it's a triangle. Specifically, the 47th, the 3rd, and the 45th. This just keeps going for every single face. You can pretty much think of this as we have a triangle somewhere in space. We know that it has certain like vertices like the one with index 2, the one with index 15. Then we also have a corresponding list of vertices, each one with their own position. We can then reference what their positions are and then assemble the triangle. Let's make a new geo nodes group for our input. Instead, I'm going to use a string, which in 5.2 2 kind of has this nice new UI where I can make a really really big string, type something in, not hit enter, but instead hit shift enter and go to new lines and type in more data. Take all of this and copy it and literally just store it as a massive string. Now, we're going to talk about how you parse this sort of thing. In Blender 5.2, there's this new concept of lists, which you can think of as a list of numbers, of vectors, indices, whatever it is you want, which makes processing this super super simple. If I take this string and cut it line by line by line, I can use a split string node, which you can see now has this new list output, which right now isn't really going to show anything interesting. But for our separator, how do we know when one string ends and the next one begins?
Use a special character saying I want to look at every line break. We get this giant giant list where every line corresponds to one from the string.
Turns out these lists are super super nice to work with. If I want to extract only the vertices from this list, I can use a filter list node, take in a list, in this case a string list, and we can separate it based on a criteria. Vertex rows we know start with the letter V and then a space. So I can use something like a match string node and check which ones start with the letter V and a space. That is going to be our criteria.
And you can see all of the sudden we get our V list and then everything that isn't that. Can of course do the same thing with faces. Check for every single string that starts with F and then a space. And now we have our face list and our vertex list. Literally that simply.
One issue is our vertex list which basically tells us the position of 3D vertices. These are supposed to be vectors. Right now they're just a string of three numbers with this v space etc. I need to take this string list and convert it into a vector list. I know I can get rid of the very beginning of the string. Find where it says v and then space and then replace it with nothing.
So this effectively deletes the beginning of it. Then we need to separate each row into three entries. We already know how to do that with the split string node. However, you can't just like split a list. We have to do it entry by entry by entry. So, it's kind of confusing when you can go in and out of lists. What I'm going to do instead is I'm going to do a field to list node.
This is the node that lets us construct lists kind of like out of nowhere. In this case, we'd want it to be a vector list, which I can just call like positions. This isn't a single input. It is a list of positions that are like, I don't know, 35 long or whatever it may be. How do we know how long each list needs to be? take a list length node which tells me the length of any kind of list and plug this in here. And now I can replace this list with a list of positions. We can extract it one by one where I'm going to use a get list item node. So you're going to get familiar with these soon. Use a string list and now I can extract them one by one by one. This is what I need to convert into a vector. I'm going to use my split string node which will let me take this per entry thing and convert it into a list where our separator is going to be the space. Now for every single index of our vertices we can get the x y and z positions. Then of course we need to recombine this into a vector. The way we do this is we take every single string and convert it into a value and then we can extract this item per item using the get list item node which now is using floats. If I take the first second and third index where indexing starts at zero I get the first second and third item that in this case exactly correspond to the position string of a certain index. You can see it's basically split into its uh components.
This I can combine into a single vector.
So now this whole list paradigm, big deal. You got to you got to stay up to date with it. It's this vector that I want to turn into a list. However, you're going to see an issue where every single entry is the same. This of course makes sense if we're looking at the exact same vertex. So you might think, okay, I'm going to look at the index of every single one and it still doesn't work. Here is where we get another concept, which is the closure to list node. What this basically does is it lets me make a list based on a certain rule. Well, we pretty much did all of the work over here. So, I'm going to turn this into a node group where I also care about the index saying which list item am I taking and converting into a vector string to vector. Throw this into a closure where I take the index and then this outputs a vector. Then this closure is going to get that nice closure to list node. Evaluate this closure. Hit refresh and you can automatically see I get this like vector output. And now instead of using this like field to list I was talking about before, I can instead use this closure to list where we know how many items we have. Then this kind of like vector output. If I now view my closure to list, you can see once again we have a list but this time of vectors. Something that's very important about this closure to list concept is your input needs to be an index. It can't be anything else.
For example, we're looking at the list and saying what is its first index? Give me an output. Second index, give me an output. And long story short, everything we did here took our vertex list and sent it to the same thing but in vectors. So that can be a node group vertex vectors. But let's not forget we also have this list of faces that look at corresponding vertices. This is also something we want to uh filter. So I'm going to use this replace string node where I want to get rid of this f in the beginning. So f space now gives us a list of indices that we can use to look at the positions. And then for each face I assemble a triangle. And it's literally that simple. Before I continue, I want to tell you that this video is sponsored by Squarespace. This is the best place on the internet to make a website for the internet.
cgmatter.com is made and hosted with Squarespace. This is where I have my free nodes and blog and whatever. And here are three reasons that I choose Squarespace. First of all, they have a integrated payment platform. This is Squarespace payments which makes giving a blend file for like a monthly subscription very easy to do. Relatedly, second of all, the asset browser or asset library lets me host files like photos or video, just useful things. And then thirdly, when you're populating your website, especially if you have monotonous tasks, you don't need to go to some separate AI chatbot thing cuz it is integrated right in there. you can head over to Squarespace and build your website because there is a 14-day free trial that you can just do this and then when you're ready to take that website and launch, you can use this link below in the description to save 10% off your first purchase of a website or a domain.
Now, back to the tutorial. For each face, I assembled this thing of three vertices. We can make use of a for each element which will let me do this operation however many times. Let me just simplify this by turning all of this into a node group where we have both of these list outputs vertex positions face strings. So, how many faces are we trying to make? Well, it's exactly how many face strings there are in this list. List length node. In this case, there are 967 faces, apparently.
Then, weirdly enough, this for each element kind of loops or uses geometry.
So, I'm just going to do a bit of a hack where I'm going to create a bunch of points. The number of points I make is exactly the number of faces. I'm not really concerned with where they are.
Each face on the list I can just do something to. Really, all we need is a triangle, which I can use a mesh circle for using only three vertices. This is what a mesh circle looks like with three vertices. Fill this with triangles. And now we just say move this vertex somewhere. Move this vertex somewhere based on our list. Remember each face is composed of three indices that eventually are going to turn into vectors is what we care about. Let's say hypothetically I care about the first face. So we have 47, 3, and 5. I want to turn this into the three vectors we calculated using vertex positions. Just like every single time I'm going to split string where the separator is a space. So now we have 47, 3, and 45.
These are three indices that we use in our vertex position. Get list item of the first, second, third. Making sure it goes 0, 1, 2, because indexing starts at zero. These are indices where we now reference this list over here. Dang it, it's getting confusing. Do a get list item where we care about a vector. First going to look at this index, which happens to be 47. However, this is a string. I'm just going to convert this into a nice uh float or even an integer where I'm now looking at this item.
Duplicate this and duplicate it again.
We're looking at the same list. Now we're basically just going to get three vectors that are corresponding to that face. This is going to be the case with every single index. This is something that should be a node group where one of the inputs should definitely be the index saying what face are we looking at. Face index. Then the outputs are three different vectors which we could combine into a list but I'm just going to keep it simple. 1 2 3. So now what we need to do is for every single point which again represents a face in this like weird way use its index as the face we're looking at. Then for our mesh circle that is composed of triangles, I need to move verse vertex to position one etc. I can do this in three different set position nodes or I could do it in one swing. We're essentially going to make a list where I'm going to use a index switch node to get our first, second, and third position. This is exactly the same concept as a list.
This index is going to be the index of the point. So I have a bunch of points where I'm now kind of looping or doing four each. Generate a triangle that has indices 0, 1, and two. For index zero, I'm going to give it the first position.
Index one, the second. This can be our set position. And then this, of course, is what we're outputting the culmination of all of these. And now the big reveal, which it's not going to work, but the big reveal. I view this and okay, we can definitely tell it's a Suzanne. This is progress. But it's almost like, what happened here? It's almost like everything got compressed to the middle over here. And this is important. Why did this happen? One thing I neglected to mention for dramatic effect I guess is if you look at the face strings you're going to see if you look very very carefully none of these have the number zero anywhere. It can reference vertex 1 38 whatever but nowhere is it going to reference vertex zero because obj format you start with like index one. You can see right here we have 1 93. It will never get lower than one.
Our issue is our vertex position list starts at index zero. So we just need to offset everything by one. So if I'm telling the truth, I swear I am. What we need to do is to go into this node group. By the way, I'm just going to call like get position. If I go into here, if you remember, ultimately the way you read this is we have a face, a specific index of face. I look at its three vertex indices it corresponds to and then I extract its three positions.
However, the index is off by one. It's always going to be one bigger than it should be. So, if I take this and I'm going to subtract by one by one, you can see indeed this gives a different result and it does kind of get rid of this like biggest face that that is like over here. However, we're still kind of getting this weirdness. Now, this took me a moment to figure out. It turns out that the reason this happens is kind of silly. It's kind of stupid. The mesh circle, this like triangle over here, is actually composed of a triangle fan. So, you expect this to be a single face, but it's composed of three. We have an extra vertex that we deal with. We have four vertices instead of three. If I set this to engon, immediately we get this triangle. Now, it's time for the big reveal. If I take this and I switch it to engon, now we have this very nice mesh. We basically took a bunch of triangles and laid them one by one by one. Here you can see how this is built up as we increase our like number of points. But those are independent triangles. They're never connected. So at the very end here, what I need is a merge by distance. It will visually look the same, but we cut down our number of vertices significantly. Now we have our Suzanne. Something I want to note is that at no point did we import an OBJ.
Yes, we exported it to get like the raw data, but at no point did we import it.
Why is this important? What it means is if I go into my node group with this big string, I can replace it with anything.
If I go into my OBJ folder, you can see I exported quite a few. Let's actually open up this bunny OBJ. This is the Stanford bunny. I'm just going to copy this. Take this whole string and paste.
And you can see we automatically get a bunny. Also, interestingly, this only took 53 milliseconds, which by the way can be sped up significantly using some tricks of lists. We don't want to split as often as we are. There are times where we can utilize lists, blah blah blah. Generally, it's pretty fast, even for like 6,000 faces. Let's open a big boy, the Stanford Dragon. Copy all of this, swap it, and it does take a moment longer, but now we get this dragon. It still took only a tenth of a second.
Something you might notice that is quite important is normally when we export an OBJ, we still export the UV map. And it turns out, yes, we can take this process and not only assign the vertices and the faces and all this, but we can assign the UV map. If I take this dragon and replace it with my own dragon node, which you can see has a UV map. Going to view this. And let's also view it with its UV map. You can see that this is also possible. It's kind of an extension of what we did over here where you can see inside the node group over here, I not only take the vertices and the faces, but I also do some tricky stuff with the UVs that involves face corners.
To learn about that, you can go to my website cgmatter.com where I'm going to do kind of like a extension companion tutorial cuz I feel like you can figure it out from here. But if you want the more stepbystep cuz it is a bit more complicated, you can go to my website.
The nodes are free. Go to my website, check it out. That that's my pitch uh for the end of this. We did our own OBJ parser which was really an introduction to
Related Videos
LBF101 Creating an XML Changelog
liquibase7511
3K views•2026-06-15
Alta Labs Cloud Dashboard Real time Network & Xnet Insights!
ShinyTechThings
158 views•2026-06-17
Wait... Group Policy Not Applying? Check This First!
keeplearning_iT
144 views•2026-06-15
Leetcode Weekly Contest 506 | Life's boring these days
Pudeesht
2K views•2026-06-14
microJAM: MAKING A MICRO GAME FOR A GAME JAM IN CLOJURESCRIPT AND TOTALLY NOT C
janetacarr
156 views•2026-06-18
Partitioning vs Bucketing vs Clustering: How to Make Queries 100x Faster
thedataandaiguy
194 views•2026-06-16
Design Claude Code Like a Senior Engineer
hayk.simonyan
344 views•2026-06-19
Linus Torvalds: AI Won’t Replace Understanding Code
SavvyNik
140 views•2026-06-19











