Okay so its time to define our model class both its .h file and cpp file. Lets start with the .h file.
First lets define a vector list made of of different vec3 that our model consists of and then lets.
Then we store each face inside the int list. What is a face? Well every 3 vertexpositions we can draw a triangle, that triangle is the face.
Then for our public class stuff. Our constructor takes a string path (constant so we dont fuzz about with it accidentally later on). Then our functions, quite simple. Constant get functions because we dont intend to change models after they have loaded in right now so our vertex count and face count will not change. Lets take a look at the cpp file now
Lets start simple with this little helper function that takes a reference to str and…
Okay so its time to define our model class both its .h file and cpp file. Lets start with the .h file.
First lets define a vector list made of of different vec3 that our model consists of and then lets.
Then we store each face inside the int list. What is a face? Well every 3 vertexpositions we can draw a triangle, that triangle is the face.
Then for our public class stuff. Our constructor takes a string path (constant so we dont fuzz about with it accidentally later on). Then our functions, quite simple. Constant get functions because we dont intend to change models after they have loaded in right now so our vertex count and face count will not change. Lets take a look at the cpp file now
Lets start simple with this little helper function that takes a reference to str and the reference we have to check if equivalence. Our .obj files for models are essentially text files with data stored in rows, each row starts with v or f and if the program sees those it knows what type of data the numbers after are. Heres an example:
those are the vertex positions for example that we have to read.
Anyways the helper function checks if the str size is larger or equal to the reference, then we actually check if our firstletter is correct with the compare function.
The compare function works like this, takes this string we have and start from this position in the string (0) and continue until the end of this index (firstLetter.size(), then if its equal to this string (firstLetter) then its correct and therefor == 0. 0 Means they are equal.
So lets go through this constructor. We create a ifstream that takes our filepath and tries to find it, if it cant fidn it it throws an error.
Then we go line by line and read the line and if the line starts with the letter v we use a istringsteam to be able to manipulate the string, essentially we skip the first two positions in the string with substr(2) (More acurately we start at 2. Then we feed in our coordinates with istringstream dynamically because istringstream are super usefull and we place them in our vector list but we use emplace_back to create a new vec3 in our vector list.
We do the same thing if the line starts with “f”, which means it defines a face. Each face line in an .obj file contains multiple vertex references separated by spaces — for example:
f 1/2/3 2/3/4 3/4/5
We split the line into separate sections (like “1/2/3”, “2/3/4”, etc.). Each section contains vertex information, where the first number before the slash refers to the vertex index.
To extract that, we look for the first / in the section using find(‘/’). Then we take everything before that slash thats the vertex index we care about using substr(0, slashPos). We convert it to an integer with stoi() and subtract 1, because .obj files use 1-based indexing, but us programmers like to start at 0.
Then we just store that int in our int list.
getVertexCount we return the size of our vertexPositions list. Facecount we return how many faces our object has, its the amount of faceVertexIndices / 3 because 3 vertices make up a triangle duh. getVertex gets you the vertex from the index you send in. getVertexFromFace() This method takes a face index (yknow triangle) and a corner number which represents a vertex of that triangle then looks up which vertex that corner refers to and returns its 3D position. So for this to make sense we have to remember that our faces are defined in our .obj file as “f 1/2/3/” etc etc, so were essentially saying, gimme the second vertex (in plain english)! It would return the number 2 in that.
Next lets head back into our main c++ file. So our models are in 3d right but we are presenting our models on a 2D plain, we have to have a function that converts 3d space into 2d space projection.
Alright so what this function does is take our 3D vertex coordinates that go from -1 to +1 (you know, model space) and remap them to 0 to screen size. We basically stretch our model to fill the whole screen. Then we just return those two values (x and y) so we can use them as pixel coordinates when drawing lines between points.
Here we see were we instantiate our model and declare its file path but also how we draw it out!
So once our model has loaded we create my favorite type of for loop which i use for like everything lol. But we declare a starting int variable and then we add + everytime we complete the loop and it finished when the model runs out of faces. So we retrieve the vertexes from their face respectively, this is where we get to use our getVertexFromFace function finally and here we also confusingnly get to use programming language with our index “Start at 0 for the 1 position this time!”. We assign those vertices and then we project those vertices via our project function in order to figure out where they should be on our screen.
Then finally we draw our beautiful lines, and by the end we will have this beautiful triceratops that totally does not look like a big blob.