Sei sulla pagina 1di 7

OK, in the last tutorial I explained how to set up some arrays to store and draw model information.

With that out of the way, how do we load information from a model file into our arrays? Well, we will
use the wavefront (.obj) model format to do this since it is a very easy format and supported by a
number of modeling programs. Let me first introduce you to the structure of the files...

~Vertex information~
First I will explain how the vertex information is stored in the file, its like this...
Each vertex of the model has it's own line. The file tells us that the current line is a vertex by starting
the line with the letter "v"
Then, the X, Y, and Z of that vertex follow the "v" on the same line, all seperated by a space. So, if
there were three vertices in the model, the vertex portion of the file would look like this:

v 1 2 1
v 7 3 4
v 9 2 1

So, the first vertex would be the point (1, 2, 1)... The 3rd vertex would be the point (9, 2, 1).
This is a very easy format to read from. I dont think I need to explain this any further as this is very
easy to understand.

~Face information~
The face information of the file is exactly the same as the vertex info, except the line will start with the
letter "f" so... A model with only three faces would look like this:
f 1 2 3
f 2 3 4
f 4 1 2

Naturally, the first face would connect the 1st, 2nd and 3rd vertices... And the third face would connect
the 4th, 1st and 2nd vertices. Once again... It doesnt get much simpler than this!

~Loading the information~


I am writing this to ONLY load triangulated faces, so this will not properly load a model which has
quads as faces... Luckily almost EVERY modeler out there has an option to convert quads to triangles,
it is just easier this way.

Ok, first we will dim our variables.


dim Vert#(1000)(2)
dim Face(1000)(2)
dim Faces
dim Verts
dim File
dim Type$

The first two variables should be familiar from the last tutorial... Except we have used 1000 to dim the
number of faces/verts... You can adjust this if the model file is bigger, but since we need to dim them as
something... Just pick a big number for now.
Then you will see the variables "Faces" and "Verts" these are what we will use to hold the number of
faces and vertices that we read from the file... Then you will see that variable "File" which we will be
using to open the model file.
Then you will see the variable "Type$" this is used to read from the file and hold the "v" or "f" which
will tell us whether to load a vertex or a face.

Ok, with that out of the way... Lets go ahead and load ourselves a model!

'Dims
dim Vert#(1000)(2)
dim Face(1000)(2)
dim Faces
dim Verts
dim File
dim Type$

'Open file
File=OpenFileRead("Model.obj")

'Read info and store in proper array


do
Type$=lcase$(ReadText(File, True))
if Type$="v" then
Verts=Verts+1
Vert#(Verts)(0)=val(ReadText(File, True))
Vert#(Verts)(1)=val(ReadText(File, True))
Vert#(Verts)(2)=val(ReadText(File, True))
elseif Type$="f" then
Faces=Faces+1
Face(Faces)(0)=val(ReadText(File, True))
Face(Faces)(1)=val(ReadText(File, True))
Face(Faces)(2)=val(ReadText(File, True))
endif
loop until EndOfFile(File)

Thats it, thats all you need to load the model! Let me explain what is going on...
• The Do/Loop: This is what keeps us loading info from the file. Notice the "until
EndOfFile(File)" after the loop... This is to tell the program to stop the loop when it has read all
of the info from the file.
• [Type$=lcase$(ReadText(File, True))]: This reads the "v" or "f" from the file... Why the
"lcase$"? Just in case they are stored in capital letters, so we dont have to worry about missing
anything.
• [if Type$="v"] and [if Type$="f"]: This checks the letter that we read from the file... If it is a "v"
then we want to save the info to the vert#()() array, but if its a "f" then we want to save it to the
Face()() array.

And thats that. If you think back to the previous tutorial you should remember how to draw the model
stored in the "Vert#()()" and "Face()()" arrays... It is the exact same process with these models too!
~Calculating Normals for lighting
Ok, now that we can load our models from a file, or create them in data tables within our program... We
can draw them, but they dont look too good... What we really need to make them look better is some
lighting. I will first explain how to turn on an OpenGL light in basic4gl so that you can light your
models...
'Turn lighting on with this command:
glEnable(GL_LIGHTING)

