How to code a 3D-Road-Algorithm for Beginners

Started by Midimaster, April 26, 2023, 09:29:07

Previous topic - Next topic

Midimaster

In this tutorial I will explain what you need to draw procedural roads into a 3D-world. This means we have no file of the 3D-oject, but we create it with only code. At the end the road will be like a regular 3D-object: you can move it, scale it, etc...

This GIF with 4 screenshots the result of the tutorial:

Roadmovie.gif




The tutorial is for Blitz3D and BlitzMax/MiniB3D. You always will find runable code examples for both in the lessons. I kept the code particularly simple. It could be done more effficient, but I did not want to divert from the kernel 3D methods. So please do not critisize the non 3D related code lines.

If you have questions I would ask for not using this topic until the 5 lessons are written. Please open a new topic in forum BlitzMax or forum Blitz3D.

The lessons I to III are still in 2D explain the road algorithm and its problematic. The lessons IV to V will explain how to create Meshes in general

In the lessons VI to ... we glue together both strategies to a runable code example.

Lesson I: All You Need Is Sinus And Cosinus

Drawing forms in any direction in a plane needs the use of Sinus and Cosinus and an action point. Each new point needs to be transfered from a action point into its direction.

Code (vb) Select
Function Tranfer(x%, z%, length% ,arc%)
    TransX = x + Sin(arc) * length
    TransZ = z - Cos(arc) * length
End Function

To be prepared for the 3D world we already use the name TransZ instead of TransY for the coordinate. Y is for the hight of objects.


Our first examples shows a rotating L-form:

Cls
    Tranfer ActionX, ActionZ, 200, 45
    CornerX = TransX
    CornerZ = TransZ
    Line ActionX, ActionZ, CornerX, CornerZ

    Tranfer CornerX, CornerZ, 100, 45+90
    Line CornerX, CornerZ, TransX, TransZ
Flip
....   
Function Tranfer(x%, z%, length% ,arc%)
    TransX = x + Sin(arc) * length
    TransZ = z - Cos(arc) * length
End Function 

The first call of the function Transfer() transfers the given Corner into the direction 45°. Then we transfer the second point in 90° orientation to the first line with Corner as action point.

Lesson1A.gif
Here is the complete code for Blitz3D:
Graphics 800,600

Global ActionX% = 300
Global ActionZ% = 200
Global Arc%     = 45


Global TransX%, TransZ%

; draw a 200pix 45° line from Actionpoint

Repeat
    Cls
    Arc=Arc+1
    Tranfer ActionX, ActionZ, 200, Arc
    CornerX = TransX
    CornerZ = TransZ
    Line ActionX, ActionZ, CornerX, CornerZ

    Tranfer CornerX, CornerZ, 100, Arc+90
    Line CornerX, CornerZ, TransX, TransZ
    Flip 1
Until KeyHit(1)


End

Function Tranfer(x%, z%, length% ,arc%)
    TransX = x + Sin(arc) * length
    TransZ = z - Cos(arc) * length
End Function

and here for BlitzMax:
SuperStrict
Graphics 800,600

Global ActionX% = 300
Global ActionZ% = 200
Global Arc%     = 45


Global TransX%, TransZ%

' draw a 200pix 45° line from Actionpoint

Repeat
    Cls
    Arc=Arc+1
    Tranfer ActionX, ActionZ, 200, Arc
    Local CornerX% = TransX
    Local CornerZ% = TransZ
    DrawLine ActionX, ActionZ, CornerX, CornerZ

    Tranfer CornerX, CornerZ, 100, Arc+90
    DrawLine CornerX, CornerZ, TransX, TransZ
    Flip 1
Until AppTerminate()
End

Function Tranfer(x%, z%, length% ,arc%)
    TransX = x + Sin(arc) * length
    TransZ = z - Cos(arc) * length
End Function



3D Likes Curves

Because of permanent use of sin() and cos() it is indeed more easy to draw curves than straight roads in the 3D-world. In the next example we have a top-view to a plane. You can draw a "curved road" with the formula you already know. We use two arrays to store the waypoints and Line (DrawLine) commands to connect them. The red point is the action point (or you can say center of the circle).

Lesson1B.gif


Hit the key <S> to step ahead:

Graphics 800,600

Global RADIUS#  = 100
Global ActionX# = 200
Global ActionZ# = 400
Global Arc%     = 180
Dim X#(200)
Dim Z#(200)
Global Count%    = 0

Repeat
    Cls
    If KeyHit(31) ; <S>

        X(Count) = ActionX + Sin(arc) * RADIUS
        Z(Count) = ActionZ - Cos(arc) * RADIUS
        Arc = Arc-10
        Count=Count +1
    EndIf    
    DrawAll
    Flip 1
Until KeyHit(1)
End


Function DrawAll()
    Color 255,255,255
    Text 100,100," Arc = " + Arc +"°"
    Text 100,120,"Count= " + count
    Text 100, 20,"Hit <S> to step ahead"

    Color 255,0,0
    Oval ActionX,ActionZ,5,5
    Color 255,255,0
    For i%=0 To 199
        If X(i+1)=0 Return
        Line X(i),Z(i),X(i+1),Z(i+1)
    Next 
End Function

You will recognize that the first hit of <S> will reuslt in no street. This is important. With only one first waypoint you cannot draw a street segment. So if you plan to draw 10 street segments you always need 11 waypoints!


Here is the same in BlitzMax:
SuperStrict
Graphics 800,600

Global RADIUS:Double  = 100
Global ActionX:Double = 200
Global ActionZ:Double = 400
Global Arc:Int     = 180
Global X:Double[200]
Global Z:Double[200]
Global Count:Int    = 0

Repeat
    Cls
    If KeyHit(KEY_S) ' <S>
        X[Count] = ActionX + Sin(arc) * RADIUS
        Z[Count] = ActionZ - Cos(arc) * RADIUS
        Arc = Arc-10
        Count=Count +1
    EndIf    
    DrawAll
    Flip 1
Until KeyHit(1)
End


Function DrawAll()
    SetColor 255,255,255
    DrawText " Arc = " + Arc +"°"   ,100,100
    DrawText "Count= " + count      ,100,120
    DrawText "Hit <S> to step ahead",100, 20

    SetColor 255,0,0
    DrawOval ActionX,ActionZ,5,5
    SetColor 255,255,0
    For Local i:Int = 0 To 199
        If X[i+1] = 0 Return
        DrawLine X[i],Z[i],X[i+1],Z[i+1]
    Next 
End Function











...back from North Pole.

Midimaster

Lesson II: Straight Road and Right Curve

To move the road straight ahead we only have to move the action point. With this change you will build a straight road after step 10:

Code (vb) Select
Repeat
Cls
If KeyHit(31) ; <S>

X(Count) = ActionX + Sin(arc) * RADIUS
Z(Count) = ActionZ - Cos(arc) * RADIUS

If Count<9
Arc = Arc-10
Else
ActionZ=ActionZ-10
EndIf
Count=Count +1
EndIf
DrawAll
Flip 1
Until KeyHit(1)
End


Lesson2A.gif

But you can also see, that the action point can stay left outside the road. So to say: A straight road is a left turn without changing the arc.


Here is the complete code for Blitz3D
Graphics 800,600


Global RADIUS#  = 100
Global ActionX# = 200
Global ActionZ# = 400
Global Arc%     = 180
Dim X#(200)
Dim Z#(200)
Global Count%    = 0



Repeat
Cls
If KeyHit(31) ; <S>

X(Count) = ActionX + Sin(arc) * RADIUS
Z(Count) = ActionZ - Cos(arc) * RADIUS

If Count<9
Arc = Arc-10
Else
ActionZ=ActionZ-10
EndIf
Count=Count +1
EndIf
DrawAll
Flip 1
Until KeyHit(1)
End


Function DrawAll()
Color 255,255,255
Text 100,100," Arc = " + Arc +"°"
Text 100,120,"Count= " + count
Text 100, 20,"Hit <S> more than 10 times to step ahead"

Color 255,0,0
Oval ActionX,ActionZ,5,5
Color 255,255,0
For i%=0 To 199
If X(i+1)=0 Return
Line X(i),Z(i),X(i+1),Z(i+1)
Next 
End Function


And here is the complete code for BlitzMax
SuperStrict
Graphics 800,600

Global RADIUS:Double  = 100
Global ActionX:Double = 200
Global ActionZ:Double = 400
Global Arc:Int     = 180
Global X:Double[200]
Global Z:Double[200]
Global Count:Int    = 0

Repeat
Cls
If KeyHit(KEY_S) ' <S>
X[Count] = ActionX + Sin(arc) * RADIUS
Z[Count] = ActionZ - Cos(arc) * RADIUS
If Count<9
Arc = Arc-10
Else
ActionZ=ActionZ-10
EndIf
Count=Count +1
EndIf
DrawAll
Flip 1
Until AppTerminate()
End


Function DrawAll()
SetColor 255,255,255
DrawText " Arc = " + Arc +"°"   ,100,100
DrawText "Count= " + count      ,100,120
DrawText "Hit <S> to step ahead",100, 20

SetColor 255,0,0
DrawOval ActionX,ActionZ,5,5
SetColor 255,255,0
For Local i:Int = 0 To 199
If X[i+1] = 0 Return
DrawLine X[i],Z[i],X[i+1],Z[i+1]
Next 
End Function



