Building A Cylinder

First a bit of theory (First I am going to bore you to death and then I am going to give you the code :-) )

You can generate any geometric primitive or surface that can be mathematically
described (cube, cone, pyramid, torus, cylinder, sphere ... ). If the shape you want
can not be parameterized (i.e. Doom 3 character) you’ll need something like 3DMax
or Milkshape (or even better, create your own 3D modeler :-) ).

Primitive Types

With TV3D / DirectX you can create several different primitive types

  • TV_TRIANGLESTRIP
  • TV_TRIANGLELIST
  • TV_LINELIST
  • TV_LINESTRIP
  • TV_POINTLIST
  • TRIANGLE_FAN - Not available in TV6.2 (You’ll have to wait for TV6.5)

First two are obviously good for solid geometry creation.

TV_TRIANGLELIST is good for creating faceted surfaces
since each triangle has 3 unique vertices and 3 unique normal vectors.

TV_TRIANGLESTRIP is good for creating smooth surfaces since DirectX treats
triangle list as one large polygon with multiple triangles.
It also uses fewer vertices.

There are benefits to using one over another, but I am not going to get into that.

Useful Links

Building The Geometry

Look at the cylinder below. If you cut it along the red line and open it up,
you get a very simple rectangle. That rectangle/polygon is very similar to
above definition of triangle strip. A third picture below applies the triangle
strip to the rectangle.

Just make sure vertices are added in a clockwise order (i.e. V1 → V2 → V3).
Now, the tricky part is to do all this while going in a circle. This is where cylinder
parametric equations come in handy.

Cylinder Parametric equations:

X=R*COS(Theta)
Y=Y
Z=R*SIN(Theta)

Or in our case:

X=R*COS(Theta)
Y=H
Z=R*SIN(Theta)

H - Height
R - Radius
Theta - Angular increment (from 0 - 360 deg or 0 - 2PI rad)

The Code (Visual Basic 6)

Public Sub Cylinder(Radius As Single, Height As Single, Sides As Single)
 
    Dim Theta As Single 'Current Angle
    Dim Inc As Single 'Angular increment
    Dim x As Single 'x coord
    Dim y As Single 'y coord
    Dim z As Single 'z coord
    Dim i As Integer
 
    Mesh.SetPrimitiveType TV_TRIANGLESTRIP
 
    'Cylinder Precision
    Inc = 2 * PI_ / Sides 'where each side has two triangles
       
    Theta = 0
   
    For i = 0 To Sides
       
        'Calculate Vertices
        x = Radius * Cos(Theta)
        y = Height
        z = Radius * Sin(Theta)
       
        'Vertex at the top of the cylinder
        Mesh.AddVertex x, 0, z, 0, 0, 0
        'Vertex at the bottom of the cylinder
        Mesh.AddVertex x, y, z, 0, 0, 0
 
        Theta = Theta + Inc
   
    Next
 
End Sub

Run the above code using the following parameters
Cylinder (40, 40, 30)
30 sides give us 30*2 triangles and 30*2 + 2 vertices

This is what you get:

You can not really see anything since we did not generate any normals.
Switch to wireframe to get a better view ( Scene.SetRenderMode TV_LINE )

Useful Links

Calculating Normals

Normals are unit vectors that define the angle between the surface and the light.
Look at the picture below for the cylinder normals.
First drawing shows the side view.
To get a nice light on your cylinders surface normals should be perpendicular
to that surface. Second drawing shows the top view.
If you want the edges to disappear you have to tell your light to bounce
directly off the edge(s).

Keep in mind one thing, vertex normals are not really on the vertex like the
image above shows.In the image below there are 3 identical vectors where
the blue one is the “real" normal vector.

Obviously, normal vectors (normals) for vertices on top of the cylinder
are identical to the ones at the bottom (you cut normals calculations by 50%).

The same deal for the top view. Red normal vectors are just representations
of real normal vectors (blue).

Back to coding.
As you can see in the above images normals have exactly the same
cords as the vertices (minus the y coord).

N.x = X = R*COS(Theta)
N.y = Y = 0
N.z = Z = R*SIN(Theta)

The Code (Visual Basic 6)