'Turn on Light #0 with this command


glEnable(GL_LIGHT0)

'Set position of Light #0 with this command


glLightfv(GL_LIGHT0, GL_POSITION, Vec3(0, 0, 0))

'Set the ambience of the light with this


glLightfv(GL_LIGHT0, GL_AMBIENT, vec4(.2,.2,.2,1))

'Set the diffuse of the light with this


glLightfv(GL_LIGHT0, GL_DIFFUSE, vec4(.5, .5, .5, 1))

Ok, some notes:


• You may have up to 8 lights by changing GL_LIGHT0 to... say... GL_LIGHT5, just remember
to turn it on.
• Ambience is basically the color when NOT in direct light... It mainly sets the dark color
• Diffuse is the color OF the light itself

Ok... So you have a light, but it looks like crap and doesnt really light the models! This is an outrage!
Well... Actually thats where the normals come in. There are two kinds of normals... Per-Face normals
and Per-Vertex normals.

Naturally, the Per-Face normals apply to an entire face, so lighting isn't as good as per-vertex, but its
much faster to calculate and is still much better then no normals.

Per-vertex normals require you to calculate the normal for each vertex in the model... This allows for
much smoother lighting, however, it takes alot longer to calculate them

The one good thing is that we only need to calculate them once... When we load the model. Then we
can load them into an array and use those normals when we draw the model. So, we will not be
decreasing our framerate any. Now, to do this... I am assuming you are using the system I have been
using throughout this lesson to store your models, these are the variables I will be using for the code.
So, first, lets calculate our Per-Face normals...
dim f(2)
dim FNorm#(Faces)(2)

'#Code to load model goes here#

for i=1 to Faces


f=Face(i)
FNorm#(i) = Normalize(CrossProduct(Vert#(f(1)) - Vert#(f(0)), Vert#(f(2)) -
Vert#(f(1))))
next