A Right Turn Is Not That Easy

For a right turn of the road we have to move the action point. The action point is always the center of the radius of the road. So if we want to turn right, the action point needs to be right of the road. But a first try to simply move the point to the right...

ActionX= ActionX+ 2*RADIUS

... ends in this catastophe:

Lesson3A.gif

To understand what happened lets have a look on how the action point sees our street. In the first steps the action point is left of us. He sees us in direction EAST (means 90°). After we placed it on the right, he sees us in direction WEST (270°).

Lesson3.png

So the action point needs to calculate +180° to our last direction to continue. The road will run from WEST 270° to NORTH 360° during the next steps. We forgot this change a so we got wrong results.

This is necessary:
ActionX= ActionX+ 2*RADIUS
Arc = Arc +180


Lesson3B.gif


This is the whole code in Blitz3D
Graphics 800,600

Global RADIUS#  = 100
Global ActionX# = 200
Global ActionZ# = 400
Global Arc%     = 180
Dim X#(200)
Dim Z#(200)
Global Count%    = 0

Repeat
Cls
If KeyHit(31) ; <S>

If Count<9
Arc = Arc-10
ElseIf Count<20
ActionZ = ActionZ-10
ElseIf Count=20
ActionX= ActionX+ 2*RADIUS
Arc = Arc +180
Else
Arc=arc+10
EndIf
X(Count) = ActionX + Sin(arc) * RADIUS
Z(Count) = ActionZ - Cos(arc) * RADIUS
Count=Count +1

EndIf
DrawAll
Flip 1
Until KeyHit(1)
End

Function DrawAll()
Color 255,255,255
Text 100,100," Arc = " + Arc +"°"
Text 100,120,"Count= " + count
Text 100, 20,"Hit <S> more than 20 times to step ahead"

Color 255,0,0
Oval ActionX,ActionZ,5,5
Color 255,255,0
For i%=0 To 199
If X(i+1)=0 Return
Line X(i),Z(i),X(i+1),Z(i+1)
Next 
End Function





This is the whole code in BlitzMax
SuperStrict
Graphics 800,600

Global RADIUS:Double  = 100
Global ActionX:Double = 200
Global ActionZ:Double = 400
Global Arc:Int     = 180
Global X:Double[200]
Global Z:Double[200]
Global Count:Int    = 0

Repeat
Cls
If KeyHit(KEY_S) ' <S>
If Count<9
Arc = Arc-10
ElseIf Count<20
ActionZ = ActionZ-10
ElseIf Count=20
ActionX= ActionX+ 2*RADIUS
Arc = Arc +180
Else
Arc=arc+10
EndIf
X[Count] = ActionX + Sin(arc) * RADIUS
Z[Count] = ActionZ - Cos(arc) * RADIUS
Count=Count +1
EndIf
DrawAll
Flip 1
Until AppTerminate()
End


Function DrawAll()
SetColor 255,255,255
DrawText " Arc = " + Arc +"°"   ,100,100
DrawText "Count= " + count      ,100,120
DrawText"Hit <S> more than 20 times to step ahead",100, 20

SetColor 255,0,0
DrawOval ActionX,ActionZ,5,5
SetColor 255,255,0
For Local i:Int = 0 To 199
If X[i+1] = 0 Return
DrawLine X[i],Z[i],X[i+1],Z[i+1]
Next 
End Function


...back from North Pole.

STEVIE G

Nice work.

I think that by defining road sections as arcs/lengths it might be tricky to build a seamless racetrack, specifically where the final section meets the first.

I prefer the 4pt Bezier method myself.

Santiago


Midimaster

#4
Lesson III: Both Sides Of A Road

Today we work with 2 lines: Left side of the road and right side of the road. Therefore we now use two Array-Pairs

Code (vb) Select
;for the left road side
xB#[]
zB#[]
;for the right road side
xC#[]
zC#[]

I'm using  the letters B and C now for the road, because later in the tutorial we will expand this to 4 waypoints A,B,C,D and the letters B and C will represent the top road surface.

The Left side of the road (B) has the distance RADIUS to the action point (also on the left side). And the right side of the road has the distance RADIUS+ STREET_WIDTH to the Action Point.

Code (vb) Select
L = RADIUS
R = RADIUS+STREET_WIDTH

When we draw a right curve the action point is on the right side on the road. This means, that now the RIGHT ROAD SIDE is closer to the action point and the LEFT ROAD SIDE has the distance  RADIUS+ STREET_WIDTH

Code (vb) Select
L = RADIUS+STREET_WIDTH
R = RADIUS

So we better do this: We differ between three states Dir%

0= LEFT CURVE
1= STRAIGHT ROAD
2= RIGHT CURVE

Then we can code:

Code (vb) Select
If dir=0
    L = RADIUS
    R = RADIUS+STREET_WIDTH
ElseIf dir=1
    L = RADIUS
    R = RADIUS+STREET_WIDTH
Else
    L = RADIUS+STREET_WIDTH
    R = RADIUS
EndIf

;yellow Line (LEFT ROAD SIDE)
xB(Count) = ActionX + Sin(arc) * L
zB(Count) = ActionZ - Cos(arc) * L

;green line  (RIGHT ROAD SIDE)
xC(Count) = ActionX + Sin(arc) * R
zC(Count) = ActionZ - Cos(arc) * R

As a last step we need to consider, that the jump distance for the action point, when jumping from left side to right side is now bigger
Code (vb) Select
ActionX= ActionX+ 2*RADIUS+STREET_WIDTH

Lesson4A.gif



Here is the complete code for Blitz3D:
Code (vb) Select
Graphics 800,600

Global RADIUS#       = 100
Global STREET_WIDTH# = 20

Global ActionX# = 200
Global ActionZ# = 400
Global Arc%     = 190

Dim xB#(200)
Dim zB#(200)

Dim xC#(200)
Dim zC#(200)

Global Count%    = 0
Global L#, R#, Dir%

Repeat
    Cls
    If KeyHit(31) ; <S>
        If Count<10
            Arc = Arc-10
            Dir=0

        ElseIf Count<20
            ActionZ=ActionZ-10
            Dir=1
        ElseIf Count=20
            ActionX= ActionX+ 2*RADIUS+STREET_WIDTH
            Arc = Arc +180
            Dir=2
        Else
            Arc=arc+10
        EndIf

        If dir=0
            L = RADIUS
            R = RADIUS+STREET_WIDTH
        ElseIf dir=1
            L = RADIUS
            R = RADIUS+STREET_WIDTH
        Else
            L = RADIUS+STREET_WIDTH
            R = RADIUS
        EndIf

        ;yellow Line (LEFT ROAD SIDE)
        xB(Count) = ActionX + Sin(arc) * L
        zB(Count) = ActionZ - Cos(arc) * L

        ;blue line  (RIGHT ROAD SIDE)
        xC(Count) = ActionX + Sin(arc) * R
        zC(Count) = ActionZ - Cos(arc) * R   


        Count=Count +1
    EndIf    
    DrawAll
    Flip 1
Until KeyHit(1)



Function DrawAll()
    Color 255,255,255
    Text 100,100," Arc = " + Arc +"°"
    Text 100,120,"Count= " + count
    Text 100, 20,"Hit <S> to step ahead"

    Color 255,0,0
    Oval ActionX,ActionZ,9,9
    Color 255,255,0
    For i%=0 To 199
        If xB(i+1)=0 Exit
        Line xB(i), zB(i), xB(i+1), zB(i+1)
    Next 

    Color 0,255,255
    For i%=0 To 199
        If xC(i+1)=0 Exit
        Line xC(i), zC(i), xC(i+1), zC(i+1)
    Next 
End Function


Here is the complete code for BlitzMax:
Code (vb) Select
SuperStrict
Graphics 800,600

Global RADIUS:Double  = 100
Global STREET_WIDTH:Double = 20
Global ActionX:Double = 200
Global ActionZ:Double = 400
Global Arc:Int     = 180

Global xB:Double[200]
Global zB:Double[200]
Global xC:Double[200]
Global zC:Double[200]

Global Count:Int    = 0
Global L:Double, R:Double, Dir:Int

Repeat
    Cls
    If KeyHit(KEY_S) ' <S>
        If Count<9
            dir=0
            Arc = Arc-10
        ElseIf Count<20
            dir=1
            ActionZ = ActionZ-10   
        ElseIf Count=20
            dir=2
            ActionX= ActionX+ 2*RADIUS+STREET_WIDTH
            Arc = Arc +180
        Else
            Arc=arc+10
        EndIf
        If dir=0
            L = RADIUS
            R = RADIUS+STREET_WIDTH
        ElseIf dir=1
            L = RADIUS
            R = RADIUS+STREET_WIDTH
        Else
            L = RADIUS+STREET_WIDTH
            R = RADIUS
        EndIf
        'yellow Line (Left ROAD SIDE)
        xB[Count] = ActionX + Sin(arc) * L
        zB[Count] = ActionZ - Cos(arc) * L

        'blue line  (Right ROAD SIDE)
        xC[Count] = ActionX + Sin(arc) * R
        zC[Count] = ActionZ - Cos(arc) * R   
        Count=Count +1
    EndIf    
    DrawAll
    Flip 1