Public Sub Cylinder(Radius As Single, Height As Single, Sides As Single)
 
    Dim Theta As Single 'Current Angle
    Dim Inc As Single 'Angular increment
    Dim x As Single 'x coord
    Dim y As Single 'y coord
    Dim z As Single 'z coord
    Dim i As Integer
    Dim n As D3DVECTOR 'vertex normal
 
    Mesh.SetPrimitiveType TV_TRIANGLESTRIP
 
    'Cylinder Precision
    Inc = 2 * PI_ / Sides 'where each side has two triangles
       
    Theta = 0
   
    For i = 0 To Sides
       
        'Calculate Vertices
        x = Radius * Cos(Theta)
        y = Height
        z = Radius * Sin(Theta)
 
        'Make sure you normalize vertex cords to get proper normals
        TVVec3Normalize n, Vector(x, 0, z)
       
        'Vertex at the top of the cylinder
        Mesh.AddVertex x, 0, z, 0, 0, 0, n.x, n.y, n.z
        'Vertex at the bottom of the cylinder
        Mesh.AddVertex x, y, z, 0, 0, 0, n.x, n.y, n.z
 
        Theta = Theta + Inc
   
    Next
 
End Sub

Run the above code using the same parameters again
Cylinder (40, 40, 30)

This is what you get:

There is lot of different ways to calculate vertex normals. This one
seems to be good for the cylinder.
My explanations here are not particularly good nor in depth. You have
2 links below, where people did a much better job.

Useful Links

Applying Texture

Now that you have a “good looking” cylinder it is time to apply
a texture to it (to make it even better looking ;-) ).
To apply texture you’ll have to figure out the UV coordinates.

U and V coordinates are just a way to talk about which pixel should be used in a
texture map, independent of how big the texture map finally turns out to be.
If U and V are 0.25 and 0.5 for a given vertex in your model, and the texture
map is 512 by 512, the pixel at 128,256 will be applied to the vertex

In general, UV coordinates range between 0 and 1.
See the sample image below.

What UV (1, 1) really means is UV (100% of texture width, 100% of texture height)
or UV (0.5, 1) really means is UV (50% of texture width, 100% of texture height).

So, in the case of cylinder we open it up again to get a rectangle.

UV1=UV(0,0)
UV2=UV(0,1)
UV3=UV(1,1)
UV4=UV(1,0)

This is fine except we have applied UV cords to corner vertices only.
Next image shows triangle strip applied to textured rectangle.
There are 12 vertices (6 on top and 6 on bottom),
10 triangles and 5 sides (2 triangles per side).
6 vertices (top and bottom) are evenly spaced,
so the step between U cords is 1/Sides = 1/5.

...and once again the code:

The Code (Visual Basic 6)

Public Sub Cylinder(Radius As Single, Height As Single, Sides As Single)
 
    Dim Theta As Single 'Current Angle
    Dim Inc As Single 'Angular increment
    Dim x As Single 'x coord
    Dim y As Single 'y coord
    Dim z As Single 'z coord
    Dim i As Integer
    Dim n As D3DVECTOR 'vertex normal
    Dim tu As Single 'U coord
    Dim tv As Single 'V coord
    Dim UStep As Single 'step between U coords
 
    Mesh.SetPrimitiveType TV_TRIANGLESTRIP
 
    'Cylinder Precision
    Inc = 2 * PI_ / Sides 'where each side has two triangles
       
    Theta = 0 'Initial value
 
    UStep = 1 / Sides
 
    tu = 0 'Initial value
   
    For i = 0 To Sides
       
        'Calculate Vertices
        x = Radius * Cos(Theta)
        y = Height
        z = Radius * Sin(Theta)
       
        'Make sure you normalize vertex cords to get proper normals
        TVVec3Normalize n, Vector(x, 0, z)
       
        'Vertex at the top of the cylinder
        'tv value is always 1for top vertices
        Mesh.AddVertex x, 0, z, 0, tu, 1, n.x, n.y, n.z
 
        'Vertex at the bottom of the cylinder
        'tv value is always 0 for bottom vertices
        Mesh.AddVertex x, y, z, 0, tu, 0, n.x, n.y, n.z
 
        Theta = Theta + Inc
       
        tu = tu + UStep
   
    Next
 