Ok... All you need to know is that the normals are actually just perpendicular lines to the face. That is
what the formula is for, the crossproduct returns the perpendicular line, and then we normalize that
vector. What we are left with is the array FNorm#()() which has 3 values for each face... Those are the
per-face normals! Then... To use these normals, you must set up your lighting as I showed previously,
load your model... Calculate the normals... And then when you draw the model, use this...
for i = 1 to Faces
f=Face(i)
glBegin(GL_TRIANGLES)
glnormal3fv(FNorm#(i))
glVertex3fv(Vert#(f(0)))
glVertex3fv(Vert#(f(1)))
glVertex3fv(Vert#(f(2)))
glEnd()
next

Now thats a tiny chunk of code when you consider, thats what is actually drawing our models!

Alright with those face normals out of the way... Dont get rid of those... We actually need them! That is
how you calculate the vertex normals... In theory it is simple: Take the average of each face normals
that connects to the givin vertex. What you will get is the vert-normals. So, we are scanning through
each vertex... Then scanning through each face to find out if it has that vertex in it... Then we can add
the normal for that face to the vert-normal... And when its all said and done, we normalize that and we
have our vertex-normals!!! Heres the code...
dim i2, i3
dim VNorm#(Verts)(2)

'#Place code to load models and calculate face normals here#

for i=1 to Verts


for i2=1 to Faces
for i3=0 to 2
if Face(i2)(i3)=i then
VNorm#(i)=VNorm#(i)+FNorm#(i2)
endif
next
next
VNorm#(i)=Normalize(VNorm#(i))
next

Alright, with our per-vertex normals calculated... We just have to use them when we draw our model!
Here is how we do that:
for i = 1 to Faces
f=Face(i)
glBegin(GL_TRIANGLES)
glnormal3fv(VNorm#(f(0)))
glVertex3fv(Vert#(f(0)))
glnormal3fv(VNorm#(f(1)))
glVertex3fv(Vert#(f(1)))
glnormal3fv(VNorm#(f(2)))
glVertex3fv(Vert#(f(2)))
glEnd()
next

And thats how you draw your vertex normals...

Ok, now for the biggy... This is everything together, and a little more... Here it is:
'variables
dim i, i2, i3
dim f(2)
dim Vert#(100)(2)
dim Face(100)(2)
dim Faces
dim Verts
dim Type$

'Vertex values
data "v","01","01","-1"
data "v","01","-1","-1"
data "v","-1","-1","-1"
data "v","-1","01","-1"
data "v","01","01","01"
data "v","01","-1","01"
data "v","-1","-1","01"
data "v","-1","01","01"

'Face values
data "f","5","1","4"
data "f","5","4","8"
data "f","3","7","8"
data "f","3","8","4"
data "f","2","6","3"
data "f","6","7","3"
data "f","1","5","2"
data "f","5","6","2"
data "f","5","8","6"
data "f","8","7","6"
data "f","1","2","3"
data "f","1","3","4"

data "end"

'Read info and store in proper array


do
Read Type$
Type$=Lcase$(Type$)
if Type$="v" then
Verts=Verts+1
Read Type$
Vert#(Verts)(0)=val(Type$)
Read Type$
Vert#(Verts)(1)=val(Type$)
Read Type$
Vert#(Verts)(2)=val(Type$)
elseif Type$="f" then
Faces=Faces+1
Read Type$
Face(Faces)(0)=val(Type$)
Read Type$
Face(Faces)(1)=val(Type$)
Read Type$
Face(Faces)(2)=val(Type$)
endif
loop until Type$="end"

'Setup lighting
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glLightfv(GL_LIGHT0, GL_POSITION, Vec3(0, 2, 2))
glLightfv(GL_LIGHT0, GL_AMBIENT, vec4(.3,.3,.3,1))
glLightfv(GL_LIGHT0, GL_DIFFUSE, vec4(.7, .7, .7, 1))

'Load per-face normals


dim FNorm#(Faces)(2)
for i=1 to Faces
f=Face(i)
FNorm#(i) = Normalize(CrossProduct(Vert#(f(1)) - Vert#(f(0)), Vert#(f(2)) -
Vert#(f(1))))
next

'Load per-vertex normals


dim VNorm#(Verts)(2)
for i=1 to Verts
for i2=1 to Faces
for i3=0 to 2
if Face(i2)(i3)=i then
VNorm#(i)=VNorm#(i)+FNorm#(i2)
endif
next
next
VNorm#(i)=Normalize(VNorm#(i))
next

'Variables for rotation


dim xspin#
dim yspin#

do
'Clear screen and setup position and rotation
glClear(GL_DEPTH_BUFFER_BIT or GL_COLOR_BUFFER_BIT)
glLoadIdentity()
gltranslatef(0,0,-5)
glrotatef(xspin#,1,0,0)
glrotatef(yspin#,0,1,0)

'Draw model
for i = 1 to Faces
f=Face(i)
glBegin(GL_POLYGON)
glnormal3fv(VNorm#(f(0)))
glVertex3fv(Vert#(f(0)))
glnormal3fv(VNorm#(f(1)))
glVertex3fv(Vert#(f(1)))
glnormal3fv(VNorm#(f(2)))
glVertex3fv(Vert#(f(2)))
glEnd()
next

'Update screen
swapbuffers()

'Spin model
xspin#=xspin#+.125
yspin#=yspin#+.25
loop

Ok, basically the only thing in this that I DIDN'T explain in any of these lessons what the data table, I
just set it up like a ".obj" model and read it as if I were loading a file... You can replace the loading
portion with the wavefront loading routine as explained earlier in this lesson and then load your own
models, or you could make a program that dump's your models to a data table, then just paste that in
your program! Enjoy...

Potrebbero piacerti anche