Until AppTerminate()
End


Function DrawAll()
    SetColor 255,255,255
    DrawText " Arc = " + Arc +"°"   ,100,100
    DrawText "Count= " + count      ,100,120
    DrawText"Hit <S> more than 20 times to step ahead",100, 20

    SetColor 255,0,0
    DrawOval ActionX,ActionZ,5,5

    SetColor 255,255,0
    For Local i:Int = 0 To 199
        If xB[i+1] = 0 Exit
        DrawLine xB[i], zB[i], xB[i+1], zB[i+1]
    Next 

    SetColor 0,255,255
    For Local i:Int = 0 To 199
        If xB[i+1] = 0 Return
        DrawLine xC[i], zC[i], xC[i+1], zC[i+1]
    Next 
   
End Function



Diagonal Straight Road

In lesson II I cheated, when telling you, that a straight road is made by simply adding a number. Of course also for the straight road parts the moving action points need to be calculated with Sinus and Cosinus.

Code (vb) Select
ActionX = ActionX - Cos(Arc)*STREET_WIDTH
ActionZ = ActionZ - Sin(Arc)*STREET_WIDTH


And we don't forget to reset the action point and the 180° at the end of each RIGHT CURVE SEGMENT after we calculated the waypoints.

Code (vb) Select
;yellow Line (LEFT ROAD SIDE)
xB(Count) = ActionX + Sin(arc) * L
zB(Count) = ActionZ - Cos(arc) * L

;blue line  (RIGHT ROAD SIDE)
xC(Count) = ActionX + Sin(arc) * R
zC(Count) = ActionZ - Cos(arc) * R
; restore action point   
If Dir=2
    Arc = Arc -180
    ActionX= ActionX - Sin(Arc) * (2*RADIUS+STREET_WIDTH)
    ActionZ= ActionZ + Cos(Arc) * (2*RADIUS+STREET_WIDTH)
EndIf

With this we can change direction now on each segment. Fell free to play around with the keys <D> for "change direction" and <S> for "step ahead"


Lesson4B.gif



Here is the complete code for Blitz3D:
Code (vb) Select
Graphics 800,600

Global RADIUS#       = 100
Global STREET_WIDTH# = 20

Global ActionX# = 200
Global ActionZ# = 400
Global Arc%     = 190

Dim xB#(200)
Dim zB#(200)

Dim xC#(200)
Dim zC#(200)

Global Count%    = 0
Global L#, R#, Dir%
Global DirWord$="LEFT"


Repeat
    Cls
    If KeyHit(32) ; <D>
        ;change direction Left Right
        dir= (dir +1) Mod 3
        Select Dir
            Case 0
                DirWord ="LEFT"
            Case 1   
                DirWord ="STRAIGHT"
            Case 2
                DirWord ="RIGHT"
        End Select
    ElseIf KeyHit(31) ; <S>
        If dir=0
            Arc = Arc-10
            L = RADIUS
            R = RADIUS+STREET_WIDTH
        ElseIf dir=1
            L = RADIUS
            R = RADIUS+STREET_WIDTH
            ActionX = ActionX - Cos(Arc)*STREET_WIDTH
            ActionZ = ActionZ - Sin(Arc)*STREET_WIDTH
        Else
            ActionX= ActionX + Sin(Arc) * (2*RADIUS+STREET_WIDTH)
            ActionZ= ActionZ - Cos(Arc) * (2*RADIUS+STREET_WIDTH)
            Arc = Arc +180
            Arc = Arc+10
            L = RADIUS+STREET_WIDTH
            R = RADIUS
        EndIf


        ;yellow Line (LEFT ROAD SIDE)
        xB(Count) = ActionX + Sin(arc) * L
        zB(Count) = ActionZ - Cos(arc) * L

        ;blue line  (RIGHT ROAD SIDE)
        xC(Count) = ActionX + Sin(arc) * R
        zC(Count) = ActionZ - Cos(arc) * R   
        If Dir=2
            Arc = Arc -180
            ActionX= ActionX - Sin(Arc) * (2*RADIUS+STREET_WIDTH)
            ActionZ= ActionZ + Cos(Arc) * (2*RADIUS+STREET_WIDTH)
        EndIf


        Count=Count +1
    EndIf    
    DrawAll
    Flip 1
Until KeyHit(1)



Function DrawAll()
    Color 255,255,255
    Text 100,100," Arc = " + Arc +"°"
    Text 100,120,"Count= " + count
    Text 100,140,"  Dir= " + DirWord
    Text 100, 20,"Hit <S> to step ahead"
    Text 100, 40,"Hit <D> to change direction LEFT STRAIGHT RIGHT"

    Color 255,0,0
    Oval ActionX,ActionZ,9,9
    Color 255,255,0
    For i%=0 To 199
        If xB(i+1)=0 Exit
        Line xB(i), zB(i), xB(i+1), zB(i+1)
    Next 

    Color 0,255,255
    For i%=0 To 199
        If xC(i+1)=0 Exit
        Line xC(i), zC(i), xC(i+1), zC(i+1)
    Next 
End Function


Here is the complete code for BlitzMax:
Code (vb) Select
SuperStrict
Graphics 800,600

Global RADIUS:Double  = 100
Global STREET_WIDTH:Double = 20
Global ActionX:Double = 200
Global ActionZ:Double = 400
Global Arc:Int     = 180

Global xB:Double[200]
Global zB:Double[200]
Global xC:Double[200]
Global zC:Double[200]

Global Count:Int    = 0
Global L:Double, R:Double, Dir:Int
Global DirWord$="LEFT"

Repeat
    Cls
    If KeyHit(KEY_D) ' <D>
        'change direction Left Right
        dir= (dir +1) Mod 3
        Select Dir
            Case 0
                DirWord ="LEFT"
            Case 1   
                DirWord ="STRAIGHT"
            Case 2
                DirWord ="RIGHT"
        End Select    
    ElseIf KeyHit(KEY_S) ' <S>   
        If dir=0
            L = RADIUS
            R = RADIUS+STREET_WIDTH
            Arc = Arc-10
        ElseIf dir=1
            L = RADIUS
            R = RADIUS+STREET_WIDTH
            ActionX = ActionX - Cos(Arc)*STREET_WIDTH
            ActionZ = ActionZ - Sin(Arc)*STREET_WIDTH
        Else
            L = RADIUS+STREET_WIDTH
            R = RADIUS
            ActionX= ActionX + Sin(Arc) * (2*RADIUS+STREET_WIDTH)
            ActionZ= ActionZ - Cos(Arc) * (2*RADIUS+STREET_WIDTH)
            Arc = Arc +180
            Arc = Arc+10
        EndIf
        'yellow Line (Left ROAD SIDE)
        xB[Count] = ActionX + Sin(arc) * L
        zB[Count] = ActionZ - Cos(arc) * L
   
        'blue line  (Right ROAD SIDE)
        xC[Count] = ActionX + Sin(arc) * R
        zC[Count] = ActionZ - Cos(arc) * R
           
        If Dir=2
            Arc = Arc -180
            ActionX= ActionX - Sin(Arc) * (2*RADIUS+STREET_WIDTH)
            ActionZ= ActionZ + Cos(Arc) * (2*RADIUS+STREET_WIDTH)
        EndIf
        Count=Count +1
    EndIf    
    DrawAll
    Flip 1
Until AppTerminate()

Function DrawAll()
    SetColor 255,255,255
    DrawText " Arc = " + Arc +"°"   ,100,100
    DrawText "Count= " + count      ,100,120
    DrawText "  Dir= " + DirWord    ,100,140

    DrawText "Hit <S>to step ahead",100, 20
    DrawText "Hit <D> to change direction LEFT STRAIGHT RIGHT", 100, 40

    SetColor 255,0,0
    DrawOval ActionX,ActionZ,5,5

    SetColor 255,255,0
    For Local i:Int = 0 To 199
        If xB[i+1] = 0 Exit
        DrawLine xB[i], zB[i], xB[i+1], zB[i+1]
    Next 

    SetColor 0,255,255
    For Local i:Int = 0 To 199
        If xB[i+1] = 0 Return
        DrawLine xC[i], zC[i], xC[i+1], zC[i+1]
    Next 
   
End Function




...back from North Pole.

Midimaster

#5
Lesson IV: Mesh, Vertex and Triangle

Here is a short excursion about the elements of a 3D object. Let us compare the technical terms of a 3D-object with the terms of a dice.

CreateMesh()
....creates an empty object. Related to our dice it is not more than "let us make a thing"

CreateSurface()
...creates an empty list of elements in the Mesh. Related to our dice we can say: The Surface enumerates all we can see from a dice: Corners, Faces,  Colors, etc... Without a Surface the dice is not visible.

AddVertex()
...creates a corner in the dice. A dice has 8 corners means, it's Surface has 8 Vertices. This vertices are unvisible, they are only virtual coordinates in 3D space.

AddTriangle()
...defines faces on the dice. A dice's face is a rectangle. But Mesh cannot handle rectangle, but only Triangles. So we need already 2 triangles to show a rectangle. Our dice has 6 rectangular faces. It's Surface has 12 Triangles. Each triangle is defined by 3 Vertices

CubeSurface.png