End Sub

Run the above code (once again) using the same parameters Cylinder (40, 40, 30)

This is what you get:

It is good idea to use checkered texture to verify texture coordinates, since it would be very easy to spot deformed squares.

Useful Links

Tweaking Texture(UV) Coordinates

There is one problem with texture we applied in previous chapter.
Texture looks blurry and stretched.
The reason for is that we applied perfectly square texture to a rectangle.

Modify the previous code by replacing

tu = tu + UStep

with

tu = tu + UStep*2

and run it again.

The end result:

It looks a lot better then before.

By multiplying UStep by two you have doubled UV coordinates of
the rectangle (cylinder) and effectively forced tiling of your texture.

The same way, if you use

tu = tu + UStep*4

you will force 4 texture tiling in U direction.

New result:

Checkered texture is nice to verify texture coordinates,
but it can not show effect of tiling.
Use this texture to see UV manipulation effects.

With Original code:
One texture wrapped around

With

tu = tu + UStep*4

Four textures wrapped around

You are not limited to just one dimensional tiling
Do these modifications

tu = tu + UStep*10

and (replace tv value of 1 with 2)

Mesh.AddVertex x, 0, z, 0, tu, 2, n.x, n.y, n.z

This results in 2×10 texture tiling:

Adding Cylinder Caps

To make this cylinder complete it would be good idea to close it up or add
caps to top and bottom.

FIGURE No. 1

Building The Geometry

Ok, it is time to cut the cylinder along the red lines and open it up.
This time cut results in two circles and the rectangle.
Last picture below applies the triangle strip to the rectangle and circles.

FIGURE No. 2

Vertex numbers are added to the triangle strip just to show how vertices are
shared between two circles and the rectangle.
The mesh is divided into three regions Bottom Cap, Cylinder and Top Cap,
and these regions are going to be rendered in that order.
As usual we have to make sure vertices are added in clockwise order.
Parametric equations stay unchanged as well, since both, top and bottom
caps share cylindrical mesh vertices.

X=R*COS(Theta)
Y=H
Z=R*SIN(Theta)

At this point its a good idea to look at the Bottom Cap triangle strip
and figure out the algorithm.
The image shows Bottom cap in terms of angular increment theta.
There are two approaches

  1. Theta 0 to 360 deg
  2. Theta -180 to 0 to 180 deg


FIGURE No. 3

First approach algorithm would look something like this:

 V1 @   0 deg
 V2 @  45 deg
 V8 @ 315 deg
 V3 @  90 deg
 V7 @ 270 deg
 ...

Second approach algorithm:

 V1 @   0 deg
 V2 @  45 deg
 V8 @ -45 deg
 V3 @  90 deg
 V7 @ -90 deg
 ...

Both approaches are mathematically identical
i.e.

 For V8 @ 315 deg COS(315) = 0.707
 For V8 @ -45 deg COS(-45) = 0.707

Second one looks more manageable (just a personal preference), feel free to play with different approaches.

Time to code :-)

The Code (Visual Basic 6)

