I was originally going to have including saving of the mesh information into a data file to load directly into an opengl scene, but I decided to instead just include the generation code into the opengl program so that you could generate several random trees on the fly if you wanted. Also, everyone has a different preferred method for saving information, so I'll leave that to the individual.
The purpose of this section will be to give our tree mesh a skin by applying the mesh coordinates to a texture as well as finding the normals for the polies for lighting.
Included in this next file is an opengl resource set that I've developed and have been using with many of my test programs. Most of the Routines are not necessary for our program, but here are a few of importance:
sub cross2 (x1!, y1!, z1!, x2!, y2!, z2!, x3!, y3!, z3!, nx!, ny!, nz!) - This sub takes in 3 3d coordinates and produces a normal vector that is found by taking the cross product of the 3d vectors
sub deinit () - Shuts down the program
sub load_texture[Alpha] (texFil$, tex as gluint) - These 2 routines are used for loading our textures into the scene. The 2nd parameter tex as gluint is a gluint that serves as a handle to an openGL texture.
sub selectRes () - The select resolution screen that pops up in the beginning. Ths doesn't really require glfw, but that just happened to be how I wrote it before finding out this can be done with SDL alone.
Here is a link to the basecode for our openGL tree generator: glbase.zip
This is essentially very easy. We take the original display code that figured out all the polygon points and before drawing them we perform a cross product to get the lighting normal, and we assign texture coordinates to each point.
Here is a blown up view of what we're going to do:

For each of the quads that make up the branch we'll map each point to a specific u/v coordinate on the texture. As you can see from the image we start with the upper left corner and proceed to the upper right in a counter clock wise fashion. For the Triangles that make up the end points we use u/v coordinates (0,0)->(.5,1)->(1,0) this way the point is at the bottom center. Since it is a triangle, we only need three u/v coordinates.
At this same time we perform our cross product by passing the first 3 coordinates for the quad and all 3 coordinates for the triangle. We only need the first 3 coordinates for the quad since it is a flat plane... relatively, heh, there is a bit of twisting, but for the most part its very close. If you wanted to be really picky you could perform a cross product for each individual vertex and average them together to make a nice round branch, but that is beyond the scope >)
The first part of the tree code for generation has been lumped into the function generateTree (). There are really no alterations to is. We just need to dim shared nBranches at the beginning for our drawing routine. Instead of using pnti(4,1) we'll be using pntf(4,2) to store our 3d coordinates for drawing. This is due to the fact that we're no longer using getXY since opengl is doing all the projection work for us. Also, we need to include leafscale! so that our drawing routine can keep track of how large to scale the leaf planes.
I've also added the pixel grid to the bottom of the tree as it was in the original generator.
Here is our new drawing routine. This takes our old line drawing and converts it to use for opengl. It is essentially exactly the same. It just skips getXY and uses glVertex3f to define our line points.
sub drawTree ()
glBegin GL_LINES
glColor3f .5, .25, 0
for branch = 0 to nBranches - 1
nNodes = branches(branch).nNodes
for node = 0 to nNodes - 1
for p = 0 to MAX_SEGS - 1
n = p + 1
if n = MAX_SEGS then n = 0
pntf(0,0) = branches(branch).nodes(node).plane(n).x
pntf(0,1) = branches(branch).nodes(node).plane(n).y
pntf(0,2) = branches(branch).nodes(node).plane(n).z
n = p
pntf(3,0) = branches(branch).nodes(node).plane(n).x
pntf(3,1) = branches(branch).nodes(node).plane(n).y
pntf(3,2) = branches(branch).nodes(node).plane(n).z
nn = node - 1
if nn > -1 then
n = p + 1
if n = MAX_SEGS then n = 0
pntf(1,0) = branches(branch).nodes(nn).plane(n).x
pntf(1,1) = branches(branch).nodes(nn).plane(n).y
pntf(1,2) = branches(branch).nodes(nn).plane(n).z
n = p
pntf(2,0) = branches(branch).nodes(nn).plane(n).x
pntf(2,1) = branches(branch).nodes(nn).plane(n).y
pntf(2,2) = branches(branch).nodes(nn).plane(n).z
refs = 4
else
pntf(1,0) = branches(branch).root.x
pntf(1,1) = branches(branch).root.y
pntf(1,2) = branches(branch).root.z
refs = 3
end if
if refs = 3 then
'triangle
glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
else
'quad
glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
glVertex3f pntf(2,0), pntf(2,1), pntf(2,2)
glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
glVertex3f pntf(2,0), pntf(2,1), pntf(2,2)
glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
end if
next
next
next
glEnd
end sub