Triangles are not comparable with the experience we have from the real world. Lets have a top-view look on a pieces of paper. It is a Mesh with a Surface and has 4 Vertices. If you now would believe that 2 Triangles are enough to completely display it you are wrong. 2 triangles would only display the upper side of the paper. When we look at a such a "paper"-mesh from behind it would be invisible. So the backside needs 2 more Triangles.

So we can say Triangles are only visible from one side, from the other side they are invisible.

In the case of our dice, we would not need the double number of triangles, because there is no possibility to look "inside" the dice. But if this cube is not a dice, but a "transport-box" we immediately would need 24 triangles to keep the box visible in all aspects.

To guarantee, that the triangle has the correct orientation and is visible we have to define the correct sequence of Vertices.
SurfaceABCD.png
When you enumerate the points clockwise...
AddTriangle Surface, A, C, B
AddTraingle Surface, B, C, D

... this rectangle would be visible from front side


When you enumerate the points counterclockwise... .
AddTriangle Surface, A, B, C
AddTraingle Surface, B, D, C

... this rectangle would be visible from back side


EntityTexture()
...adds a colored content to a surface. A final surface without Texture is visible as complete white object. In the case of our paper-mesh this would be sufficient. In the case of our dice the Texture contains the dots   

dice.png



Now to our Road

This is the minimum startup example:

Code (vb) Select
Graphics3D 800,600,32,0

Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street = CreateMesh()
Global Surf   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#

Global xA#, zA#    ; left  street waypoint
Global xB#, zB#    ; right street waypoint
Global xC#, zC#    ; left  street waypoint +1
Global xD#, zD#    ; right street waypoint +1

StraightRoad

UpdateNormals Street

;top-view camera:
Global Camera = CreateCamera()
PositionEntity Camera,0,5,0
TurnEntity   Camera, 90,0,0

WireFrame True
Repeat
    UpdateWorld
    RenderWorld
    Flip 1
Until KeyHit(1)

We create a Mesh and a Surface and a Camera with a top-down-view.
We define 4 waypoints A B C D
We write all this standard stuff for 3D
Have a look on the command WireFrame. This will show us the triangles and not the solid surface.


Calculating the waypoints

Code (vb) Select
Function StraightRoad()
    Local sinus#   = Sin(Arc)
    Local cosinus# = Cos(Arc)
  

    ; A and B
    xA = cosinus * LEFT_SIDE   + ActionX
    zA = sinus   * LEFT_SIDE   + ActionZ

    xB = cosinus * RIGHT_SIDE  + ActionX
    zB = sinus   * RIGHT_SIDE  + ActionZ

    ; move ahead
    ActionX = ActionX - sinus
    ActionZ = ActionZ + cosinus   

    ; C and D
    xC = cosinus * LEFT_SIDE   + ActionX
    zC = sinus   * LEFT_SIDE   + ActionZ

    xD = cosinus * RIGHT_SIDE  + ActionX
    zD = sinus   * RIGHT_SIDE  + ActionZ


    AddVertexes

    ; now the 2 triangles
    AddTraingles     
End Function
In the function StraightRoad() we calculate the 4 waypoints related to its position like in lesson III.



Then we Add this values to the surface with AddVertices()

Code (vb) Select
Function AddVertexes()
        AddVertex Surf, xA, 0, zA, 0, 0
        AddVertex Surf, xB, 0, zB, 0, 0
        AddVertex Surf, xC, 0, zC, 0, 0
        AddVertex Surf, xD, 0, zD, 0, 0
End Function

A Vertex needs 5 parameters The coordinates for X Y and Z. At the moment our Y is always 0. Here you would be able to give the road a altitude (for a bridge or over a hill)

The 4th and 5th parameter is related to the texture. At the moment we dont use it.
Out  4 waypoints are now added into a list with 4 entries starting with item-0 to item-3 like an array.


The last step is defining the triangles.

Code (vb) Select
Function AddTraingles()

    ;    C--------D
    ;    |        |
    ;    |        |
    ;    |        |
    ;    |        |
    ;    A--------B


    ; vertex-list:
    ; 0.  1.  2.  3.   
    ; A - B - C - D 


    ; A-D-B:           A   D   B 
    AddTriangle Surf , 0 , 3 , 1

    ; A-C-D:           A   C   D 
    AddTriangle Surf , 0 , 2 , 3
End Function


When you start the app you will see nothing spectacular: a rectangle as a first street element.

But it is your first selfmade 3D-object. you can move it like a Cube, or colorize it, size it. Try what happens if you comment out the WireFrame command. Try to play around with using a different ARC%. That's enough for today.



Here is the complete code in Blitz3D:
Code (vb) Select
Graphics3D 800,600,32,0

Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street = CreateMesh()
Global Surf   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#

Global xA#, zA#    ; left  street waypoint
Global xB#, zB#    ; right street waypoint
Global xC#, zC#    ; left  street waypoint +1
Global xD#, zD#    ; right street waypoint +1

StraightRoad

UpdateNormals Street


;top-view camera:
Global Camera = CreateCamera()
PositionEntity Camera,0,5,0
TurnEntity   Camera, 90,0,0



WireFrame True
Repeat
    UpdateWorld
    RenderWorld
    Flip 1
Until KeyHit(1)





Function StraightRoad()
    Local sinus#   = Sin(Arc)
    Local cosinus# = Cos(Arc)
  

    ; A and B
    xA = cosinus * LEFT_SIDE   + ActionX
    zA = sinus   * LEFT_SIDE   + ActionZ

    xB = cosinus * RIGHT_SIDE  + ActionX
    zB = sinus   * RIGHT_SIDE  + ActionZ

    ; move ahead
    ActionX = ActionX - sinus
    ActionZ = ActionZ + cosinus   

    ; C and D
    xC = cosinus * LEFT_SIDE   + ActionX
    zC = sinus   * LEFT_SIDE   + ActionZ

    xD = cosinus * RIGHT_SIDE  + ActionX
    zD = sinus   * RIGHT_SIDE  + ActionZ


    AddVertexes

    ; now the 2 triangles
    AddTraingles     
End Function



Function AddVertexes()
        AddVertex Surf, xA, 0, zA, 0, 0
        AddVertex Surf, xB, 0, zB, 0, 0
        AddVertex Surf, xC, 0, zC, 0, 0
        AddVertex Surf, xD, 0, zD, 0, 0
End Function



Function AddTraingles()

    ;    C--------D
    ;    |        |
    ;    |        |
    ;    |        |
    ;    |        |
    ;    A--------B


    ; vertex-list:
    ; 0.  1.  2.  3.   
    ; A - B - C - D 


    ; A-D-B:           A   D   B 
    AddTriangle Surf , 0 , 3 , 1

    ; A-C-D:           A   C   D 
    AddTriangle Surf , 0 , 2 , 3
End Function



Here is the complete code in BlitzMax:

Code (vb) Select
SuperStrict
Import "../../minib3dsf/minib3d.bmx"

Graphics3D 800,600,0,32

Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street:TMesh = CreateMesh()
Global Surf:TSurface   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#

Global xA#, zA#    ' Left  street waypoint
Global xB#, zB#    ' Right street waypoint
Global xC#, zC#    ' Left  street waypoint +1
Global xD#, zD#    ' Right street waypoint +1

StraightRoad

UpdateNormals Street


'top-view camera:
Global Camera:TCamera = CreateCamera()
PositionEntity Camera,0,5,0
TurnEntity   Camera, 90,0,0



WireFrame True
Repeat
    UpdateWorld
    RenderWorld
    Flip 1
Until AppTerminate()





Function StraightRoad()
    Local sinus#   = Sin(Arc)
    Local cosinus# = Cos(Arc)
  

    ' A And B
    xA = cosinus * LEFT_SIDE   + ActionX
    zA = sinus   * LEFT_SIDE   + ActionZ

    xB = cosinus * RIGHT_SIDE  + ActionX
    zB = sinus   * RIGHT_SIDE  + ActionZ

    ' move ahead
    ActionX = ActionX - sinus
    ActionZ = ActionZ + cosinus   

    ' C And D
    xC = cosinus * LEFT_SIDE   + ActionX
    zC = sinus   * LEFT_SIDE   + ActionZ

    xD = cosinus * RIGHT_SIDE  + ActionX
    zD = sinus   * RIGHT_SIDE  + ActionZ


    AddVertexes

    ' now the 2 triangles
    AddTraingles     
End Function



Function AddVertexes()
        AddVertex Surf, xA, 0, zA, 0, 0
        AddVertex Surf, xB, 0, zB, 0, 0
        AddVertex Surf, xC, 0, zC, 0, 0
        AddVertex Surf, xD, 0, zD, 0, 0
End Function



Function AddTraingles()

    '    C--------D
    '    |        |
    '    |        |
    '    |        |
    '    |        |
    '    A--------B


    ' vertex-list:
    ' 0.  1.  2.  3.   
    ' A - B - C - D 


    ' A-D-B:           A   D   B 
    AddTriangle Surf , 0 , 3 , 1

    ' A-C-D:           A   C   D 
    AddTriangle Surf , 0 , 2 , 3
End Function

...back from North Pole.

Midimaster

#6
Lesson V: Continously adding street elements


As we now know how we have built a first street element, we will now re-arrange the code towards an endless adding of elements:

Each step adds 2 new waypoints. We now call them B and C, because in the next lesson we will need the letters A and B for something special.
Code (vb) Select
Global xB#, zB#    ; left  street point
Global xC#, zC#    ; right street point

This has no influence on the calculation. But we need to fix this in all related code lines.
Code (vb) Select
Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)
    xB = cosinus * LeftSide   + ActionX
    zB = sinus   * LeftSide   + ActionZ

    xC = cosinus * RightSide  + ActionX
    zC = sinus   * RightSide  + ActionZ
End Function



Each step this 2 waypoints will be connected with the prior waypoints to get 2 new triangles.
Code (vb) Select
Function AddTraingles()
    ;    .        .
    ;    .        .
    ;    .        .
    ;    B3------C3
    ;    |        |
    ;    |        |
    ;    |        |
    ;    B2------C2
    ;    |        |
    ;    |        |
    ;    |        |
    ;    |        |
    ;    B1------C1


    ; vertex-list:
    ; 0.   1.   2.   3.   4.   5.   6.   7.  
    ; B1 - C1 - B2 - C2 - B3 - C3 - B4 - ....


    Local LastPoint% = CountVertices(surf)-4

    ; B1-C2-C1:        B1            C2            C1 
    AddTriangle Surf , LastPoint+0 , LastPoint+3 , LastPoint+1

    ; B1-B2-C2:        B1            B2            C2 
    AddTriangle Surf , LastPoint+0 , LastPoint+2 , LastPoint+3
End Function


We outsource the calculation of the waypoints in a new function CalculateWayPoints()  to be prepaired for the next lesson. ("no spaghetti code" -> keep functions short!)

Code (vb) Select
Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)
xB = cosinus * LeftSide   + ActionX
zB = sinus   * LeftSide   + ActionZ

xC = cosinus * RightSide  + ActionX
zC = sinus   * RightSide  + ActionZ
End Function


To get a more detailed road we shrink the go-ahead-step to 1/4:

Code (vb) Select
ActionX = ActionX -   sinus/4.0
ActionZ = ActionZ + cosinus/4.0   


Hit the key <S> several times to add road segments!


Lesson6.gif


Here is the complete code for Blitz3D
Code (vb) Select
Graphics3D 800,600,32,0

Const POINTS%=2
Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street = CreateMesh()
Global Surf   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#

Global xB#, zB#    ; left  street point
Global xC#, zC#    ; right street point

;top-view camera:
Global Camera = CreateCamera()
PositionEntity Camera,0,5,0
TurnEntity   Camera, 90,0,0

WireFrame True
Repeat
    If KeyHit(31)
        StraightRoad
        UpdateNormals Street
        UpdateWorld
    EndIf
    RenderWorld
    Text 100,100," Arc = " + Arc +"°"
    Text 100,120,"Vertices= " + CountVertices(surf)
    Text 100, 20,"Hit <S> to step ahead"
    Flip 1
Until KeyHit(1)


Function StraightRoad()
    ; adds a straight street element to the road

    Local sinus#   = Sin(Arc)
    Local cosinus# = Cos(Arc)
  
    CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE

    AddVertexes
    If CountVertices(surf)> POINTS
        AddTraingles     
    EndIf
    ActionX = ActionX -   sinus/4.0
    ActionZ = ActionZ + cosinus/4.0   
End Function


Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)
    xB = cosinus * LeftSide   + ActionX
    zB = sinus   * LeftSide   + ActionZ

    xC = cosinus * RightSide  + ActionX
    zC = sinus   * RightSide  + ActionZ
End Function


Function AddVertexes()
    ; 2 points:         x   y  z   u  v
        AddVertex Surf, xB, 0, zB, 0, 0
        AddVertex Surf, xC, 0, zC, 0, 0
End Function


Function AddTraingles()
    ;    .        .
    ;    .        .
    ;    .        .
    ;    B3------C3
    ;    |        |
    ;    |        |
    ;    |        |
    ;    B2------C2
    ;    |        |
    ;    |        |
    ;    |        |
    ;    B1------C1


    ; vertex-list:
    ; 0.   1.   2.   3.   4.   5.   6.   7.  
    ; B1 - C1 - B2 - C2 - B3 - C3 - B4 - ....


    Local LastPoint% = CountVertices(surf)-4

    ; B1-C2-C1:        B1            C2            C1 
    AddTriangle Surf , LastPoint+0 , LastPoint+3 , LastPoint+1

    ; B1-B2-C2:        B1            B2            C2 
    AddTriangle Surf , LastPoint+0 , LastPoint+2 , LastPoint+3
End Function



Here is the complete code for BlitzMAX

Code (vb) Select
SuperStrict
Import "../../minib3dsf/minib3d.bmx"

Graphics3D 800,600,0,32

Const POINTS%=2
Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street:TMesh = CreateMesh()
Global Surf:TSurface   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#

Global xB#, zB#    ' Left  street point
Global xC#, zC#    ' Right street point


'top-view camera:
Global Camera:TCamera = CreateCamera()
PositionEntity Camera,0,5,0
TurnEntity   Camera, 90,0,0
    Print "------------------------"
    Print " Arc = " + Arc +"°"
    Print "Verteces= " + CountVertices(surf)
    Print"Hit <S> to step ahead"

WireFrame True
Repeat
    If KeyHit(KEY_S)
        StraightRoad
        UpdateNormals Street
        UpdateWorld
        Print "Vertices= " + CountVertices(surf)
        Print"Hit <S> to step ahead"
    EndIf
    RenderWorld

    Flip 1
Until AppTerminate()

Function StraightRoad()
    Local sinus#   = Sin(Arc)
    Local cosinus# = Cos(Arc)

    CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE

    AddVertexes
    If CountVertices(surf)> POINTS
        AddTraingles     
    EndIf
    ActionX = ActionX -   sinus/4.0
    ActionZ = ActionZ + cosinus/4.0   
End Function


Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)
    xB = cosinus * LeftSide   + ActionX
    zB = sinus   * LeftSide   + ActionZ

    xC = cosinus * RightSide  + ActionX
    zC = sinus   * RightSide  + ActionZ
End Function


Function AddVertexes()
        ' 2 points:     x   y  z   u  v
        AddVertex Surf, xB, 0, zB, 0, 0
        AddVertex Surf, xC, 0, zC, 0, 0
End Function


Function AddTraingles()
    '    .        .
    '    .        .
    '    .        .
    '    B1------C1
    '    |        |
    '    |        |
    '    |        |
    '    B1------C1
    '    |        |
    '    |        |
    '    |        |
    '    B1------C1

    ' vertex-list:
    ' 0.   1.   2.   3.   4.   5.   6.   7.  
    ' B1 - C1 - B2 - C2 - B3 - C3 - B4 - ....


    Local LastPoint% = CountVertices(surf)-4

    ' B1-C2-C1:        B1            C2            C1 
    AddTriangle Surf , LastPoint+0 , LastPoint+3 , LastPoint+1

    ' B1-B2-C2:        B1            B2            C2 
    AddTriangle Surf , LastPoint+0 , LastPoint+2 , LastPoint+3
End Function




...back from North Pole.

Midimaster

#7
Lesson VI: Texture and Curves

Today we will finish our Road-Project. We ad the texture and you will learn about U and V parameters

Adding a texture is very easy. We load a picture as "texture" and connect it with the street

Code (vb) Select
Global TEXTURE = LoadTexture("street.png",9)
EntityTexture Street,TEXTURE

Global Light = CreateLight(2)
LightRange Light,1000
PositionEntity Light, 10,50,-10
And we need some light to see the surface. A perfect texture would alway be a quadratic image with a edge length of a multiple of 2. So you can create an image with 64, 128, 256, 1024,....pixel

In the surface we have to define how to place the texture. We can move it, stretch it and repeat it. Think about the texture like a skin or a  "stretchable wallpaper" . You can cut out any rectangle of the texture and pin it at the any point of the surface. Here you can stretch it as you like.

The pins are our vertices. Only here we can fix the texture. Therefore we have the U and V parameters. U and V can only run between 0 and 1. 0

DiceCoordinates.png

U parameter:
0.0 means left edge of the texture  x =   0
0.5 means middle of the texture     x = 256
1.0 means right edge of the texture x = 512

V parameter:
0.0 means upper edge of the texture  y = 512
0.5 means middle of the texture      y = 256
1.0 means lower edge of the texture  y =   0

Some Examples

this is our rectangle:

rectangle.png

This shows no texture, because all values are 0:
A AddVertex Surf, x+0,y,z+0 , 0 , 0
B AddVertex Surf, x+1,y,z+0 , 0 , 0
C AddVertex Surf, x+0,y,z+1 , 0 , 0
D AddVertex Surf, x+1,y,z+1 , 0 , 0



This shows the complete texture in this rectangle:
A AddVertex Surf, x+0,y,z+0 , 0 , 0
B AddVertex Surf, x+1,y,z+0 , 1 , 0
C AddVertex Surf, x+0,y,z+1 , 0 , 1
D AddVertex Surf, x+1,y,z+1 , 1 , 1



This shows the number 6 of our dice:
A AddVertex Surf, x+0,y,z+0 , 0.25 , 0
B AddVertex Surf, x+1,y,z+0 , 0.50 , 0
C AddVertex Surf, x+0,y,z+1 , 0.25 , 0.25
D AddVertex Surf, x+1,y,z+1 , 0.50 , 0.25