Public Sub Cylinder(Radius As Single, Height As Single, Sides As Single)
 
    Dim Theta As Single 'Current Angle
    Dim Inc As Single 'Angular increment
    Dim x As Single 'x coord
    Dim y As Single 'y coord
    Dim z As Single 'z coord
    Dim i As Integer
    Dim n As D3DVECTOR 'vertex normal
    Dim tu As Single 'U coord
    Dim tv As Single 'V coord
    Dim UStep As Single 'step between U coords
    Dim Sign As Integer 'a flag to indicate theta sign change
 
    Mesh.SetPrimitiveType TV_TRIANGLESTRIP
 
    'Cylinder Precision
    Inc = 2 * PI_ / Sides 'where each side has two triangles
       
    Theta = 0 'Initial value
 
    UStep = 1 / Sides
 
    tu = 0 'Initial value
    
    'Bottom Cap
 
    Sign = -1
    
    For i = 1 To Sides 'Draw The Bottom Cap Loop
    
        'Calculate vertices
        x = Radius * Cos(Sign * Theta)
        y = 0
        z = Radius * Sin(Sign * Theta)
        
        Mesh.AddVertex x, 0, z, 0, 0, 0, 0, 0, 0
    
        If Sign = 1 Then
            
            'Change the sign
            Sign = -1
        
        ElseIf Sign = -1 Then
            
            'Change the sign
            Sign = 1
            
            'Increment theta for the next loop
            Theta = Theta + Inc
 
        End If
        
    Next
    
    'The Cylinder
    
    'At this point it is important not to reset theta to zero
    'even though at the end of the following loop accumulated theta
    'will equal 3PI
    'See the "NOTE 1" below
    
    'Original code from "Building A Cylinder"
    'Including vertex normals and texture coordinates
    
    tu = 0 'Initial value
 
    For i = 0 To Sides 'Draw The Cylinder Loop
       
        'Calculate Vertices
        x = Radius * Cos(Theta)
        y = Height
        z = Radius * Sin(Theta)
       
        'Make sure you normalize vertex cords to get proper normals
        TVVec3Normalize n, Vector(x, 0, z)
       
        'Vertex at the top of the cylinder
        Mesh.AddVertex x, 0, z, 0, tu, 2, n.x, n.y, n.z
 
        'Vertex at the bottom of the cylinder
        Mesh.AddVertex x, y, z, 0, tu, 0, n.x, n.y, n.z
 
        Theta = Theta + Inc
       
        tu = tu + UStep * 10
 
    Next
 
End Sub
NOTE 1:
Look at FIGURE No.3. When drawing the Bottom Cap triangle strip, last vertex
drawn is vertex V5 (V1->V2->V8->V3->V7->V4->V6->V5). 
This last vertex V5 is drawn at the angle Theta = 180 deg.
To keep the triangle strip flow continuous it is necessary to start the "Cylindrical" 
triangle strip where the "Bottom Cap" triangle strip left off, at Theta = 180 deg.
Nothing changes mathematically. It is absolutely the same to go from 0 to 2PI 
(as in previous tutorial) or from PI to 3PI (as long as you complete the full 
circle of 360 deg or 2PI rad).
The same way, we will have to start drawing the "Top Cap" triangle strip where 
"Cylindrical" triangle strip stopped ... but more on that later.

Run the above code as Cylinder (40, 40, 30)

This is what we get:

FIGURE No. 4

Of course, bottom cap is black because we have no vertex normals defined.

Calculating Normals

Look at the cylinder side view image below. To get proper light for
Top and Bottom caps it is important that normals are perpendicular
to their surface.
It is obvious from the image below that all normals for the Top cap
are identical. The same goes for the Bottom cap.
Top Cap normals point in positive Y Axis direction and Bottom Cap
normals point in negative Y Axis direction.

FIGURE No. 5

There is no need for normal calculations and normalization here.
Top cap normals are all unit vectors:

Vector(0,1,0)

and Bottom cap normals are all unit vectors:

Vector(0,-1,0)

By replacing (in Draw The Bottom Cap Loop )

Mesh.AddVertex x, 0, z, 0, 0, 0, 0, 0, 0

with

Mesh.AddVertex x, 0, z, 0, 0, 0, 0, -1, 0

we get:

FIGURE No. 6

Back To Geometry

Ok, at this point we have Bottom Cap and the Cylindrical body.
Its time for the Top Cap ;-)

So far the code did something like this (in simplified pseudocode):

Program Start
Theta = 0
Calculate Bottom Cap Vertices (Start at Theta=0)
Theta = PI (180 deg)
Calculate Cylindrical Body Vertices (Start at Theta=PI)
Theta = 3*PI (540 deg)

Based on previous logic (Note 1) we would expect to continue like this

Calculate Top Cap Vertices (Start at Theta=3*PI)
Program End

Well ... starting Top Cap from 540 deg (3PI) just does not sound
very nice or clean (see FIGURE No. 7).

FIGURE No. 7

This looks quite ugly :-)

Since we are dealing with circles and angles here its time to use
trigonometry to our advantage ;-)

As far as a full circle is concerned trigonometry does not care for
how many times we went around that circle.
Simply put, we start removing full circles (360 deg) from our starting 540 deg