The leaf planes are left out to aid in describing the skinning process.
Our tree will use 2 textures. One for the leaf plane and one for the bark. They will require 2 slightly different loading routines due to the fact that the leaf plane has an alpha channel so you can see through the leaves.
This requires the following code around the init call:
DIM SHARED barkTex as gluint, leafTex as gluint init 'not new load_texture "bark.png", barkTex load_textureAlpha "leaves.png", leafTex
Bark: 
Leaves: 
The actual loading of the textures is beyond the scope, but if you would like to learn more please visit: NeHe Lesson 06: Texture Mapping
Instead of using lines, we'll now use Quads and Triangles. And each time we'll use glTexCoord2f to map a u/v coordinate to each point. Remember when drawing polygons to have them follow the correct point order (CCW or CW) depending on your implementation so that they are not backfacing and possibly culled.
Part of our addition to this routine will be having it draw in 2 passes. Once for the quads and once for the triangles. This is so that we don't have to call glBegin for each polygon, only twice. The Bold code below is new.
sub drawTree ()
glBindTexture GL_TEXTURE_2D, barkTex
glColor3f 1, 1, 1
for pass = 0 to 1
if pass = 0 then glBegin GL_TRIANGLES
if pass = 1 then glBegin GL_QUADS
for branch = 0 to nBranches - 1
nNodes = branches(branch).nNodes
for node = 0 to nNodes - 1
for p = 0 to MAX_SEGS - 1
n = p + 1
if n = MAX_SEGS then n = 0
pntf(0,0) = branches(branch).nodes(node).plane(n).x
pntf(0,1) = branches(branch).nodes(node).plane(n).y
pntf(0,2) = branches(branch).nodes(node).plane(n).z
n = p
pntf(3,0) = branches(branch).nodes(node).plane(n).x
pntf(3,1) = branches(branch).nodes(node).plane(n).y
pntf(3,2) = branches(branch).nodes(node).plane(n).z
nn = node - 1
if nn > -1 then
n = p + 1
if n = MAX_SEGS then n = 0
pntf(1,0) = branches(branch).nodes(nn).plane(n).x
pntf(1,1) = branches(branch).nodes(nn).plane(n).y
pntf(1,2) = branches(branch).nodes(nn).plane(n).z
n = p
pntf(2,0) = branches(branch).nodes(nn).plane(n).x
pntf(2,1) = branches(branch).nodes(nn).plane(n).y
pntf(2,2) = branches(branch).nodes(nn).plane(n).z
refs = 4
else
pntf(1,0) = branches(branch).root.x
pntf(1,1) = branches(branch).root.y
pntf(1,2) = branches(branch).root.z
refs = 3
end if
if refs = 3 and pass = 0 then
'triangle
glTexCoord2f 0, 0
glVertex3f pntf(0,0), pntf(0,1), pntf(0,2)
glTexCoord2f .5, 1
glVertex3f pntf(1,0), pntf(1,1), pntf(1,2)
glTexCoord2f 1, 0
glVertex3f pntf(3,0), pntf(3,1), pntf(3,2)
elseif refs = 4 and pass = 1 then
'quad
glTexCoord2f 0, 0
glVertex3f pntf(0,0), pntf(0, 1), pntf(0,2)
glTexCoord2f 0, 1
glVertex3f pntf(1,0), pntf(1, 1), pntf(1,2)
glTexCoord2f 1, 1
glVertex3f pntf(2,0), pntf(2, 1), pntf(2,2)
glTexCoord2f 1, 0
glVertex3f pntf(3,0), pntf(3, 1), pntf(3,2)
end if
next
next
next
glEnd
next
end sub
First we Bind our texture to GL_TEXTURE_2D so that it shows on our polygons. Next we have our 2 pass system. At the end you see how we changed the drawing commands to include the texture coordinates.
To have this tree lit, we need to generate a surface normal for each polygon. This can be performed by simply calling cross2 (...) before our drawing takes place. Then including glNormal3f nx!, ny!, nz! before the drawing of each poly
Add this code before if refs = 3 and pass = 0 then
cross2 pntf(0, 0), pntf(0, 1), pntf(0, 2), pntf(1, 0), pntf(1, 1), pntf(1, 2), pntf(3, 0), pntf(3, 1), pntf(3, 2), nx!, ny!, nz! glNormal3f nx!, ny!, nz!
Before:
After:
That's it! Our tree is skinned! Lastly we'll move on to finishing up the openGL scene by adding the transparent leaf planes and making a small random forest!
First we will be including in the leaf drawing code from the first generator. Upon writing this part of the tutorial i realized that the X coordinates for the planes in pnt() are backwards. This was causing them to be culled.
This code will be added directly to the end of drawTree ()
The code additions are in Bold
'draw the leaf planes:
pnt(0,0) = .5
pnt(0,1) = -.5
pnt(1,0) = .5
pnt(1,1) = .5
pnt(2,0) = -.5
pnt(2,1) = .5
pnt(3,0) = -.5
pnt(3,1) = -.5
glDisable GL_LIGHTING
glBindTexture GL_TEXTURE_2D, leafTex
glColor4f 1, 1, 1, .8
for leaf = 0 to MAX_LEAVES
glPushMatrix
glTranslatef leaves(leaf).x, leaves(leaf).y, leaves(leaf).z
glRotatef camrz, 0, 0, -1
glRotatef camry, 0, 1, 0
glRotatef camrx, -1, 0, 0
for p = 0 to 3
pntf(p, 0) = pnt(p,0) * leaves(leaf).size * leafscale!
pntf(p, 1) = pnt(p,1) * leaves(leaf).size * leafscale!
pntf(p, 2) = 0
next
glBegin GL_QUADS
glTexCoord2f 0, 0
glVertex3f pntf(0,0), pntf(0, 1), pntf(0,2)
glTexCoord2f 0, 1
glVertex3f pntf(1,0), pntf(1, 1), pntf(1,2)
glTexCoord2f 1, 1
glVertex3f pntf(2,0), pntf(2, 1), pntf(2,2)
glTexCoord2f 1, 0
glVertex3f pntf(3,0), pntf(3, 1), pntf(3,2)
glEnd
glPopMatrix
next
glEnable GL_LIGHTING
After the fixes to pnt() we disable the lighting so that our leaf planes are constantly lit. Next we set the color of the plane to bright white with a slight transparency so that the full color of the leaf texture shows up, but they are slightly see though.
glPushMatrix
glTranslatef leaves(leaf).x, leaves(leaf).y, leaves(leaf).z
glRotatef camrz, 0, 0, -1
glRotatef camry, 0, 1, 0
glRotatef camrx, -1, 0, 0
This code essentially is the same thing we did with our matricies in the first generator. We translate our drawing point to the location of the center of the tree plane. Then Rotate in the opposite direction and order of our camera rotations at the begining so that our plane is facing the camera.
for p = 0 to 3
pntf(p, 0) = pnt(p,0) * leaves(leaf).size * leafscale!
pntf(p, 1) = pnt(p,1) * leaves(leaf).size * leafscale!
pntf(p, 2) = 0
next
This block of code has changed considerably to use pntf() and to take out the matrix multiplication since it is no longer necessary. We then use these pntf() coordinates to draw our texture.
Some very necessary parts of code that make this effect with the leaves possible can be found toward the bottom of init ()
glBlendFunc GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA glAlphaFunc GL_GREATER, .5 glEnable GL_BLEND glEnable GL_ALPHA_TEST
glBlendFunc GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA - Sets up our blending function to blend any alpha color that has an alpha value with the color in the background.
glAlphaFunc GL_GREATER, .5 - This routine will cause any alpha value in our leaf texture that is > .5 to be drawn. This way the engine allows you to draw through parts of the texture that have an alpha value of 0. This effect is called BillBoarding and is very important. To see what happens when you don't do this comment out glEnable GL_ALPHA_TEST
The last 2 lines simply turn on Alpha Blending and the Alpha Test routine
Some small changes that I've made to the tree generator settings to get this tree are:
MAX_LEAVES = 10 'give the impression of a much fuller tree
sag! = 20 'make the branches sag more
leafscale! = 4 'make the leaf planes larger
Here is the current state of the code: openGLTree.bas This requires the above download for the rest of the base files. Note: Hit ENTER to generate a new tree just like in the original.
For this effect we'll create about 10 base trees and randomly place then around the environment. Trees are notorious for consuming resources so we'll use a system called Display Lists to 'precompile' our tree models into memory on the video card so that they draw tremendously faster.
For a more in-depth explanation of display lists check out: NeHe Lesson 12: Display Lists
Each display list will have a handle like the textures. Since we have to change our leaf plane coordinates and angles every fame to reflect the current camera properties we cannot precompile them, so we'll need to keep track of each leaf layout for each tree display list. You'll see this change in the dimensioning of leaves().
Also, There is now a drawLeaves () sub so that they can be drawn independently of the tree branches.
Additions in Bold
CONST MAX_TREES = 50, MAX_TREE_DL = 10
DIM SHARED branches(MAX_BRANCHES) AS branchType, leaves(MAX_TREE_DL, MAX_LEAVES) AS leafType, nBranches
DIM SHARED rotSpd!, scaleSpd!, leafscale!
DIM SHARED barkTex as gluint, leafTex as gluint
DIM SHARED barkTex2 as gluint
type treeDataType
x as single 'location of the tree
z as single
xAngle as single 'angle of the tree
yAngle as single
displayList as gluint 'the display list the tree uses
end type
DIM SHARED treeDL(MAX_TREE_DL - 1) as gluint, treeData(MAX_TREES - 1) as treeDataType
Now instead of calling generateTree, we call generateForest:
sub generateForest ()
for tree = 0 to MAX_TREE_DL - 1
generateTree tree
treeDL(tree) = glGenLists(1)
glNewList treeDL(tree), GL_COMPILE
drawTree
glEndList
next
Loop through each tree display list, generating the tree, creating the display list, then compiling it.
for tree = 0 to MAX_TREES - 1
maxdist! = 60
maxXAngle! = 20
angle! = rnd * 360
treeData(tree).x = (rnd * maxdist! - (maxdist! / 2)) * cos(rad * angle!)
treeData(tree).z = (rnd * maxdist! - (maxdist! / 2)) * sin(rad * angle!)
treeData(tree).displayList = int(rnd * MAX_TREE_DL)
treeData(tree).xAngle = rnd * maxXAngle! - (maxXAngle! / 2)
treeData(tree).yAngle = rnd * 360
next
end sub
Loop through each forest tree positioning it randomly in a circular area, assigning a random display list, and changing the angle of the tree.
The last change to the code is in drawview () where we would normally call drawTree to draw a single tree. Instead we loop through each forest tree, change the drawing location to the tree location, rotate the tree, then call the display list. Afterwards we draw the leaf planes specific to that display list and return our matrix back to normal:
for tree = 0 to MAX_TREES - 1
glPushMatrix
treeList = treeData(tree).displayList
glTranslatef treeData(tree).x, 0, treeData(tree).z
glRotatef treeData(tree).xAngle, 1, 0, 0
glRotatef treeData(tree).yAngle, 0, 1, 0
glCallList treeDL(treeList)
drawLeaves tree
glPopMatrix
next
You might have noticed that we'll need to add these new rotations to the counter rotations of the leaf planes to make them continue to face forward. Here are those changes to drawLeaves (tree):
sub drawLeaves (tree)
treeList = treeData(tree).displayList
[...]
glPushMatrix
glTranslatef leaves(treeList, leaf).x, leaves(treeList, leaf).y, leaves(treeList, leaf).z
glRotatef treeData(tree).yAngle, 0, -1, 0
glRotatef treeData(tree).xAngle, -1, 0, 0
glRotatef camrz, 0, 0, -1
glRotatef camry, 0, 1, 0
glRotatef camrx, -1, 0, 0
for p = 0 to 3
pntf(p, 0) = pnt(p,0) * leaves(treeList, leaf).size * leafscale!
pntf(p, 1) = pnt(p,1) * leaves(treeList, leaf).size * leafscale!
pntf(p, 2) = 0
next
Notice the changes to the leaves() array to take into consideration the display list dependant leaf set.
Some small changes were necessary to make this forest view. rotSpd! = .5 * fpsr and scaleSpd! = .5 * fpsr were added to main (). These speed up the rotation and scaling speed as well as make them frame rate dependant so the scene rotates at a constant speed at different frame rates. Also, zback = 100 instead of 50 to facilitate seeing all the trees.
Front View:

Top View:

That's it for the Tree Tutorial. I hope my explanations were clear and that you've found this technique helpful. If you have any questions, drop me a line at syn9_at_dev9.net. Please keep in mind that there are undoubtedly more ways to make trees and optimize these routines. So I implore you to seek out other methods and find the one that fits your code best.
Here are all final project files: treeTutorial1-6_Files.zip