This shows the numbers 5 6 2 of our dice and will look terrible compressed:
A AddVertex Surf, x+0,y,z+0 , 0.0  , 0
B AddVertex Surf, x+1,y,z+0 , 0.75 , 0
C AddVertex Surf, x+0,y,z+1 , 0.0  , 0.25
D AddVertex Surf, x+1,y,z+1 , 0.75 , 0.25


In our road we need the complete texture width so the U is for the left side always 0 and for the ride side always 1

The V runs step by step from 0 to 1 and when it reaches 1 we run backwards down to 0.

This backward run is because we dont want to see a brightness jump when reaching the V=1. you never know, whether your texture has the same color at upper edge like at lower edge. So it is better to mirror the texture, than to restart at 0.

So our V runs like this:
0.0 - 0.2 - 0.4 - 0-6 - 0.8 - 1.0 - 0.8 - 0.6 - 0.4 - 0.2 - 0.0 - 0.2 - 0.4 - .....


We place this code in the function AddVertexes(). The variable DistSwap% will run from 0 to 19, and V# will be calculated running from 0 to 1 and then back to 0 in 10 steps:
Global DistSwap%
.....

Function AddVertexes()
    Local v#
    If DistSwap<11
        v= DistSwap/10.0
    Else
        v= (20.0-DistSwap)/10.0   
    EndIf
    DistSwap = (DistSwap+1) Mod 20

    ; 2 points:     x   y  z   u  v
    AddVertex Surf, xB, 0, zB, 0, v   ;<---- See U and V
    AddVertex Surf, xC, 0, zC, 1, v   ;<---- See U and V 
End Function



Banquette


As in real road construction also in the 3D world a baguette is important. It levels out the difference between the street level and the alternating ground altitude. Without banquette we could "look under the street". so we add a banquette to our road. This is our new road profile:

; PROFILE:
;
; (left side)                                      (right side)
;                  B --------Street----------C
;                 *                           *
; ___Ground______*____________Floor____________*________________________
;             A *                               * D 
;

The waypoints A and D lay below the ground the planes A-B and C-D will prevent the look below the road.

We need 4 waypoints now:

Global xA#, zA#    ; right banquette point
Global xB#, zB#    ; left  street point
Global xC#, zC#    ; right street point
Global xD#, zD#    ; right banquette point


And the Y parameter of AddVertex is now used for the deepth of the banquette waypoints:

        AddVertex Surf, xA,-0.1, zA, 0.0, v
        AddVertex Surf, xB,   0, zB, 0.1, v
        AddVertex Surf, xC,   0, zC, 0.9, v
        AddVertex Surf, xD,-0.1, zD, 1.0, v

The U parameters show us: The street texture picture starts om the left with a banquette from 0 to 0.1, then the road follows between 0.1 and 0.9 and the right banquette finishs the picture between 0.9 and 1

streettext.png




To have two more waypoints means also to have 4 new triangles to represent the new banquette planes A1-B1-A2-B2 and C1-D1-C2-D2

6Triangles.png

Function AddTraingles()
    ;    .              .
    ;    .              .
    ;    .              .
    ;    A3-B3------C3-D3
    ;    |  |        |  |
    ;    |  |        |  |
    ;    |  |        |  |
    ;    A2-B2------C2-D2
    ;    |  |        |  |
    ;    |  |        |  |
    ;    |  |        |  |
    ;    A1-B1------C1-D1


    ; vertex-list:
    ; 0  1  2  3    4  5  6  7    8  9.... 
    ; A1-B1-C1-D1 - A2-B2-C2-D2 - A3-A4...


    Local LastPoint% = CountVertices(surf)-8


; left baquette:


    ; A1-B2-B1:        A1            B2            B1 
    AddTriangle Surf , LastPoint+0 , LastPoint+5 , LastPoint+1

    ; A1-A2-B2:        A1            A2            B2 
    AddTriangle Surf , LastPoint+0 , LastPoint+4 , LastPoint+5


; road


    ; B1-C2-C1:        B1            C2            C1 
    AddTriangle Surf , LastPoint+1 , LastPoint+6 , LastPoint+2

    ; B1-B2-C2:        B1            B2            C2 
    AddTriangle Surf , LastPoint+1 , LastPoint+5 , LastPoint+6


;right banquette:


    ; C1-D2-D1:        C1            D2            D1 
    AddTriangle Surf , LastPoint+2 , LastPoint+7 , LastPoint+3

    ; C1-C2-D2:        C1            C2            D2 
    AddTriangle Surf , LastPoint+2 , LastPoint+6 , LastPoint+7
End Function





Ready for today! You need the image street.png from the attachment to run the app!

Lesson7.gif


Here comes the complete code for Blitz3D
Graphics3D 800,600,32,0

Const POINTS%=4
Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street = CreateMesh()
Global Surf   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#, DistSwap%

Global xA#, zA#    ; right banquette point
Global xB#, zB#    ; left  street point
Global xC#, zC#    ; right street point
Global xD#, zD#    ; right banquette point


Global TEXTURE = LoadTexture("street.png",9)
EntityTexture Street,TEXTURE



;top-view camera:
Global Camera = CreateCamera()
PositionEntity Camera,0,3,-2
TurnEntity   Camera, 30,-10,0

Global Light = CreateLight(2)
LightRange Light,1000
PositionEntity Light, 10,50,-10


;WireFrame True
Repeat
    If KeyHit(31)
        StraightRoad
        UpdateNormals Street
        UpdateWorld
    EndIf
    RenderWorld
    Text 100,100," Arc = " + Arc +"°"
    Text 100,120,"Vertices= " + CountVertices(surf)
    Text 100, 20,"Hit <S> to step ahead"
    Flip 1
Until KeyHit(1)



Function StraightRoad()
    ; adds a straight street element to the road

    Local sinus#   = Sin(Arc)
    Local cosinus# = Cos(Arc)
  
    CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE

    AddVertexes
    If CountVertices(surf)> POINTS
        AddTraingles     
    EndIf
    ActionX = ActionX -   sinus/4.0
    ActionZ = ActionZ + cosinus/4.0   
End Function


Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)

    Local banquetL#, banquetR#
    If dir=2
        banquetL = 1.05
        banquetR = 0.95
    Else
        banquetL = 0.95
        banquetR = 1.05
    EndIf


    xA = banquetL * cosinus * LeftSide   + ActionX
    zA = banquetL * sinus   * LeftSide   + ActionZ

    xB = cosinus * LeftSide   + ActionX
    zB = sinus   * LeftSide   + ActionZ

    xC = cosinus * RightSide  + ActionX
    zC = sinus   * RightSide  + ActionZ

    xD = banquetR *cosinus * RightSide  + ActionX
    zD = banquetR *sinus   * RightSide  + ActionZ
End Function


Function AddVertexes()
        Local v#
        If DistSwap<11
            v= DistSwap/10.0
        Else
            v= (20.0-DistSwap)/10.0   
        EndIf
        DistSwap = (DistSwap+1) Mod 20

    ; 4 points:         x    y   z    u   v
        AddVertex Surf, xA,-0.1, zA, 0.0, v
        AddVertex Surf, xB,   0, zB, 0.1, v
        AddVertex Surf, xC,   0, zC, 0.9, v
        AddVertex Surf, xD,-0.1, zD, 1.0, v
End Function


Function AddTraingles()
    ;    .              .
    ;    .              .
    ;    .              .
    ;    A3-B3------C3-D3
    ;    |  |        |  |
    ;    |  |        |  |
    ;    |  |        |  |
    ;    A2-B2------C2-D2
    ;    |  |        |  |
    ;    |  |        |  |
    ;    |  |        |  |
    ;    A1-B1------C1-D1


    ; vertex-list:
    ; 0  1  2  3    4  5  6  7    8  9.... 
    ; A1-B1-C1-D1 - A2-B2-C2-D2 - A3-A4...


    Local LastPoint% = CountVertices(surf)-8


; left baquette:


    ; A1-B2-B1:        A1            B2            B1 
    AddTriangle Surf , LastPoint+0 , LastPoint+5 , LastPoint+1

    ; A1-A2-B2:        A1            A2            B2 
    AddTriangle Surf , LastPoint+0 , LastPoint+4 , LastPoint+5


; road


    ; B1-C2-C1:        B1            C2            C1 
    AddTriangle Surf , LastPoint+1 , LastPoint+6 , LastPoint+2

    ; B1-B2-C2:        B1            B2            C2 
    AddTriangle Surf , LastPoint+1 , LastPoint+5 , LastPoint+6


;right banquette:


    ; C1-D2-D1:        C1            D2            D1 
    AddTriangle Surf , LastPoint+2 , LastPoint+7 , LastPoint+3

    ; C1-C2-D2:        C1            C2            D2 
    AddTriangle Surf , LastPoint+2 , LastPoint+6 , LastPoint+7
End Function



And here is the code for BlitzMax:
SuperStrict
Import "../../minib3dsf/minib3d.bmx"

Graphics3D 800,600,0,32

Const POINTS%=4
Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street:TMesh = CreateMesh()
Global Surf:TSurface   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#, DistSwap%