we ended up with:

540-360=180

this is where we stop since 180 deg is half a circle
(no more full circles to remove)

Same calculation in terms of radians where 2PI is a full circle:

3PI-2PI=PI

we stop again since PI is half circle.

Now, to see that in world of Trigonometry 540⇔180 are “equivalent”
we can do this:

COS(540)=-1
COS(180)=-1

Well, looks the same to me :-)

So, we have removed one full circle from our starting 540 deg Theta
and ended up with a new staring angle Theta=180 deg.
We do the same thing for all incremental Thetas in FIGURE No. 7

This is what we end up with:

FIGURE No. 8

I am going to make a leap of faith here and replace V8 theta of
495 with -225. Quick check:

COS(495)=-0.707
COS(-255)=0.707

Looks fine ;-)

Following the same logic we get this:

FIGURE No. 9

Another way to look at it:

FIGURE No. 10

FIGURE No. 9 is the one I like (just a matter of personal preference).
FIgures 7, 8, 10 are also valid, as long as you figure out the proper algorithm.

There is really only one important thing here:
The fact that we need to start with Theta=180 (PI).
How we go around the circle from that point is up to us.

Enough BS, its time to code again :-)

The Code (Visual Basic 6)

Public Sub Cylinder(Radius As Single, Height As Single, Sides As Single)
 
    Dim Theta As Single 'Current Angle
    Dim Inc As Single 'Angular increment
    Dim x As Single 'x coord
    Dim y As Single 'y coord
    Dim z As Single 'z coord
    Dim i As Integer
    Dim n As D3DVECTOR 'vertex normal
    Dim tu As Single 'U coord
    Dim tv As Single 'V coord
    Dim UStep As Single 'step between U coords
    Dim Sign As Integer 'a flag to indicate theta sign change
 
    Mesh.SetPrimitiveType TV_TRIANGLESTRIP
 
    'Cylinder Precision
    Inc = 2 * PI_ / Sides 'where each side has two triangles
       
    Theta = 0 'Initial value
 
    UStep = 1 / Sides
 
    tu = 0 'Initial value
    
    'Bottom Cap
 
    Sign = -1
    
    For i = 1 To Sides 'Draw The Bottom Cap Loop
    
        'Calculate vertices
        x = Radius * Cos(Sign * Theta)
        y = 0
        z = Radius * Sin(Sign * Theta)
        
        Mesh.AddVertex x, 0, z, 0, 0, 0, 0, -1, 0
    
        If Sign = 1 Then
            
            'Change the sign
            Sign = -1
        
        ElseIf Sign = -1 Then
            
            'Change the sign
            Sign = 1
            
            'Increment theta for the next loop
            Theta = Theta + Inc
 
        End If
        
    Next
    
    'The Cylinder
    
    'At this point it is important not to reset theta to zero
    'even though at the end of the following loop accumulated theta
    'will equal 3PI
    'See the "NOTE 1" below
    
    'Original code from "Building A Cylinder"
    'Including vertex normals and texture coordinates
 
    tu = 0 'Initial value
 
    For i = 0 To Sides 'Draw The Cylinder Loop
       
        'Calculate Vertices
        x = Radius * Cos(Theta)
        y = Height
        z = Radius * Sin(Theta)
       
        'Make sure you normalize vertex cords to get proper normals
        TVVec3Normalize n, Vector(x, 0, z)
       
        'Vertex at the top of the cylinder
        Mesh.AddVertex x, 0, z, 0, tu, 2, n.x, n.y, n.z
 
        'Vertex at the bottom of the cylinder
        Mesh.AddVertex x, y, z, 0, tu, 0, n.x, n.y, n.z
 
        Theta = Theta + Inc
       
        tu = tu + UStep * 10
 
    Next
    
    'The Top Cap
    
    Theta = PI_
    Sign = 1
    
    For i = 1 To Sides 'Draw The Top Cap Loop
    
        'Calculate vertices
        x = Radius * Cos(Sign * Theta)
        y = Height
        z = Radius * Sin(Sign * Theta)
        
        'All Vertex Normals equal to +1 in Y axis direction
 
 
        Mesh.AddVertex x, y, z, 0, 0, 0, 0, 1, 0
    
        If Sign = 1 Then
            
            'Change the sign
            Sign = -1
            
            'Increment theta for the next loop
            Theta = Theta + Inc
        
        ElseIf Sign = -1 Then
            
            'Change the sign
            Sign = 1
 
        End If
        
    Next
 
End Sub

Run the above code as Cylinder (40, 40, 30)

This is what we get:

With checkered Texture
FIGURE No. 11

With TV3D Texture
FIGURE No. 12

No texture
FIGURE No. 13

One thing we are missing here are cap textures.

Applying Cap Texture

Bottom and Top Caps are basically a 2D circle.
Parametric equation of circle in a Cartesian coordinate system is (Circle):

X=R*COS (Theta)
Y=R*SIN (Theta)

R - Circle radius

for a circle with center in origin x=0 and y=0.

Similarly, parametric equation of circle in a “texture world”
or UV coordinate system is :

U=R*COS (Theta)
V=R*SIN (Theta)

R - Circle radius

for a circle with center in origin U=0 and V=0.

Knowing that UV coordinates range between 0 and 1
we need a “unit circle” (Circle with diameter D=1).

Next image shows the inscribed Unit Circle (D=1 and R=D/2=0.5).

FIGURE No. 14

Now, if we want our caps to show the texture exactly as it appears
in FIGURE No. 14 we need to adjust the above equations.
The problem is that our circle center is not in the origin (0,0).
Circle center is offset by R in U and V direction (see NOTE 2).

The problem is solved by rewriting the equations as:

U=R*COS(Theta)+R
V=R*SIN(Theta)+R

or

U=0.5*COS(Theta)+0.5
V=0.5*SIN(Theta)+0.5

or in code

tu = 0.5 * Cos(Sign * Theta) + 0.5
tv = 0.5 * Sin(Sign * Theta) + 0.5
NOTE 2:
Even without adding these offsets, the texture would be applied nicely.
Its just that we would not get the desired effect (FIGURE No.14).

The Code (Visual Basic 6)

Public Sub Cylinder(Radius As Single, Height As Single, Sides As Single)
 
    Dim Theta As Single 'Current Angle
    Dim Inc As Single 'Angular increment
    Dim x As Single 'x coord
    Dim y As Single 'y coord
    Dim z As Single 'z coord
    Dim i As Integer
    Dim n As D3DVECTOR 'vertex normal
    Dim tu As Single 'U coord
    Dim tv As Single 'V coord
    Dim UStep As Single 'step between U coords
    Dim Sign As Integer 'a flag to indicate theta sign change
 
    Mesh.SetPrimitiveType TV_TRIANGLESTRIP
 
    'Cylinder Precision
    Inc = 2 * PI_ / Sides 'where each side has two triangles
       
    Theta = 0 'Initial value
 
    UStep = 1 / Sides
 
    tu = 0 'Initial value
    
    'Bottom Cap
 
    Sign = -1
    
    For i = 1 To Sides 'Draw The Bottom Cap Loop
    
        'Calculate vertices
        x = Radius * Cos(Sign * Theta)
        y = 0
        z = Radius * Sin(Sign * Theta)
        
        tu = 0.5 * Cos(Sign * Theta) + 0.5
        tv = 0.5 * Sin(Sign * Theta) + 0.5
        
        Mesh.AddVertex x, 0, z, 0, tu, tv, 0, -1, 0
    
        If Sign = 1 Then
            
            'Change the sign
            Sign = -1
        
        ElseIf Sign = -1 Then
            
            'Change the sign
            Sign = 1
            
            'Increment theta for the next loop
            Theta = Theta + Inc
 
        End If
        
    Next
    
    'The Cylinder
    
    'At this point it is important not to reset theta to zero
    'even though at the end of the following loop accumulated theta
    'will equal 3PI
    'See the "NOTE 1" bellow
    
    'Original code from "Building A Cylinder"
    'Including vertex normals and texture coordinates
 
    tu = 0 'Initial value
 
 
    For i = 0 To Sides 'Draw The Cylinder Loop
       
        'Calculate Vertices
        x = Radius * Cos(Theta)
        y = Height
        z = Radius * Sin(Theta)
       
        'Make sure you normalize vertex cords to get proper normals
        TVVec3Normalize n, Vector(x, 0, z)
       
        'Vertex at the top of the cylinder
        Mesh.AddVertex x, 0, z, 0, tu, 2, n.x, n.y, n.z
 
        'Vertex at the bottom of the cylinder
        Mesh.AddVertex x, y, z, 0, tu, 0, n.x, n.y, n.z
 
        Theta = Theta + Inc
       
        tu = tu + UStep * 10
 
    Next
    
    'The Top Cap
    
    Theta = PI_
    Sign = 1
    
    For i = 1 To Sides 'Draw The Top Cap Loop
    
        'Calculate vertices
        x = Radius * Cos(Sign * Theta)
        y = Height
        z = Radius * Sin(Sign * Theta)
        
        tu = 0.5 * Cos(Sign * Theta) + 0.5
        tv = 0.5 * Sin(Sign * Theta) + 0.5
        
        'All Vertex Normals equal to +1 in Y axis direction
        Mesh.AddVertex x, y, z, 0, tu, tv, 0, 1, 0
    
        If Sign = 1 Then
            
            'Change the sign
            Sign = -1
            
            'Increment theta for the next loop
            Theta = Theta + Inc
        
        ElseIf Sign = -1 Then
            
            'Change the sign
            Sign = 1
 
        End If
        
    Next
 