Global xA#, zA#    ' Right banquette point
Global xB#, zB#    ' Left  street point
Global xC#, zC#    ' Right street point
Global xD#, zD#    ' Right banquette point

Global TEXTURE:TTexture = LoadTexture("street.png",9)
EntityTexture Street,TEXTURE

'top-view camera:
Global Camera:TCamera = CreateCamera()
PositionEntity Camera,0,3,-2
TurnEntity   Camera, 30,-10,0
    Print "------------------------"
    Print " Arc = " + Arc +"°"
    Print "Verteces= " + CountVertices(surf)
    Print"Hit <S> to step ahead"

Global Light:TLight = CreateLight(2)
LightRange Light,1000
PositionEntity Light, 10,50,-10


'WireFrame True
Repeat
    If KeyHit(KEY_S)
        StraightRoad
        UpdateNormals Street
        UpdateWorld
        Print "Vertices= " + CountVertices(surf)
        Print"Hit <S> to step ahead"
    EndIf
    RenderWorld

    Flip 1
Until AppTerminate()

Function StraightRoad()
    Local sinus#   = Sin(Arc)
    Local cosinus# = Cos(Arc)

    CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE

    AddVertexes
    If CountVertices(surf)> POINTS
        AddTraingles     
    EndIf
    ActionX = ActionX -   sinus/4.0
    ActionZ = ActionZ + cosinus/4.0   
End Function


Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)
    Local banquetL#, banquetR#
    banquetL = 0.95
    banquetR = 1.05

    xA = banquetL * cosinus * LeftSide   + ActionX
    zA = banquetL * sinus   * LeftSide   + ActionZ

    xB = cosinus * LeftSide   + ActionX
    zB = sinus   * LeftSide   + ActionZ

    xC = cosinus * RightSide  + ActionX
    zC = sinus   * RightSide  + ActionZ

    xD = banquetR *cosinus * RightSide  + ActionX
    zD = banquetR *sinus   * RightSide  + ActionZ
End Function


Function AddVertexes()
        Local v#
        If DistSwap<11
            v= DistSwap/10.0
        Else
            v= (20.0-DistSwap)/10.0   
        EndIf
        DistSwap = (DistSwap+1) Mod 20

    ' 4 points:         x    y   z    u   v
        AddVertex Surf, xA,-0.1, zA, 0.0, v
        AddVertex Surf, xB,   0, zB, 0.1, v
        AddVertex Surf, xC,   0, zC, 0.9, v
        AddVertex Surf, xD,-0.1, zD, 1.0, v
End Function


Function AddTraingles()
    '    .              .
    '    .              .
    '    .              .
    '    A3-B3------C3-D3
    '    |  |        |  |
    '    |  |        |  |
    '    |  |        |  |
    '    A2-B2------C2-D2
    '    |  |        |  |
    '    |  |        |  |
    '    |  |        |  |
    '    A1-B1------C1-D1


    ' vertex-list:
    ' 0  1  2  3    4  5  6  7    8  9.... 
    ' A1-B1-C1-D1 - A2-B2-C2-D2 - A3-A4...

    Local LastPoint% = CountVertices(surf)-8

' Left baquette:

    ' A1-B2-B1:        A1            B2            B1 
    AddTriangle Surf , LastPoint+0 , LastPoint+5 , LastPoint+1

    ' A1-A2-B2:        A1            A2            B2 
    AddTriangle Surf , LastPoint+0 , LastPoint+4 , LastPoint+5


' road

    ' B1-C2-C1:        B1            C2            C1 
    AddTriangle Surf , LastPoint+1 , LastPoint+6 , LastPoint+2

    ' B1-B2-C2:        B1            B2            C2 
    AddTriangle Surf , LastPoint+1 , LastPoint+5 , LastPoint+6


'Right banquette:

    ' C1-D2-D1:        C1            D2            D1 
    AddTriangle Surf , LastPoint+2 , LastPoint+7 , LastPoint+3

    ' C1-C2-D2:        C1            C2            D2 
    AddTriangle Surf , LastPoint+2 , LastPoint+6 , LastPoint+7
End Function
...back from North Pole.

Midimaster

Lesson VII: Curves And Long Sequences


to change from Single-Seqment-Adding to Script-Based-Road construction we simply have to add a For/Next-loop into the construction function StraightRoad()

Code (vb) Select
..
StraightRoad 10
UpdateNormals Street
...

Function StraightRoad(Steps%)
; adds steps% straight street elements to the road

Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)
    For i%= 0 To Steps-1

CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE
AddVertexes
AddTraingles 

ActionX = ActionX -   sinus/4.0
ActionZ = ActionZ + cosinus/4.0
Next
End Function



The Left Curve

In the curved version the sinus calculation needs to be inside the For/Next-loop. And we add 5° to the Arc each loop:

Function CurvedRoad(Steps%)
; adds steps% straight street elements to the road

    For i%= 0 To Steps-1
Arc=Arc+5
Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)

CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE
AddVertexes
AddTraingles 

Next
End Function
As the action point does not change during turns we need not to add something to it




Right Curve

This is more complicate. We need to position the action point on the right side of the road, then add 180° to the Arc. Before we leave the function we need to reset these both changes.

As a second thing we have to think about the fact, that right road edge is now nearer to the action point than the left edge. We react by simply swap the distance constants LEFT_SIDE and RIGHT_SIDE in the calculation-call:

; for left turns:
CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE
...
; for right turns:
CalculateWayPoints cosinus, sinus, RIGHT_SIDE, LEFT_SIDE


The same happens in the banquette calculation

Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)

Local banquetL#=0.95
Local banquetR#=1.05

If LeftSide>RightSide
banquetL = 1.035
banquetR = 0.915
EndIf
....

Don't ask me, why the right banquette needs different values. I adjusted it because of the better look. maybe the use of FLOATs leads to calculation inaccuracies.


The complete right curve:
Function RightCurve(Steps%)
; adds steps% right turn street elements to the road

ActionX= ActionX + Cos(Arc) * (2*RADIUS+1)
ActionZ= ActionZ + Sin(Arc) * (2*RADIUS+1)
Arc=Arc+180

    For i%= 0 To Steps-1
Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)

CalculateWayPoints cosinus, sinus, RIGHT_SIDE, LEFT_SIDE
AddVertexes
AddTraingles
 
Arc = Arc -5
Next

Arc=Arc-180
ActionX= ActionX - Cos(Arc) * (2*RADIUS+1)
ActionZ= ActionZ - Sin(Arc) * (2*RADIUS+1)
End Function



Now you can write scrips like this to get a complete racing course:

StraightRoad 20
RightCurve 10
StraightRoad 30
LeftCurve 9
LeftCurve 9
StraightRoad 10
RightCurve 10
RightCurve 10
LeftCurve 43
StraightRoad 80
LeftCurve 18
RightCurve 36


Lessson7.png


here is the complete code in Blitz3D

Graphics3D 800,600,32,0

Const POINTS%=4
Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street = CreateMesh()
Global Surf   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#, DistSwap%

Global xA#, zA# ; right banquette point
Global xB#, zB# ; left  street point
Global xC#, zC# ; right street point
Global xD#, zD# ; right banquette point


Global TEXTURE = LoadTexture("street.png",9)
EntityTexture Street,TEXTURE

;' 3D-view camera:
Global Camera = CreateCamera()
PositionEntity Camera,0,6,-5
RotateEntity   Camera, 30,-10,0

; top-view camera If you like:
;PositionEntity Camera,0,5,5
;rotateEntity   Camera, 90,0,0

Global Light = CreateLight(2)
LightRange Light,1000
PositionEntity Light, 10,50,-10


StraightRoad 20
RightCurve 10
StraightRoad 30
LeftCurve 9
LeftCurve 9
StraightRoad 10
RightCurve 10
RightCurve 10
LeftCurve 43
StraightRoad 80
LeftCurve 18
RightCurve 36


UpdateNormals Street

; two balls To show the action points:
kugel= CreateSphere()
ScaleEntity Kugel,0.1,0.1,0.1
kugel2= CreateSphere()
ScaleEntity Kugel2,0.1,0.1,0.1
EntityColor kugel2, 2550,0,0


;WireFrame True
Repeat
UpdateWorld

PositionEntity kugel, ActionX,0, ActionZ
PositionEntity kugel2, ActionX + Cos(Arc) * (2*RADIUS+1)  , 0, ActionZ+ Sin(Arc) * (2*RADIUS+1)

RenderWorld
Flip 1
Until KeyHit(1)



Function StraightRoad(Steps%)
; adds steps% straight street elements to the road

Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)
    For i%= 0 To Steps-1

CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE
AddVertexes
AddTraingles 

ActionX = ActionX -   sinus/4.0
ActionZ = ActionZ + cosinus/4.0
Next
End Function


Function RightCurve(Steps%)
; adds steps% right turn street elements to the road

ActionX= ActionX + Cos(Arc) * (2*RADIUS+1)
ActionZ= ActionZ + Sin(Arc) * (2*RADIUS+1)
Arc=Arc+180

    For i%= 0 To Steps-1
Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)

CalculateWayPoints cosinus, sinus, RIGHT_SIDE, LEFT_SIDE
AddVertexes
AddTraingles 


Arc = Arc -5
Next

Arc=Arc-180
ActionX= ActionX - Cos(Arc) * (2*RADIUS+1)
ActionZ= ActionZ - Sin(Arc) * (2*RADIUS+1)
End Function


Function LeftCurve(Steps%)
; adds steps% left turn street elements to the road

    For i%= 0 To Steps-1
Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)

CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE
AddVertexes
AddTraingles 

Arc = Arc + 5
Next
End Function


Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)
Local banquetL#=0.95
Local banquetR#=1.05

If LeftSide>RightSide
banquetL = 1.035
banquetR = 0.915
EndIf

xA = banquetL * cosinus * LeftSide   + ActionX
zA = banquetL * sinus   * LeftSide   + ActionZ

xB = cosinus * LeftSide   + ActionX
zB = sinus   * LeftSide   + ActionZ

xC = cosinus * RightSide  + ActionX
zC = sinus   * RightSide  + ActionZ

xD = banquetR *cosinus * RightSide  + ActionX
zD = banquetR *sinus   * RightSide  + ActionZ
End Function


Function AddVertexes()
Local v#
If DistSwap<11
v= DistSwap/10.0
Else
v= (20.0-DistSwap)/10.0
EndIf
DistSwap = (DistSwap+1) Mod 20

; 4 points:         x    y   z    u   v
AddVertex Surf, xA,-0.1, zA, 0.0, v
AddVertex Surf, xB,   0, zB, 0.1, v
AddVertex Surf, xC,   0, zC, 0.9, v
AddVertex Surf, xD,-0.1, zD, 1.0, v
End Function


Function AddTraingles()
If CountVertices(surf)<= POINTS Return
;    .              .
;    .              .
;    .              .
;    A3-B3------C3-D3
;    |  |        |  |
;    |  |        |  |
;    |  |        |  |
;    A2-B2------C2-D2
;    |  |        |  |
;    |  |        |  |
;    |  |        |  |
;    A1-B1------C1-D1


; vertex-list:
; 0  1  2  3    4  5  6  7    8  9.... 
; A1-B1-C1-D1 - A2-B2-C2-D2 - A3-A4...


Local LastPoint% = CountVertices(surf)-2*POINTS


; left baquette:


; A1-B2-B1:        A1            B2            B1 
AddTriangle Surf , LastPoint+0 , LastPoint+5 , LastPoint+1

; A1-A2-B2:        A1            A2            B2 
AddTriangle Surf , LastPoint+0 , LastPoint+4 , LastPoint+5


; road


; B1-C2-C1:        B1            C2            C1 
AddTriangle Surf , LastPoint+1 , LastPoint+6 , LastPoint+2

; B1-B2-C2:        B1            B2            C2 
AddTriangle Surf , LastPoint+1 , LastPoint+5 , LastPoint+6


;right banquette:


; C1-D2-D1:        C1            D2            D1 
AddTriangle Surf , LastPoint+2 , LastPoint+7 , LastPoint+3

; C1-C2-D2:        C1            C2            D2 
AddTriangle Surf , LastPoint+2 , LastPoint+6 , LastPoint+7
End Function



And here is the complete code in BlitzMax.

SuperStrict
Import "../../minib3dsf/minib3d.bmx"

Graphics3D 800,600,0,32

Const POINTS%=4
Const RADIUS#=2
Const LEFT_SIDE#=RADIUS , RIGHT_SIDE#=RADIUS+1

Global Street:TMesh = CreateMesh()
Global Surf:TSurface   = CreateSurface(Street)

Global Arc%, ActionX# , ActionZ#, DistSwap%

Global xA#, zA# ' Right banquette point
Global xB#, zB# ' Left  street point
Global xC#, zC# ' Right street point
Global xD#, zD# ' Right banquette point

Global TEXTURE:TTexture = LoadTexture("street.png",9)
EntityTexture Street,TEXTURE

' 3D-view camera:
Global Camera:TCamera = CreateCamera()
PositionEntity Camera,0,6,-5
RotateEntity   Camera, 30,-10,0

' top-view camera if you like:
'PositionEntity Camera,0,5,5
'RotateEntity   Camera, 90,0,0

Global Light:TLight = CreateLight(2)
LightRange Light,1000
PositionEntity Light, 10,50,-10


StraightRoad 20
RightCurve 10
StraightRoad 30
LeftCurve 9
LeftCurve 9
StraightRoad 10
RightCurve 10
RightCurve 10
LeftCurve 43
StraightRoad 80
LeftCurve 18
RightCurve 36

UpdateNormals Street

' two balls to show the action points:
Global kugel:Tentity = CreateSphere()
ScaleEntity Kugel,0.1,0.1,0.1
Global kugel2:TEntity = CreateSphere()
ScaleEntity Kugel2,0.1,0.1,0.1
EntityColor kugel2, 2550,0,0



'WireFrame True
Repeat
UpdateWorld

PositionEntity kugel, ActionX,0, ActionZ
PositionEntity kugel2, ActionX + Cos(Arc) * (2*RADIUS+1)  , 0, ActionZ+ Sin(Arc) * (2*RADIUS+1)
RenderWorld

Flip 1
Until AppTerminate()

Function StraightRoad(Steps%)
' adds steps% straight street elements To the road

Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)

For Local i%= 0 Until Steps
CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE

AddVertexes
If CountVertices(surf)> POINTS
AddTraingles 
EndIf
ActionX = ActionX -   sinus/4.0
ActionZ = ActionZ + cosinus/4.0
Next
End Function


Function LeftCurve(Steps%)
' adds steps% Left turn street elements To the road

    For Local i%= 0 To Steps-1
Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)
CalculateWayPoints cosinus, sinus, LEFT_SIDE, RIGHT_SIDE
Arc = Arc + 5

AddVertexes
If CountVertices(surf)> POINTS
AddTraingles 
EndIf
Next
End Function



Function RightCurve(Steps%)
' adds steps% Right turn street elements To the road

ActionX= ActionX + Cos(Arc) * (2*RADIUS+1)
ActionZ= ActionZ + Sin(Arc) * (2*RADIUS+1)
Arc=Arc+180

    For Local i%= 0 To Steps-1
Local sinus#   = Sin(Arc)
Local cosinus# = Cos(Arc)
Arc = Arc -5

CalculateWayPoints cosinus, sinus, RIGHT_SIDE, LEFT_SIDE
AddVertexes
If CountVertices(surf)> POINTS
AddTraingles 
EndIf
Next

Arc=Arc-180
ActionX= ActionX - Cos(Arc) * (2*RADIUS+1)
ActionZ= ActionZ - Sin(Arc) * (2*RADIUS+1)
End Function









Function CalculateWayPoints(cosinus#, sinus#, LeftSide#, RightSide#)
Local banquetL#=0.95
Local banquetR#=1.05

If LeftSide>RightSide
banquetL = 1.035
banquetR = 0.915
EndIf

xA = banquetL * cosinus * LeftSide   + ActionX
zA = banquetL * sinus   * LeftSide   + ActionZ

xB = cosinus * LeftSide   + ActionX
zB = sinus   * LeftSide   + ActionZ

xC = cosinus * RightSide  + ActionX
zC = sinus   * RightSide  + ActionZ

xD = banquetR *cosinus * RightSide  + ActionX
zD = banquetR *sinus   * RightSide  + ActionZ
End Function


Function AddVertexes()
Local v#
If DistSwap<11
v= DistSwap/10.0
Else
v= (20.0-DistSwap)/10.0
EndIf
DistSwap = (DistSwap+1) Mod 20

' 4 points:         x    y   z    u   v
AddVertex Surf, xA,-0.1, zA, 0.0, v
AddVertex Surf, xB,   0, zB, 0.1, v
AddVertex Surf, xC,   0, zC, 0.9, v
AddVertex Surf, xD,-0.1, zD, 1.0, v
End Function


Function AddTraingles()
If CountVertices(surf)<= POINTS Return
'    .              .
'    .              .
'    .              .
'    A3-B3------C3-D3
'    |  |        |  |
'    |  |        |  |
'    |  |        |  |
'    A2-B2------C2-D2
'    |  |        |  |
'    |  |        |  |
'    |  |        |  |
'    A1-B1------C1-D1


' vertex-list:
' 0  1  2  3    4  5  6  7    8  9.... 
' A1-B1-C1-D1 - A2-B2-C2-D2 - A3-A4...

Local LastPoint% = CountVertices(surf)-8

' Left baquette:

' A1-B2-B1:        A1            B2            B1 
AddTriangle Surf , LastPoint+0 , LastPoint+5 , LastPoint+1

' A1-A2-B2:        A1            A2            B2 
AddTriangle Surf , LastPoint+0 , LastPoint+4 , LastPoint+5


' road

' B1-C2-C1:        B1            C2            C1 
AddTriangle Surf , LastPoint+1 , LastPoint+6 , LastPoint+2

' B1-B2-C2:        B1            B2            C2 
AddTriangle Surf , LastPoint+1 , LastPoint+5 , LastPoint+6


'Right banquette:

' C1-D2-D1:        C1            D2            D1 
AddTriangle Surf , LastPoint+2 , LastPoint+7 , LastPoint+3

' C1-C2-D2:        C1            C2            D2 
AddTriangle Surf , LastPoint+2 , LastPoint+6 , LastPoint+7
End Function
...back from North Pole.