End Sub

As usual, run the code as Cylinder(40,40,30).

Bottom Cap result:

FIGURE No. 15

Bottom cap looks fine.

Top Cap result:

FIGURE No. 16

Well, Top cap is “fine” but mirrored.
There is a reason for a mirrored texture (believe it or not :-) )

The main reason is the identical code we use for both, the bottom cap and the top cap.

tu = 0.5 * Cos(Sign * Theta) + 0.5
tv = 0.5 * Sin(Sign * Theta) + 0.5

Look at the three steps in FIGURE No. 17 (Bottom Cap View) .
Second step shows texture applied to the bottom cap, and that looks fine.
Now, if you use the same code for the top cap, you basically take the bottom
cap texture and “translate” it to the top cap along the cylinder height(step 3).

FIGURE No. 17


To make this even more clear, look at the same three steps from a different
perspective (Top Cap View FIGURE No. 18).

FIGURE No. 18

There is your mirrored texture view, right there at step 3.

Ok, hopefully this last explanation made sense :-).

Fixing this mirrored/flipped texture is quite easy.
All you have to do is make your U OR V coordinate negative.
Make sure you do not change both coordinates to negative values.
Changing U=-U and V=-V will still give you mirrored texture.

New code for Top Cap:

tu = -(0.5 * Cos(Sign * Theta) + 0.5)
tv = 0.5 * Sin(Sign * Theta) + 0.5

Make this change and run the code.

Everything should work fine now, except I do not like that
single large texture applied to top and bottom cap :-(.

Its time to tile the texture :-).
Tiling is very easy:
All you have to do is increase the size of the Unit Circle.
You increase the circle size by increasing its radius.
Lets say we want 3×3 tiling:

For Bottom Cap:

tu = 0.5 * 3 * Cos(Sign * Theta) + 0.5
tv = 0.5 * 3 * Sin(Sign * Theta) + 0.5

For Top Cap:

tu = -(0.5 * 3 * Cos(Sign * Theta) + 0.5)
tv = 0.5 * 3 * Sin(Sign * Theta) + 0.5

Insert this code to get:

FIGURE No. 19

Or for better view:

Light-XviD-Small.avi (500K)
Rot-XviD-Small.avi (450K)

The End

Keep in mind that this is far from “good” and “optimized” code.
This code will work if you have your application setup correctly
Textures, materials, lights ... (see Getting Started Tutorials)

If you have questions, suggestions, comments ... etc, you can PM me (Vuli)
on TrueVision3D forum TrueVision3D Forum

Have fun :-)

 
tutorialsarticlesandexamples/building_a_cylinder.txt · Last modified: 2013/11/22 13:32