Dark Bit Factory & Gravity

PROGRAMMING => Freebasic => Topic started by: Hezad on August 21, 2008

Title: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Hezad on August 21, 2008
Hello there !

So it's my first post  :updance:

First, just a few words to present myself :
I'm a french (so please excuse me if some of my sentences sound ambiguous !) freebasic user since some years now but I really don't define myself as a programmer ! In fact, I started "coding" on a TO7 and a ZX Spectrum (by "coding", i mean copying the programs listed in the manual and writing some ultra simple stuff). Then I coded a bit on PC with QBasic, I quickly stopped and started to code with GFA Basic on my Atari Mega STE (The only thing I ever really coded with it were a pong) :P
So 1 or 2 years ago, after some years without coding anything, I decided to start again coding and I found an awesome language which was (and IS) freebasic !

Oh and I'm a musician too :) Guitar, saxophone, drums and computer music. Don't ask me if I ever written any demo music, the answer is no :p I tried one or two times but it was catastrophic  :whack:


Well ! So now, about the Gouraud filling :D The relsoft's tutorial (http://www.phatcode.net/articles.php?id=214 (http://www.phatcode.net/articles.php?id=214)) helped me a lot since it totally guided me through this small project. Thanks a lot to him for that ! And thanks to Freebasic forum users which helped me a lot to understand Screen buffer access, especially counting_pine (I didn't want to use Pset : even if my code is everything but optimized, I wanted to access the buffer "by myself").

a screenshot :
(http://narky00.free.fr/screen_gouraud.jpg)

a Zip with code+Exe :
http://narky00.free.fr/Gouraud.rar (http://narky00.free.fr/Gouraud.rar)

And the code :
#Define Norm(a) sqr(a.x*a.x + a.y*a.y + a.z*a.z)

Const MAX_TRIANGLES_PER_OBJECT = 500
Const xRes = 640, yRes = 480

Dim shared as integer ptr ScrPtr

Screenres xRes,yRes,32,2

ScrPtr = Screenptr

Type Vector
    as single x,y,z
End Type

Type ColorT
    as integer r,g,b
End Type

Type VertexT
   
    as Vector Pos3d
   
    as Vector Pos2d
   
    as ColorT Color
   
End Type

Type TriangleT
   
    as Vector Normal
   
    as integer idZ
   
    as VertexT Vertex(2)
   
End Type

Type ObjectT
   
    as TriangleT Triangle(MAX_TRIANGLES_PER_OBJECT)
   
    as Vector Center
   
    as integer nb_triangles
   
End Type

'' 3d Characteristics
Dim shared as single FOV = 256

Sub Sort_Object_Z(obj as ObjectT)
   
    For i as integer = 1 to obj.nb_Triangles
       
        obj.triangle(i).IdZ = i
        if i>1 then
            if obj.triangle(i).vertex(0).pos3d.z + obj.triangle(i).vertex(1).pos3d.z + obj.triangle(i).vertex(2).pos3d.z < obj.triangle(i-1).vertex(0).pos3d.z + obj.triangle(i-1).vertex(1).pos3d.z + obj.triangle(i-1).vertex(2).pos3d.z then

                Swap obj.triangle(i), obj.triangle(i-1)
                i=1
               
            end if
        end if
       
    Next
   
End Sub


Sub Calc_Triangle_2d_ZNormal(Tri as TriangleT)
   
    Tri.Normal.Z = (Tri.Vertex(1).pos2d.x - Tri.Vertex(0).pos2d.x) * (Tri.Vertex(0).pos2d.y - Tri.Vertex(2).pos2d.y) - (Tri.Vertex(1).pos2d.y - Tri.Vertex(0).pos2d.y) * (Tri.Vertex(0).pos2d.x - Tri.Vertex(2).pos2d.x)

End Sub

Sub Calc_Triangle_Normal(Tri as TriangleT)
   
    dim as single ax,bx,ay,by,az,bz
   
    ax = Tri.Vertex(1).pos3d.x - Tri.Vertex(0).pos3d.x
    bx = Tri.Vertex(2).pos3d.x - Tri.Vertex(1).pos3d.x
    ay = Tri.Vertex(1).pos3d.y - Tri.Vertex(0).pos3d.y
    by = Tri.Vertex(2).pos3d.y - Tri.Vertex(1).pos3d.y
    az = Tri.Vertex(1).pos3d.z - Tri.Vertex(0).pos3d.z
    bz = Tri.Vertex(2).pos3d.z - Tri.Vertex(1).pos3d.z
   
    Tri.Normal.x = ay * bz - az * by
    Tri.Normal.y = az * bx - ax * bz
    Tri.Normal.z = ax * by - ay * bx
   
    '' Not normalized for now !! Normalize it if Code shading !
   
End Sub
   
Sub Project_Object(Obj as ObjectT)
   
    if obj.nb_triangles = 0 then exit Sub
   
    For i as integer = 1 to obj.nb_triangles
       
        For j as integer = 0 to 2
           
            if obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z = FOV then obj.Triangle(i).Vertex(j).pos3d.z+=1
           
            obj.Triangle(i).Vertex(j).pos2d.x = xRes/2 + FOV * (obj.Center.x + obj.Triangle(i).Vertex(j).pos3d.x) / (FOV - (obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
            obj.Triangle(i).Vertex(j).pos2d.y = yRes/2 - FOV * (obj.Center.y + obj.Triangle(i).Vertex(j).pos3d.y) / (FOV - (obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
        Next
       
    Next
   
End Sub

Sub GLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal r1 as integer,ByVal g1 as integer,ByVal b1 as integer,ByVal r2 as integer,ByVal g2 as integer,ByVal b2 as integer)
   
    dim as single dR,dG,dB
    dim as single R,g,b
   
    dim as integer itx1,itx2
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
   
    if x1<x2 then
        itx1 = x1
        itx2 = x2
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        itx1 = x2
        itx2 = x1
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    dR = (sr2 - sr1)/(itx2 - itx1)
    dG = (sg2 - sg1)/(itx2 - itx1)
    dB = (sb2 - sb1)/(itx2 - itx1)
   
    r = sr1
    g = sg1
    b = sb1
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
            ScrPtr[y*xRes+i] = rgb(r,g,b)
        end if

        r += dR
        g += dG
        b += dB
       
    Next
   
End Sub

Sub Gouraud_Fill(ByVal Triangle as TriangleT)
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1).Pos2d.y,Triangle.Vertex(0).Pos2d.y
        swap Triangle.Vertex(1).Pos2d.x,Triangle.Vertex(0).Pos2d.x
           
        swap Triangle.Vertex(1).Color.r,Triangle.Vertex(0).Color.r
        swap Triangle.Vertex(1).Color.g,Triangle.Vertex(0).Color.g
        swap Triangle.Vertex(1).Color.b,Triangle.Vertex(0).Color.b
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2).Pos2d.y,Triangle.Vertex(0).Pos2d.y
        swap Triangle.Vertex(2).Pos2d.x,Triangle.Vertex(0).Pos2d.x
           
        swap Triangle.Vertex(2).Color.r,Triangle.Vertex(0).Color.r
        swap Triangle.Vertex(2).Color.g,Triangle.Vertex(0).Color.g
        swap Triangle.Vertex(2).Color.b,Triangle.Vertex(0).Color.b
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2).Pos2d.y,Triangle.Vertex(1).Pos2d.y
        swap Triangle.Vertex(2).Pos2d.x,Triangle.Vertex(1).Pos2d.x
           
        swap Triangle.Vertex(2).Color.r,Triangle.Vertex(1).Color.r
        swap Triangle.Vertex(2).Color.g,Triangle.Vertex(1).Color.g
        swap Triangle.Vertex(2).Color.b,Triangle.Vertex(1).Color.b
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as integer dr1,dg1,db1,dr2,dg2,db2,dr3,dg3,db3
    dim as single Slope1,Slope2,Slope3
    dim as single RSlope1, GSlope1,BSlope1
    dim as single RSlope2, GSlope2,BSlope2
    dim as single RSlope3, GSlope3,BSlope3
   
   
    '' interpolate 0 to 1 (pos + color)
       
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dr1 = Triangle.Vertex(1).color.r - Triangle.Vertex(0).color.r
    dg1 = Triangle.Vertex(1).color.g - Triangle.Vertex(0).color.g
    db1 = Triangle.Vertex(1).color.b - Triangle.Vertex(0).color.b
   
    if dY1 <> 0 then
   
        Slope1 = dX1 / dY1
       
        RSlope1 = dR1 / dY1
        GSlope1 = dG1 / dY1
        BSlope1 = dB1 / dY1
       
    else
       
        Slope1 = 0
       
        RSlope1 = 0
        GSlope1 = 0
        BSlope1 = 0
       
    End if
   
   
    '' interpolate 1 to 2
       
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dr2 = Triangle.Vertex(2).color.r - Triangle.Vertex(1).color.r
    dg2 = Triangle.Vertex(2).color.g - Triangle.Vertex(1).color.g
    db2 = Triangle.Vertex(2).color.b - Triangle.Vertex(1).color.b
   
    if dY2 <> 0 then
   
        Slope2 = dX2 / dY2
       
        RSlope2 = dR2 / dY2
        GSlope2 = dG2 / dY2
        BSlope2 = dB2 / dY2
       
    else
       
        Slope2 = 0
       
        RSlope2 = 0
        GSlope2 = 0
        BSlope2 = 0
       
    End if
   
   
    '' interpolate 0 to 2
       
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dr3 = Triangle.Vertex(0).color.r - Triangle.Vertex(2).color.r
    dg3 = Triangle.Vertex(0).color.g - Triangle.Vertex(2).color.g
    db3 = Triangle.Vertex(0).color.b - Triangle.Vertex(2).color.b
   
    if dY3 <> 0 then
   
        Slope3 = dX3 / dY3
       
        RSlope3 = dR3 / dY3
        GSlope3 = dG3 / dY3
        BSlope3 = dB3 / dY3
       
    else
       
        Slope3 = 0
       
        RSlope3 = 0
        GSlope3 = 0
        BSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurR1=Triangle.Vertex(0).Color.r,_
                   CurG1=Triangle.Vertex(0).Color.g,_
                   CurB1=Triangle.Vertex(0).Color.b,_
                   CurR2=Triangle.Vertex(0).Color.r,_
                   CurG2=Triangle.Vertex(0).Color.g,_
                   CurB2=Triangle.Vertex(0).Color.b
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2)
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        CurR1 += RSlope1
        CurR2 += RSlope3
        CurG1 += GSlope1
        CurG2 += GSlope3
        CurB1 += BSlope1
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurX1 = Triangle.Vertex(1).pos2d.x
    CurR1 = Triangle.Vertex(1).color.r
    Curg1 = Triangle.Vertex(1).color.g
    Curb1 = Triangle.Vertex(1).color.b
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2)
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurR1 += RSlope2
        CurR2 += RSlope3
        CurG1 += GSlope2
        CurG2 += GSlope3
        CurB1 += BSlope2
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
End Sub

Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)
   
    dim as single tmpX,tmpY,TmpZ
   
    For i as integer = 1 to obj.NB_triangles
        For j as integer = 0 to 2
           
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tx) - obj.Triangle(i).Vertex(j).pos3d.z * sin(Tx)
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Tx) + obj.Triangle(i).Vertex(j).pos3d.y * sin(Tx)
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
           
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Ty) - obj.Triangle(i).Vertex(j).pos3d.x * sin(Ty)
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Ty) + obj.Triangle(i).Vertex(j).pos3d.z * sin(Ty)
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
           
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Tz) - obj.Triangle(i).Vertex(j).pos3d.y * sin(Tz)
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tz) + obj.Triangle(i).Vertex(j).pos3d.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
           
        Next
    Next
   
End sub










'' Test

Dim as objectT Pyramid

Pyramid.nb_triangles = 4
Pyramid.Center.z = 1
Pyramid.Center.y = 0
Pyramid.Center.x = 0

For i as integer = 1 to Pyramid.nb_triangles
    for j as integer = 0 to 2
       
        Read Pyramid.triangle(i).Vertex(j).pos3d.x, Pyramid.triangle(i).Vertex(j).pos3d.y,Pyramid.triangle(i).Vertex(j).pos3d.z,Pyramid.triangle(i).Vertex(j).Color.r,Pyramid.triangle(i).Vertex(j).Color.g,Pyramid.triangle(i).Vertex(j).Color.b
   
        Pyramid.triangle(i).Vertex(j).pos3d.x *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.y *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.z *= 4
    next
   
next

dim as single t=.02
dim as single tmpx,tmpz,tmpy
dim as integer j

Do
   
    Screenlock : cls
   
    Project_object Pyramid
   
    Sort_object_Z Pyramid
   
    For i as integer = 1 to Pyramid.nb_triangles
       
        Calc_Triangle_Normal Pyramid.triangle(i)
       
        if Pyramid.triangle(i).Normal.Z>0 then
            Gouraud_Fill Pyramid.triangle(i)
        end if
    Next
   
    Rotate(Pyramid,t/2,t/3,t/2)
   
    screenunlock : sleep 1,1
   
loop until multikey(&h01)


'' triangle 1
Data 0,30,0,250,50,50
Data 20,0,-20,50,250,50
Data -20,0,-20,50,50,250

'' triangle 2
Data 0,30,0,250,50,50
Data 0,0,20,250,50,250
Data 20,0,-20,50,250,50

'' triangle 3
Data 0,30,0,250,50,50
Data -20,0,-20,50,50,250
Data 0,0,20,250,50,250

'' triangle 4
Data 20,0,-20,50,250,50
Data 0,0,20,250,50,250
Data -20,0,-20,50,50,250


To finish, let me tell you that I red and saw a lot of awesome stuff here, especially ultra optimized stuff, so please excuse the fact that this code is more a proof-that-I-can-do-this than a real compo. The next steps are resolving some glitches, and then, coding a Gouraud shading routine with lights :D

++ !

Hezad *glad to be there*


ps : Code Updated (Not the zip though) : the pyramid was moving a bit too fast :p
Title: Re: Gouraud Filling + Presentation :D
Post by: mazemaker on August 21, 2008
this is great!

lights should be easy... you already have triangle normal calc. easy to normalise properly.

Title: Re: Gouraud Filling + Presentation :D
Post by: Rbz on August 21, 2008
 :hi: a board dude, nice work and keep it up
Title: Re: Gouraud Filling + Presentation :D
Post by: Hezad on August 22, 2008
Thanks :)

Quotelights should be easy...

I hope so ^^
Title: Re: Gouraud Filling + Presentation :D
Post by: Hezad on August 22, 2008
Update !

Here is the Gouraud Shading :)

Code :
#Define Norm(a) sqr(a.x*a.x + a.y*a.y + a.z*a.z)

Const MAX_TRIANGLES_PER_OBJECT = 500
Const MAX_OBJECTS = 10
Const xRes = 640, yRes = 480

Dim shared as integer ptr ScrPtr

Screenres xRes,yRes,32,2

ScrPtr = Screenptr

Type Vector
    as single x,y,z
End Type

Type ColorT
    as integer r,g,b
End Type

Type VertexT
   
    as Vector Pos3d
   
    as Vector Gouraud_Normal
   
    as Vector Pos2d
   
    as ColorT Color
   
End Type

Type TriangleT
   
    as Vector Normal
   
    as integer idZ
   
    as VertexT Vertex(2)
   
End Type

Type ObjectT
   
    as TriangleT Triangle(MAX_TRIANGLES_PER_OBJECT)
   
    as Vector Center
   
    as integer nb_triangles
   
End Type

Type LightT
   
    Pos3d as Vector
   
    Intensity as single
   
End Type

'' 3d Characteristics
Dim shared as single FOV = 256

Sub Sort_Object_Z(obj as ObjectT)
   
    For i as integer = 1 to obj.nb_Triangles
       
        obj.triangle(i).IdZ = i
        if i>1 then
            if obj.triangle(i).vertex(0).pos3d.z + obj.triangle(i).vertex(1).pos3d.z + obj.triangle(i).vertex(2).pos3d.z < obj.triangle(i-1).vertex(0).pos3d.z + obj.triangle(i-1).vertex(1).pos3d.z + obj.triangle(i-1).vertex(2).pos3d.z then

                Swap obj.triangle(i), obj.triangle(i-1)
                i=1
               
            end if
        end if
       
    Next
   
End Sub

Sub Calc_Triangle_2d_ZNormal(Tri as TriangleT)
   
    Tri.Normal.Z = (Tri.Vertex(1).pos2d.x - Tri.Vertex(0).pos2d.x) * (Tri.Vertex(0).pos2d.y - Tri.Vertex(2).pos2d.y) - (Tri.Vertex(1).pos2d.y - Tri.Vertex(0).pos2d.y) * (Tri.Vertex(0).pos2d.x - Tri.Vertex(2).pos2d.x)

End Sub

Sub Calc_Triangle_Normal(Tri as TriangleT)
   
    dim as single ax,bx,ay,by,az,bz,NNorm
   
    ax = Tri.Vertex(1).pos3d.x - Tri.Vertex(0).pos3d.x
    bx = Tri.Vertex(2).pos3d.x - Tri.Vertex(1).pos3d.x
    ay = Tri.Vertex(1).pos3d.y - Tri.Vertex(0).pos3d.y
    by = Tri.Vertex(2).pos3d.y - Tri.Vertex(1).pos3d.y
    az = Tri.Vertex(1).pos3d.z - Tri.Vertex(0).pos3d.z
    bz = Tri.Vertex(2).pos3d.z - Tri.Vertex(1).pos3d.z
   
    Tri.Normal.x = ay * bz - az * by
    Tri.Normal.y = az * bx - ax * bz
    Tri.Normal.z = ax * by - ay * bx
   
    '' Normalize (ouch, not optimized !) :
    NNorm = Norm(Tri.Normal)
   
    Tri.Normal.x /= NNorm
    Tri.Normal.y /= NNorm
    Tri.Normal.z /= NNorm
   
End Sub

Sub Calc_Object_Normals(obj as ObjectT)
   
    for i as integer = 1 to obj.nb_triangles
       
        Calc_Triangle_Normal(obj.Triangle(i))
   
    next
   
End Sub

Function SetLight(x as single,y as single, z as single,intens as single) as LightT
   
    Dim as LightT Tmp
   
    Tmp.pos3d.x = x
    Tmp.pos3d.y = y
    Tmp.pos3d.z = z
   
    Tmp.Intensity = intens
   
    Return Tmp
   
End Function

Sub Project_Object(Obj as ObjectT)
   
    if obj.nb_triangles = 0 then exit Sub
   
    For i as integer = 1 to obj.nb_triangles
       
        For j as integer = 0 to 2
           
            if obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z = FOV then obj.Triangle(i).Vertex(j).pos3d.z+=1
           
            obj.Triangle(i).Vertex(j).pos2d.x = xRes/2 + FOV * (obj.Center.x + obj.Triangle(i).Vertex(j).pos3d.x) / (FOV - (obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
            obj.Triangle(i).Vertex(j).pos2d.y = yRes/2 - FOV * (obj.Center.y + obj.Triangle(i).Vertex(j).pos3d.y) / (FOV - (obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
       
        Next
       
    Next
   
End Sub

Sub GLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal r1 as integer,ByVal g1 as integer,ByVal b1 as integer,ByVal r2 as integer,ByVal g2 as integer,ByVal b2 as integer)
   
    dim as single dR,dG,dB
    dim as single R,g,b
   
    dim as integer itx1,itx2
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
   
    if x1<x2 then
        itx1 = x1
        itx2 = x2
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        itx1 = x2
        itx2 = x1
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    dR = (sr2 - sr1)/(itx2 - itx1)
    dG = (sg2 - sg1)/(itx2 - itx1)
    dB = (sb2 - sb1)/(itx2 - itx1)
   
    r = sr1
    g = sg1
    b = sb1
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
            ScrPtr[y*xRes+i] = rgb(r,g,b)
        end if

        r += dR
        g += dG
        b += dB
       
    Next
   
End Sub

Sub Gouraud_Fill(ByVal Triangle as TriangleT)
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1).Pos2d.y,Triangle.Vertex(0).Pos2d.y
        swap Triangle.Vertex(1).Pos2d.x,Triangle.Vertex(0).Pos2d.x
           
        swap Triangle.Vertex(1).Color.r,Triangle.Vertex(0).Color.r
        swap Triangle.Vertex(1).Color.g,Triangle.Vertex(0).Color.g
        swap Triangle.Vertex(1).Color.b,Triangle.Vertex(0).Color.b
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2).Pos2d.y,Triangle.Vertex(0).Pos2d.y
        swap Triangle.Vertex(2).Pos2d.x,Triangle.Vertex(0).Pos2d.x
           
        swap Triangle.Vertex(2).Color.r,Triangle.Vertex(0).Color.r
        swap Triangle.Vertex(2).Color.g,Triangle.Vertex(0).Color.g
        swap Triangle.Vertex(2).Color.b,Triangle.Vertex(0).Color.b
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2).Pos2d.y,Triangle.Vertex(1).Pos2d.y
        swap Triangle.Vertex(2).Pos2d.x,Triangle.Vertex(1).Pos2d.x
           
        swap Triangle.Vertex(2).Color.r,Triangle.Vertex(1).Color.r
        swap Triangle.Vertex(2).Color.g,Triangle.Vertex(1).Color.g
        swap Triangle.Vertex(2).Color.b,Triangle.Vertex(1).Color.b
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as integer dr1,dg1,db1,dr2,dg2,db2,dr3,dg3,db3
    dim as single Slope1,Slope2,Slope3
    dim as single RSlope1, GSlope1,BSlope1
    dim as single RSlope2, GSlope2,BSlope2
    dim as single RSlope3, GSlope3,BSlope3
   
   
    '' interpolate 0 to 1 (pos + color)
       
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dr1 = Triangle.Vertex(1).color.r - Triangle.Vertex(0).color.r
    dg1 = Triangle.Vertex(1).color.g - Triangle.Vertex(0).color.g
    db1 = Triangle.Vertex(1).color.b - Triangle.Vertex(0).color.b
   
    if dY1 <> 0 then
   
        Slope1 = dX1 / dY1
       
        RSlope1 = dR1 / dY1
        GSlope1 = dG1 / dY1
        BSlope1 = dB1 / dY1
       
    else
       
        Slope1 = 0
       
        RSlope1 = 0
        GSlope1 = 0
        BSlope1 = 0
       
    End if
   
   
    '' interpolate 1 to 2
       
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dr2 = Triangle.Vertex(2).color.r - Triangle.Vertex(1).color.r
    dg2 = Triangle.Vertex(2).color.g - Triangle.Vertex(1).color.g
    db2 = Triangle.Vertex(2).color.b - Triangle.Vertex(1).color.b
   
    if dY2 <> 0 then
   
        Slope2 = dX2 / dY2
       
        RSlope2 = dR2 / dY2
        GSlope2 = dG2 / dY2
        BSlope2 = dB2 / dY2
       
    else
       
        Slope2 = 0
       
        RSlope2 = 0
        GSlope2 = 0
        BSlope2 = 0
       
    End if
   
   
    '' interpolate 0 to 2
       
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dr3 = Triangle.Vertex(0).color.r - Triangle.Vertex(2).color.r
    dg3 = Triangle.Vertex(0).color.g - Triangle.Vertex(2).color.g
    db3 = Triangle.Vertex(0).color.b - Triangle.Vertex(2).color.b
   
    if dY3 <> 0 then
   
        Slope3 = dX3 / dY3
       
        RSlope3 = dR3 / dY3
        GSlope3 = dG3 / dY3
        BSlope3 = dB3 / dY3
       
    else
       
        Slope3 = 0
       
        RSlope3 = 0
        GSlope3 = 0
        BSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurR1=Triangle.Vertex(0).Color.r,_
                   CurG1=Triangle.Vertex(0).Color.g,_
                   CurB1=Triangle.Vertex(0).Color.b,_
                   CurR2=Triangle.Vertex(0).Color.r,_
                   CurG2=Triangle.Vertex(0).Color.g,_
                   CurB2=Triangle.Vertex(0).Color.b
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2)
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        CurR1 += RSlope1
        CurR2 += RSlope3
        CurG1 += GSlope1
        CurG2 += GSlope3
        CurB1 += BSlope1
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurX1 = Triangle.Vertex(1).pos2d.x
    CurR1 = Triangle.Vertex(1).color.r
    Curg1 = Triangle.Vertex(1).color.g
    Curb1 = Triangle.Vertex(1).color.b
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2)
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurR1 += RSlope2
        CurR2 += RSlope3
        CurG1 += GSlope2
        CurG2 += GSlope3
        CurB1 += BSlope2
        CurB2 += BSlope3
       
    Next
   
End Sub

#Define SamePnt(a,b) iif (a.pos3d.x = b.pos3d.x and a.pos3d.y = b.pos3d.y and a.pos3d.z = b.pos3d.z,1,0)
       
Sub Gouraud_Shader(obj_ as objectT,L_ as LightT)
   
    Dim as single Dot1,Dot2,Dot3
   
    Dim as objectT obj = obj_
    Dim as LightT L = L_
   
    dim as single tmpXNorm,TmpYNorm,TmpZNorm
    dim as integer tmpNBfaces
   
    '' "Vertex normals"
    For k as integer = 1 to obj.nb_triangles
        for i as integer = 0 to 2
            TmpXNorm = 0
            TmpYNorm = 0
            TmpZNorm = 0
            tmpNBfaces = 0
       
            for j as integer = 1 to obj.nb_triangles
                if SamePnt(obj.triangle(k).Vertex(0),obj.triangle(j).Vertex(i)) or _
                   SamePnt(obj.triangle(k).Vertex(1),obj.triangle(j).Vertex(i)) or _
                   SamePnt(obj.triangle(k).Vertex(2),obj.triangle(j).Vertex(i)) then
                   
                    TmpXNorm += obj.triangle(j).Normal.x
                    TmpYNorm += obj.triangle(j).Normal.y
                    TmpZNorm += obj.triangle(j).Normal.z
   
                    tmpNBfaces += 1
               
                end if
            next
       
            TmpXNorm /= TmpNBfaces
            TmpYNorm /= TmpNBfaces
            TmpZNorm /= TmpNBfaces
   
            obj.triangle(k).Vertex(i).Gouraud_Normal.x = TmpXNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.y = TmpyNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.z = TmpzNorm
       
        next
   
        Dot1 = (obj.triangle(k).Vertex(0).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.z * L.pos3d.z)
        if Dot1<0 then Dot1 = 0
        if Dot1>1 then Dot1 = 1
   
        Dot2 = (obj.triangle(k).Vertex(1).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.z * L.pos3d.z)
        if Dot2<0 then Dot2 = 0
        if Dot2>1 then Dot2 = 1
       
        Dot3 = (obj.triangle(k).Vertex(2).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.z * L.pos3d.z)
        if Dot3<0 then Dot3 = 0
        if Dot3>1 then Dot3 = 1
       
        '' apply new color
        obj_.triangle(k).Vertex(0).Color.r = 255*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.g = 255*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.b = 255*L.Intensity*Dot1
       
        obj_.triangle(k).Vertex(1).Color.r = 255*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.g = 255*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.b = 255*L.Intensity*Dot2
       
        obj_.triangle(k).Vertex(2).Color.r = 255*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.g = 255*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.b = 255*L.Intensity*Dot3
    next
   
   
   
End sub

Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)
   
    dim as single tmpX,tmpY,TmpZ
   
    For i as integer = 1 to obj.NB_triangles
        For j as integer = 0 to 2
           
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tx) - obj.Triangle(i).Vertex(j).pos3d.z * sin(Tx)
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Tx) + obj.Triangle(i).Vertex(j).pos3d.y * sin(Tx)
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
           
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Ty) - obj.Triangle(i).Vertex(j).pos3d.x * sin(Ty)
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Ty) + obj.Triangle(i).Vertex(j).pos3d.z * sin(Ty)
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
           
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Tz) - obj.Triangle(i).Vertex(j).pos3d.y * sin(Tz)
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tz) + obj.Triangle(i).Vertex(j).pos3d.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
           
        Next
    Next
   
End sub










'' Test

'' Loading objects

Dim as ObjectT Pyramid

Pyramid.nb_triangles = 4
Pyramid.Center.z = 1
Pyramid.Center.y = 0
Pyramid.Center.x = 0

For i as integer = 1 to Pyramid.nb_triangles
    for j as integer = 0 to 2
       
        Read Pyramid.triangle(i).Vertex(j).pos3d.x, Pyramid.triangle(i).Vertex(j).pos3d.y,Pyramid.triangle(i).Vertex(j).pos3d.z,Pyramid.triangle(i).Vertex(j).Color.r,Pyramid.triangle(i).Vertex(j).Color.g,Pyramid.triangle(i).Vertex(j).Color.b
   
        Pyramid.triangle(i).Vertex(j).pos3d.x *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.y *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.z *= 4
    next
   
next


dim as single t=.02

Dim as LightT Light

Light = SetLight(1,2,0,1)

Do
   
    Screenlock : cls
   
    Project_object Pyramid
   
    Sort_object_Z Pyramid
   
    Calc_object_normals Pyramid
   
    Gouraud_Shader Pyramid, Light
   
    For i as integer = 1 to Pyramid.nb_triangles
       
        if Pyramid.triangle(i).Normal.Z>0 then
            Gouraud_Fill Pyramid.triangle(i)
        end if
    Next
   
    Rotate(Pyramid,t/2,t/3,t/2)
   
    screenunlock : sleep 1,1
   
loop until multikey(&h01)


'' PYRAMID
'' triangle 1
Data 0,30,0,250,50,50
Data 20,0,-20,50,250,50
Data -20,0,-20,50,50,250

'' triangle 2
Data 0,30,0,250,50,50
Data 0,0,20,250,50,250
Data 20,0,-20,50,250,50

'' triangle 3
Data 0,30,0,250,50,50
Data -20,0,-20,50,50,250
Data 0,0,20,250,50,250

'' triangle 4
Data 20,0,-20,50,250,50
Data 0,0,20,250,50,250
Data -20,0,-20,50,50,250


*Just found how to attach files*

Attached : Screen + Zip(Code+Exe)

If you prefer I to upload it on my FTP instead of here, please tell me :)
Title: Re: Gouraud Filling (Update : Gouraud Shading)
Post by: Clyde on August 22, 2008
Welldone mate and :hi to the forums.
Title: Re: Gouraud Filling (Update : Gouraud Shading)
Post by: Shockwave on August 22, 2008
Hi Hezad :)

It's really good to see someone else having a go at software rendering, hats off to you.

Welcome to the forum.

You say that the code is not too optimised, it looks ok to me, nicely set out too. As far as the normals calcs go, you could pre-compute these once at the beginning and just rotate them with the rest of the object, but I really don't think you should lose any sleep over them with an object like a pyramid!

You're only a tiny way away from linear texture mapping with this too. All you'd need to do is have a texture map in memory and where you are interpolating the colour accross the object from the edges, just apply the same method to some UV (texture x and texture y) co-ordinates.

I am sure that you are going to have a lot of fun with your learning, you're already doing fantastic!

So have some good Karma from me and please enjoy the site.

Ps. 2 Things, you are free to attach your files to the posts, don't worry :) Your written English is better than you give yourself credit for.
Title: Re: Gouraud Filling (Update : Gouraud Shading)
Post by: benny! on August 22, 2008
 :hi: Hezad.
Title: Re: Gouraud Filling (Update : Gouraud Shading)
Post by: Hezad on August 22, 2008
Thanks everyone :) And thanks for the welcome :D

QuoteYou say that the code is not too optimised, it looks ok to me, nicely set out too. As far as the normals calcs go, you could pre-compute these once at the beginning and just rotate them with the rest of the object, but I really don't think you should lose any sleep over them with an object like a pyramid!

hey cool ! I was wondering how I could precalculate the normals since I'd need to update them with the rotation but yeah, I just need to rotate them with the object !!  :inspired: lol I didn't thought about it ! Thanks for this advice !

QuoteYou're only a tiny way away from linear texture mapping with this too. All you'd need to do is have a texture map in memory and where you are interpolating the colour accross the object from the edges, just apply the same method to some UV (texture x and texture y) co-ordinates.

lol in fact, I started directly with texture mapping tries but that was catastrophic ^^ So I decided to do Gouraud filling + shading before to make sure I understand. Now, I'll try texture mapping :) I'll try to make a cube with a moving plasma mapped on it :D And I already have some ideas to merge texture mapping and gouraud shading (but will I succeed in this objective?Iit's another question :P)

QuoteI am sure that you are going to have a lot of fun with your learning, you're already doing fantastic!

So have some good Karma from me and please enjoy the site.

I'm pretty sure too concerning the fun :P And thanks a lot for the karma :)

Oh and I already enjoyed the board since I saw awesome productions and/or snippets here and I'm sure I'll continue to enjoy it :D
Title: Re: Gouraud Filling (Update : Gouraud Shading)
Post by: Hezad on August 23, 2008
Update !

The renderer now supports Z-Buffering :D and I ameliorated a bit the lighting routine

Code :


''  =======================---. 3D Stuff .---=======================
''
''      Gouraud Shading + Z-Buffer rendering
''
''                      Code : Hezad  [   Hezad0 )at( Gmail.com    ]
''
''
''  Thanks to Relsoft for his awesome tutorial :
''                 [  http://www.phatcode.net/articles.php?id=214  ]
''
''
''  ================================================================


#Define Norm(a) sqr(a.x*a.x + a.y*a.y + a.z*a.z)

Const MAX_TRIANGLES_PER_OBJECT = 500
Const MAX_OBJECTS = 10
Const xRes = 640, yRes = 480

Const SCREEN_MEM = xRes * yRes

Const BGND_COLOR = rgb(50,50,70)

Const MAX_Z = 1000

Dim shared as integer ptr ScrPtr
ReDim shared as integer ZBuffer(SCREEN_MEM)

Screenres xRes,yRes,32,2

ScrPtr = Screenptr

Type Vector
    as single x,y,z
End Type

Type ColorT
    as integer r,g,b
End Type

Type VertexT
   
    as Vector Pos3d
   
    as Vector Gouraud_Normal
   
    as Vector Pos2d
   
    as ColorT Color
   
    as ColorT OriginalColor
   
End Type

Type TriangleT
   
    as Vector Normal
   
    as integer idZ
   
    as VertexT Vertex(2)
   
    as Vector Parent_Center
   
End Type

Type ObjectT
   
    as TriangleT Triangle(MAX_TRIANGLES_PER_OBJECT)
   
    as Vector Center
   
    as integer nb_triangles
   
End Type

Type LightT
   
    Pos3d as Vector
   
    Intensity as single
   
End Type

Type CameraT
   
    Pos as Vector
   
    Angle as Vector
   
End Type

   
Dim shared as single FOV = 256
Dim shared as CameraT Camera

Sub Set_Camera(x as integer, y as integer, z as integer)
   
    Camera.Pos.x = x
    Camera.Pos.y = y
    Camera.Pos.z = z
   
End Sub

Sub Sort_Object_Z(obj as ObjectT)
   
    For i as integer = 1 to obj.nb_Triangles
       
        obj.triangle(i).IdZ = i
        if i>1 then
            if obj.triangle(i).vertex(0).pos3d.z + obj.triangle(i).vertex(1).pos3d.z + obj.triangle(i).vertex(2).pos3d.z < obj.triangle(i-1).vertex(0).pos3d.z + obj.triangle(i-1).vertex(1).pos3d.z + obj.triangle(i-1).vertex(2).pos3d.z then

                Swap obj.triangle(i), obj.triangle(i-1)
                i=1
               
            end if
        end if
       
    Next
   
End Sub

Sub Calc_Triangle_2d_ZNormal(Tri as TriangleT)
   
    Tri.Normal.Z = (Tri.Vertex(1).pos2d.x - Tri.Vertex(0).pos2d.x) * (Tri.Vertex(0).pos2d.y - Tri.Vertex(2).pos2d.y) - (Tri.Vertex(1).pos2d.y - Tri.Vertex(0).pos2d.y) * (Tri.Vertex(0).pos2d.x - Tri.Vertex(2).pos2d.x)

End Sub

Sub Calc_Triangle_Normal(Tri as TriangleT)
   
    dim as single ax,bx,ay,by,az,bz,NNorm
   
    ax = Tri.Vertex(1).pos3d.x - Tri.Vertex(0).pos3d.x
    bx = Tri.Vertex(2).pos3d.x - Tri.Vertex(1).pos3d.x
    ay = Tri.Vertex(1).pos3d.y - Tri.Vertex(0).pos3d.y
    by = Tri.Vertex(2).pos3d.y - Tri.Vertex(1).pos3d.y
    az = Tri.Vertex(1).pos3d.z - Tri.Vertex(0).pos3d.z
    bz = Tri.Vertex(2).pos3d.z - Tri.Vertex(1).pos3d.z
   
    Tri.Normal.x = ay * bz - az * by
    Tri.Normal.y = az * bx - ax * bz
    Tri.Normal.z = ax * by - ay * bx
   
    '' Normalize
    NNorm = Norm(Tri.Normal)
   
    Tri.Normal.x /= NNorm
    Tri.Normal.y /= NNorm
    Tri.Normal.z /= NNorm
   
End Sub

#Define SamePnt(a,b) iif (a.pos3d.x = b.pos3d.x and a.pos3d.y = b.pos3d.y and a.pos3d.z = b.pos3d.z,1,0)

Sub PreCalc_Object_Normals(obj as ObjectT)
   
    dim as single tmpXNorm,TmpYNorm,TmpZNorm
    dim as integer tmpNBfaces
   
    for k as integer = 1 to obj.nb_triangles
       
        '' face normal
        Calc_Triangle_Normal(obj.Triangle(k))
   
        '' "Vertex normals"
        for i as integer = 0 to 2
            TmpXNorm = 0
            TmpYNorm = 0
            TmpZNorm = 0
            tmpNBfaces = 0
       
            for j as integer = 1 to obj.nb_triangles
                if SamePnt(obj.triangle(k).Vertex(0),obj.triangle(j).Vertex(i)) or _
                   SamePnt(obj.triangle(k).Vertex(1),obj.triangle(j).Vertex(i)) or _
                   SamePnt(obj.triangle(k).Vertex(2),obj.triangle(j).Vertex(i)) then
                   
                    TmpXNorm += obj.triangle(j).Normal.x
                    TmpYNorm += obj.triangle(j).Normal.y
                    TmpZNorm += obj.triangle(j).Normal.z
   
                    tmpNBfaces += 1
               
                end if
            next
       
            TmpXNorm /= TmpNBfaces
            TmpYNorm /= TmpNBfaces
            TmpZNorm /= TmpNBfaces
   
            obj.triangle(k).Vertex(i).Gouraud_Normal.x = TmpXNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.y = TmpyNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.z = TmpzNorm
       
        next
       
    next
   
End Sub

Function SetLight(x as single,y as single, z as single,intens as single) as LightT
   
    Dim as LightT Tmp
   
    Tmp.pos3d.x = x
    Tmp.pos3d.y = y
    Tmp.pos3d.z = z
   
    Tmp.Intensity = intens
   
    Return Tmp
   
End Function

Declare Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)

Sub Project_Object(Obj as ObjectT)
   
    dim as single aX,aY,aZ
   
    if obj.nb_triangles = 0 then exit Sub
   
    For i as integer = 1 to obj.nb_triangles
       
        obj.Triangle(i).Parent_Center = obj.Center
       
        For j as integer = 0 to 2
           
            if obj.Triangle(i).Vertex(j).pos3d.z = Camera.pos.z - obj.Center.z + FOV then obj.Triangle(i).Vertex(j).pos3d.z+=.0001
           
            obj.Triangle(i).Vertex(j).pos2d.x = xRes/2 + FOV*(-Camera.pos.x + obj.Center.x + obj.Triangle(i).Vertex(j).pos3d.x) / (FOV - (-Camera.pos.z + obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
            obj.Triangle(i).Vertex(j).pos2d.y = yRes/2 - FOV*(-Camera.pos.y + obj.Center.y + obj.Triangle(i).Vertex(j).pos3d.y) / (FOV - (-Camera.pos.z + obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
           
        Next
       
    Next
   
End Sub

Sub GLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal r1 as integer,ByVal g1 as integer,ByVal b1 as integer,ByVal r2 as integer,ByVal g2 as integer,ByVal b2 as integer,ByVal z1 as integer,ByVal z2 as integer)
   
    dim as single dR,dG,dB, dZ
    dim as single R,g,b
   
    dim as integer itx1,itx2,zz1,zz2
    Dim as single CurZ
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
   
    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        itx1 = x1
        itx2 = x2
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        zz1 = z2
        zz2 = z1
       
        itx1 = x2
        itx2 = x1
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    dR = (sr2 - sr1)/(itx2 - itx1)
    dG = (sg2 - sg1)/(itx2 - itx1)
    dB = (sb2 - sb1)/(itx2 - itx1)
   
    dZ = (zz2 - zz1)/(itx2 - itx1)
   
    r = sr1
    g = sg1
    b = sb1
   
    CurZ = zz1
   
    dim as integer it
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
           
            it = y*xRes + i
           
            If (FOV - (-Camera.pos.z + CurZ))>0 then
               
                If Curz > ZBuffer(it) then
                   
                    ZBuffer(it) = CurZ
                   
                    ScrPtr[it] = rgb(r,g,b)
                   
                    '' UnComment to show the ZBuffer Gfx :D
                    'dim as integer zBufferCol = 255*cast(single,(abs(ZBuffer(it))/MAX_Z))
                    'ScrPtr[it] = rgb(zBufferCol,zBufferCol,zBufferCol)
                   
                End if
           
            end if
       
        end if
       
        curZ += dZ
       
        r += dR
        g += dG
        b += dB
       
    Next
   
End Sub

Sub Gouraud_Fill(ByVal Triangle as TriangleT)
   
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1),Triangle.Vertex(0)
    end if
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(0)
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(1)
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as integer dr1,dg1,db1,dr2,dg2,db2,dr3,dg3,db3
    dim as single Slope1,Slope2,Slope3
    dim as single RSlope1, GSlope1,BSlope1
    dim as single RSlope2, GSlope2,BSlope2
    dim as single RSlope3, GSlope3,BSlope3
   
    Dim as integer dz1,dz2,dz3
    Dim as single zSlope1,zSlope2,zSlope3
   
    '' interpolate 0 to 1 (pos + color)
   
    dZ1 = Triangle.Vertex(1).pos3d.z - Triangle.Vertex(0).pos3d.z
   
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dr1 = Triangle.Vertex(1).color.r - Triangle.Vertex(0).color.r
    dg1 = Triangle.Vertex(1).color.g - Triangle.Vertex(0).color.g
    db1 = Triangle.Vertex(1).color.b - Triangle.Vertex(0).color.b
   
    if dY1 <> 0 then
       
        zSlope1 = dZ1 / dY1
       
        Slope1 = dX1 / dY1
       
        RSlope1 = dR1 / dY1
        GSlope1 = dG1 / dY1
        BSlope1 = dB1 / dY1
       
    else
       
        zSlope1 = 0
       
        Slope1 = 0
       
        RSlope1 = 0
        GSlope1 = 0
        BSlope1 = 0
       
    End if
   
   
    '' interpolate 1 to 2
   
    dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z
   
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dr2 = Triangle.Vertex(2).color.r - Triangle.Vertex(1).color.r
    dg2 = Triangle.Vertex(2).color.g - Triangle.Vertex(1).color.g
    db2 = Triangle.Vertex(2).color.b - Triangle.Vertex(1).color.b
   
    if dY2 <> 0 then
       
        zSlope2 = dZ2 / dY2
       
        Slope2 = dX2 / dY2
       
        RSlope2 = dR2 / dY2
        GSlope2 = dG2 / dY2
        BSlope2 = dB2 / dY2
       
    else
       
        zSlope2 = 0
       
        Slope2 = 0
       
        RSlope2 = 0
        GSlope2 = 0
        BSlope2 = 0
       
    End if
   
   
    '' interpolate 0 to 2
   
    dZ3 = Triangle.Vertex(0).pos3d.z - Triangle.Vertex(2).pos3d.z
   
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dr3 = Triangle.Vertex(0).color.r - Triangle.Vertex(2).color.r
    dg3 = Triangle.Vertex(0).color.g - Triangle.Vertex(2).color.g
    db3 = Triangle.Vertex(0).color.b - Triangle.Vertex(2).color.b
   
    if dY3 <> 0 then
       
        zSlope3 = dZ3 /dY3
       
        Slope3 = dX3 / dY3
       
        RSlope3 = dR3 / dY3
        GSlope3 = dG3 / dY3
        BSlope3 = dB3 / dY3
       
    else
       
        zSlope3 = 0
       
        Slope3 = 0
       
        RSlope3 = 0
        GSlope3 = 0
        BSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurR1=Triangle.Vertex(0).Color.r,_
                   CurG1=Triangle.Vertex(0).Color.g,_
                   CurB1=Triangle.Vertex(0).Color.b,_
                   CurR2=Triangle.Vertex(0).Color.r,_
                   CurG2=Triangle.Vertex(0).Color.g,_
                   CurB2=Triangle.Vertex(0).Color.b
   
    dim as single CurZ1=Triangle.Vertex(0).pos3d.z, CurZ2=Triangle.Vertex(0).pos3d.z
   
    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2, CurZ1, CurZ2)
       
        CurZ1 += zSlope1
        CurZ2 += zSlope3
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        CurR1 += RSlope1
        CurR2 += RSlope3
        CurG1 += GSlope1
        CurG2 += GSlope3
        CurB1 += BSlope1
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurZ1 = Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z
   
    CurX1 = Triangle.Vertex(1).pos2d.x
    CurR1 = Triangle.Vertex(1).color.r
    Curg1 = Triangle.Vertex(1).color.g
    Curb1 = Triangle.Vertex(1).color.b
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2, CurZ1, CurZ2)
       
        CurZ1 += zSlope2
        CurZ2 += zSlope3
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurR1 += RSlope2
        CurR2 += RSlope3
        CurG1 += GSlope2
        CurG2 += GSlope3
        CurB1 += BSlope2
        CurB2 += BSlope3
       
    Next
   
End Sub
       
Sub Calc_Gouraud_Shader(obj_ as objectT,L_ as LightT)
   
    Dim as single Dot1,Dot2,Dot3
   
    Dim as objectT obj = obj_
    Dim as LightT L = L_
   
        '' dot products
   
    For k as integer = 1 to obj.nb_triangles
        Dot1 = (obj.triangle(k).Vertex(0).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.z * L.pos3d.z)
        if Dot1<0 then Dot1 = 0
        if Dot1>1 then Dot1 = 1
   
        Dot2 = (obj.triangle(k).Vertex(1).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.z * L.pos3d.z)
        if Dot2<0 then Dot2 = 0
        if Dot2>1 then Dot2 = 1
       
        Dot3 = (obj.triangle(k).Vertex(2).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.z * L.pos3d.z)
        if Dot3<0 then Dot3 = 0
        if Dot3>1 then Dot3 = 1
       
        '' apply new color
        obj_.triangle(k).Vertex(0).Color.r = obj_.triangle(k).Vertex(0).OriginalColor.r*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.g = obj_.triangle(k).Vertex(0).OriginalColor.g*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.b = obj_.triangle(k).Vertex(0).OriginalColor.b*L.Intensity*Dot1
       
        obj_.triangle(k).Vertex(1).Color.r = obj_.triangle(k).Vertex(1).OriginalColor.r*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.g = obj_.triangle(k).Vertex(1).OriginalColor.g*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.b = obj_.triangle(k).Vertex(1).OriginalColor.b*L.Intensity*Dot2
       
        obj_.triangle(k).Vertex(2).Color.r = obj_.triangle(k).Vertex(2).OriginalColor.r*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.g = obj_.triangle(k).Vertex(2).OriginalColor.g*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.b = obj_.triangle(k).Vertex(2).OriginalColor.b*L.Intensity*Dot3
    next
   
   
   
End sub

Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)
   
    dim as single tmpX,tmpY,TmpZ,normX,normY,normZ
    dim as single VnormX,VnormY,VnormZ
   
    For i as integer = 1 to obj.NB_triangles
        For j as integer = 0 to 2
           
            '' Points
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tx) - obj.Triangle(i).Vertex(j).pos3d.z * sin(Tx)
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Tx) + obj.Triangle(i).Vertex(j).pos3d.y * sin(Tx)
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
           
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Ty) - obj.Triangle(i).Vertex(j).pos3d.x * sin(Ty)
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Ty) + obj.Triangle(i).Vertex(j).pos3d.z * sin(Ty)
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
           
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Tz) - obj.Triangle(i).Vertex(j).pos3d.y * sin(Tz)
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tz) + obj.Triangle(i).Vertex(j).pos3d.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
                   
       
            '' Vertex normals
            VnormY = obj.Triangle(i).Vertex(j).Gouraud_Normal.y * cos(Tx) - obj.Triangle(i).Vertex(j).Gouraud_Normal.z * sin(Tx)
            VnormZ = obj.Triangle(i).Vertex(j).Gouraud_Normal.z * cos(Tx) + obj.Triangle(i).Vertex(j).Gouraud_Normal.y * sin(Tx)
            obj.Triangle(i).Vertex(j).Gouraud_Normal.y = VnormY
            obj.Triangle(i).Vertex(j).Gouraud_Normal.z = VnormZ
           
            VnormZ = obj.Triangle(i).Vertex(j).Gouraud_Normal.z * cos(Ty) - obj.Triangle(i).Vertex(j).Gouraud_Normal.x * sin(Ty)
            VnormX = obj.Triangle(i).Vertex(j).Gouraud_Normal.x * cos(Ty) + obj.Triangle(i).Vertex(j).Gouraud_Normal.z * sin(Ty)
            obj.Triangle(i).Vertex(j).Gouraud_Normal.x = VnormX

            VnormX = obj.Triangle(i).Vertex(j).Gouraud_Normal.x * cos(Tz) - obj.Triangle(i).Vertex(j).Gouraud_Normal.y * sin(Tz)
            VnormY = obj.Triangle(i).Vertex(j).Gouraud_Normal.y * cos(Tz) + obj.Triangle(i).Vertex(j).Gouraud_Normal.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).Gouraud_Normal.x = VnormX
            obj.Triangle(i).Vertex(j).Gouraud_Normal.y = VnormY
            obj.Triangle(i).Vertex(j).Gouraud_Normal.z = VnormZ
        Next
       
        '' normals
        NormY = obj.Triangle(i).Normal.y * cos(Tx) - obj.Triangle(i).Normal.z * sin(Tx)
        NormZ = obj.Triangle(i).Normal.z * cos(Tx) + obj.Triangle(i).Normal.y * sin(Tx)
        obj.Triangle(i).Normal.y = NormY
        obj.Triangle(i).Normal.z = NormZ
       
        NormZ = obj.Triangle(i).Normal.z * cos(Ty) - obj.Triangle(i).Normal.x * sin(Ty)
        NormX = obj.Triangle(i).Normal.x * cos(Ty) + obj.Triangle(i).Normal.z * sin(Ty)
        obj.Triangle(i).Normal.x = NormX
       
        NormX = obj.Triangle(i).Normal.x * cos(Tz) - obj.Triangle(i).Normal.y * sin(Tz)
        NormY = obj.Triangle(i).Normal.y * cos(Tz) + obj.Triangle(i).Normal.x * sin(Tz)
           
        obj.Triangle(i).Normal.x = NormX
        obj.Triangle(i).Normal.y = NormY
        obj.Triangle(i).Normal.z = NormZ
       
    Next
   
End sub

#Define Cls_zBuffer For klm as integer =0 to SCREEN_MEM-1 : Zbuffer(klm) = -MAX_Z : next







'' Test

'' Loading objects

Dim as ObjectT Pyramid, Pyramid2

Pyramid.nb_triangles = 4
Pyramid.Center.z = 0
Pyramid.Center.y = 0
Pyramid.Center.x = -50

Pyramid2.Nb_triangles = 4

For i as integer = 1 to Pyramid.nb_triangles
    for j as integer = 0 to 2
       
        Read Pyramid.triangle(i).Vertex(j).pos3d.x, Pyramid.triangle(i).Vertex(j).pos3d.y,Pyramid.triangle(i).Vertex(j).pos3d.z,Pyramid.triangle(i).Vertex(j).OriginalColor.r,Pyramid.triangle(i).Vertex(j).OriginalColor.g,Pyramid.triangle(i).Vertex(j).OriginalColor.b
   
        Pyramid.triangle(i).Vertex(j).pos3d.x *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.y *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.z *= 4
       
    next
   
next

dim as single t=.02

Dim as LightT Light

Light = SetLight(-.9,1,.8,1)

PreCalc_object_normals Pyramid

Set_Camera 0,0,0

Pyramid2 = Pyramid

Pyramid2.Center.x = 50

Do
   
    Screenlock : cls
   
    Paint(0,0),BGND_COLOR
   
    CLS_zBuffer
   
    Project_object Pyramid
    Project_object Pyramid2
   
    '' No Need to sort since there is Z-Buffer now !
    'Sort_object_Z Pyramid
   
    Calc_Gouraud_Shader Pyramid, Light
    Calc_Gouraud_Shader Pyramid2, Light
   
    For i as integer = 1 to Pyramid.nb_triangles
       
        if Pyramid.triangle(i).Normal.Z>0 then
            Gouraud_Fill Pyramid.triangle(i)
        end if
       
        if Pyramid2.triangle(i).Normal.Z>0 then
            Gouraud_Fill Pyramid2.triangle(i)
        end if
       
    Next
   
    Rotate(Pyramid,t,t/2,t/2)
   
    Rotate(Pyramid2,-t,t/4,-t/2)
   
    screenunlock : sleep 1,1
   
loop until multikey(&h01)


'' PYRAMID
'' triangle 1
Data 0,30,0,250,50,50
Data 20,0,-20,250,50,50
Data -20,0,-20,250,50,50

'' triangle 2
Data 0,30,0,250,50,250
Data 0,0,20,250,50,250
Data 20,0,-20,250,50,250

'' triangle 3
Data 0,30,0,50,50,250
Data -20,0,-20,50,50,250
Data 0,0,20,50,50,250

'' triangle 4
Data 20,0,-20,50,250,50
Data 0,0,20,50,250,50
Data -20,0,-20,50,250,50


Attached : Screenshot + Rar(src + exe)
Title: Re: Gouraud Filling (Update : Z-Buffer)
Post by: Jim on August 23, 2008
Looks good now.

One thing with the normals is that once you have added all the face normals together you need to re-normalise the result, instead of dividing by the number of faces added.

A small optimisation too.  You do something like

dx = x2-x1
dr = (r2-r1) / dx
dg = (g2-g1) / dx
drb= (b2-b1) / dx

It would be quicker to do

dx = 1.0/(x2-x1)
dr = (r2-r1) * dx
dg = (g2-g1) * dx
drb= (b2-b1) * dx

But while you're learning we might be better off not talking about optimisation just now and more about algorithms.

Jim
Title: Re: Gouraud Filling (Update : Z-Buffer)
Post by: Shockwave on August 23, 2008
Nice Z-Buffering :)

Now that you have this capability in your renderer you could easily make some really complicated objects with non-convex (overlapping) faces and since you worked out how to interpolate the colours and the Z co-ordinates, texture co-ordinates should be easy for you to do now :)

Looking forward to seeing your first Star wars X-Wing fighter :D
Title: Re: Gouraud Filling (Update : Z-Buffer)
Post by: Hezad on August 23, 2008
Thanks for your replies :)

Jim :

QuoteOne thing with the normals is that once you have added all the face normals together you need to re-normalise the result, instead of dividing by the number of faces added.
You must be speaking of the Calc_triangle_2d_normal() sub ? 'cause I don't use it :) I have another sub I use called Calc_triangle_normal() so the first is kinda deprecated :p I should get it off the code.
If you speak about something else, please tell me :)

QuoteBut while you're learning we might be better off not talking about optimisation just now and more about algorithms.
Small optimization tips like this one are perfect :) It permits me to learn some basic optimization stuff without recoding everything :D
So thanks.


Shockwave :

QuoteNow that you have this capability in your renderer you could easily make some really complicated objects with non-convex (overlapping) faces and since you worked out how to interpolate the colours and the Z co-ordinates, texture co-ordinates should be easy for you to do now Smiley

Yeah :D Next step : texture mapping ^^


QuoteLooking forward to seeing your first Star wars X-Wing fighter Cheesy
hehe I should write an object importer :p And I don't know in which soft I could modelize it. maybe milkshape 3D ? for now, I don't even think about 3dsmax or those kind of 3d factories ^^
Title: Re: Gouraud Filling (Update : Z-Buffer)
Post by: Jim on August 23, 2008
The mistake is in PreCalc_Object_Normals

Jim
Title: Re: Gouraud Filling (Update : Z-Buffer)
Post by: Hezad on August 23, 2008
oh ok :) In fact, the part your talking about (when dividing NbFaces to TmpNorm), is not for faces normals but for Vertex normals (for shading).

Anyway, replacing the division by a normalization looks much better, the light's much better contrasted ! Thanks :)

Some news :
While I was working on Texture Mapping (I'm still on it, there's a problem with the UV projection, but the "pasting" part works for now :) ), I noticed there is a big problem with the way I globally handle my coordinates. I'm so lost I don't know in which sense are organized my axis anymore (especially the Z one ..)

To be continued .. :P
Title: Re: Gouraud Filling (Update : Z-Buffer)
Post by: Hezad on August 23, 2008
Update :

Texture mapping implemented :D

On the other side, I played around with the Light equation part so the Gouraud shading seems jerky  ::) Gotta work on it but still, here is the texture mapping !

I'm really glad, it's the first time I go so far in 3D  :updance: (hm .. I like this smiley)

Code :



''  =======================---. 3D Stuff .---=======================
''
''      Gouraud Shading + Z-Buffer rendering
''
''                      Code : Hezad  [   Hezad0 )at( Gmail.com    ]
''
''
''  Thanks to Relsoft for his awesome tutorial :
''                 [  http://www.phatcode.net/articles.php?id=214  ]
''
''
''  ================================================================


#Define Norm(a) sqr(a.x*a.x + a.y*a.y + a.z*a.z)

Const MAX_TRIANGLES_PER_OBJECT = 500
Const MAX_OBJECTS = 10
Const xRes = 640, yRes = 480

Const SCREEN_MEM = xRes * yRes

Const BGND_COLOR = rgb(50,50,70)

Const MAX_Z = 1000

Dim shared as integer ptr ScrPtr
ReDim shared as integer ZBuffer(SCREEN_MEM)

Screenres xRes,yRes,32,2

ScrPtr = Screenptr

Dim shared as single FPS_t,CurFPS,FPS_counter

Type Vector
    as single x,y,z
End Type

Type ColorT
    as integer r,g,b
End Type

Type TextureT
   
    as vector Size

    as integer ptr GFX
   
End Type

Sub Set_Texture_Size(Text as TextureT, x as integer, y as integer)
   
    If Text.GFX <> 0 then deallocate Text.GFX
   
    Text.Size.x = x
    Text.Size.y = y
   
    Text.GFX = Callocate(x*y,sizeof(integer))
   
End Sub

Type VertexT
   
    as Vector Pos3d
   
    as Vector Gouraud_Normal
   
    as Vector Pos2d
   
    as ColorT Color
   
    as ColorT OriginalColor
   
    as single U,V
   
End Type

Type TriangleT
   
    as Vector Normal
   
    as integer idZ
   
    as VertexT Vertex(2)
   
    as Vector Parent_Center
   
End Type

Type ObjectT
   
    as TriangleT Triangle(MAX_TRIANGLES_PER_OBJECT)
   
    as Vector Center
   
    as integer nb_triangles
   
End Type

Type LightT
   
    Pos3d as Vector
   
    Intensity as single
   
End Type

Type CameraT
   
    Pos as Vector
   
    Angle as Vector
   
End Type

   
Dim shared as single FOV = 256
Dim shared as CameraT Camera

Sub Set_Camera(x as integer, y as integer, z as integer)
   
    Camera.Pos.x = x
    Camera.Pos.y = y
    Camera.Pos.z = z
   
End Sub

Sub Sort_Object_Z(obj as ObjectT)
   
    For i as integer = 1 to obj.nb_Triangles
       
        obj.triangle(i).IdZ = i
        if i>1 then
            if obj.triangle(i).vertex(0).pos3d.z + obj.triangle(i).vertex(1).pos3d.z + obj.triangle(i).vertex(2).pos3d.z < obj.triangle(i-1).vertex(0).pos3d.z + obj.triangle(i-1).vertex(1).pos3d.z + obj.triangle(i-1).vertex(2).pos3d.z then

                Swap obj.triangle(i), obj.triangle(i-1)
                i=1
               
            end if
        end if
       
    Next
   
End Sub

Sub Calc_Triangle_Normal(Tri as TriangleT)
   
    dim as single ax,bx,ay,by,az,bz,NNorm
   
    ax = Tri.Vertex(1).pos3d.x - Tri.Vertex(0).pos3d.x
    bx = Tri.Vertex(2).pos3d.x - Tri.Vertex(1).pos3d.x
    ay = Tri.Vertex(1).pos3d.y - Tri.Vertex(0).pos3d.y
    by = Tri.Vertex(2).pos3d.y - Tri.Vertex(1).pos3d.y
    az = Tri.Vertex(1).pos3d.z - Tri.Vertex(0).pos3d.z
    bz = Tri.Vertex(2).pos3d.z - Tri.Vertex(1).pos3d.z
   
    Tri.Normal.x = ay * bz - az * by
    Tri.Normal.y = az * bx - ax * bz
    Tri.Normal.z = ax * by - ay * bx
   
    '' Normalize
    NNorm = 1/Norm(Tri.Normal)
   
    Tri.Normal.x *= NNorm
    Tri.Normal.y *= NNorm
    Tri.Normal.z *= NNorm
   
End Sub

#Define SamePnt(a,b) iif (a.pos3d.x = b.pos3d.x and a.pos3d.y = b.pos3d.y and a.pos3d.z = b.pos3d.z,1,0)

Sub PreCalc_Object_Normals(obj as ObjectT)
   
    dim as single tmpXNorm,TmpYNorm,TmpZNorm
    dim as integer tmpNBfaces
   
    for k as integer = 1 to obj.nb_triangles
       
        '' face normal
        Calc_Triangle_Normal(obj.Triangle(k))
   
        '' "Vertex normals"
        for i as integer = 0 to 2
            TmpXNorm = 0
            TmpYNorm = 0
            TmpZNorm = 0
       
            for j as integer = 1 to obj.nb_triangles
                if SamePnt(obj.triangle(k).Vertex(0),obj.triangle(j).Vertex(i)) or _
                   SamePnt(obj.triangle(k).Vertex(1),obj.triangle(j).Vertex(i)) or _
                   SamePnt(obj.triangle(k).Vertex(2),obj.triangle(j).Vertex(i)) then
                   
                    TmpXNorm += obj.triangle(j).Normal.x
                    TmpYNorm += obj.triangle(j).Normal.y
                    TmpZNorm += obj.triangle(j).Normal.z
               
                end if
            next
           
            dim as single _Normm = 1/sqr(TmpXNorm*TmpXNorm+TmpYNorm*TmpYNorm+TmpZNorm*TmpZNorm)
           
            TmpXNorm *= _Normm
            TmpYNorm *= _Normm
            TmpZNorm *= _Normm
           
            obj.triangle(k).Vertex(i).Gouraud_Normal.x = TmpXNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.y = TmpyNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.z = TmpzNorm
       
        next
       
    next
   
End Sub

Function SetLight(x as single,y as single, z as single,intens as single) as LightT
   
    Dim as LightT Tmp
   
    Tmp.pos3d.x = x
    Tmp.pos3d.y = y
    Tmp.pos3d.z = z
   
    Tmp.Intensity = intens
   
    Return Tmp
   
End Function

Declare Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)

Sub Project_Object(Obj as ObjectT)
   
    dim as single aX,aY,aZ
   
    if obj.nb_triangles = 0 then exit Sub
   
    For i as integer = 1 to obj.nb_triangles
       
        obj.Triangle(i).Parent_Center = obj.Center
       
        For j as integer = 0 to 2
           
            if obj.Triangle(i).Vertex(j).pos3d.z = Camera.pos.z - obj.Center.z + FOV then obj.Triangle(i).Vertex(j).pos3d.z+=.0001
           
            obj.Triangle(i).Vertex(j).pos2d.x = xRes/2 + FOV*(-Camera.pos.x + obj.Center.x + obj.Triangle(i).Vertex(j).pos3d.x) / (FOV - (-Camera.pos.z + obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
            obj.Triangle(i).Vertex(j).pos2d.y = yRes/2 - FOV*(-Camera.pos.y + obj.Center.y + obj.Triangle(i).Vertex(j).pos3d.y) / (FOV - (-Camera.pos.z + obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
           
        Next
       
    Next
   
End Sub

Sub GLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal r1 as integer,ByVal g1 as integer,ByVal b1 as integer,ByVal r2 as integer,ByVal g2 as integer,ByVal b2 as integer,ByVal z1 as integer,ByVal z2 as integer)
   
    dim as single dR,dG,dB, dZ
    dim as single R,g,b
   
    dim as integer itx1,itx2,zz1,zz2
    Dim as single CurZ,xDiv
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
   
    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        itx1 = x1
        itx2 = x2
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        zz1 = z2
        zz2 = z1
       
        itx1 = x2
        itx2 = x1
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    xDiv = 1/(itx2 - itx1)
   
    dR = xDiv * (sr2 - sr1)
    dG = xDiv * (sg2 - sg1)
    dB = xDiv * (sb2 - sb1)
   
    dZ = xDiv * (zz2 - zz1)
   
    r = sr1
    g = sg1
    b = sb1
   
    CurZ = zz1
   
    dim as integer it
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
           
            it = y*xRes + i
           
            If (FOV - (-Camera.pos.z + CurZ))>0 then
               
                If Curz > ZBuffer(it) then
                   
                    ZBuffer(it) = CurZ
                   
                    ScrPtr[it] = rgb(r,g,b)
                   
                    '' UnComment to show the ZBuffer Gfx :D
                    'dim as integer zBufferCol = 255*cast(single,(abs(ZBuffer(it))/MAX_Z))
                    'ScrPtr[it] = rgb(zBufferCol,zBufferCol,zBufferCol)
                   
                End if
           
            end if
       
        end if
       
        curZ += dZ
       
        r += dR
        g += dG
        b += dB
       
    Next
   
End Sub

Sub Gouraud_Fill(ByVal Triangle as TriangleT)
   
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1),Triangle.Vertex(0)
        swap Triangle.Vertex(1).Gouraud_Normal,Triangle.Vertex(0).Gouraud_Normal
    end if
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(0)
        swap Triangle.Vertex(1).Gouraud_Normal,Triangle.Vertex(0).Gouraud_Normal
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(1)
        swap Triangle.Vertex(1).Gouraud_Normal,Triangle.Vertex(0).Gouraud_Normal
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as integer dr1,dg1,db1,dr2,dg2,db2,dr3,dg3,db3
    dim as single Slope1,Slope2,Slope3, dYDiv
    dim as single RSlope1, GSlope1,BSlope1
    dim as single RSlope2, GSlope2,BSlope2
    dim as single RSlope3, GSlope3,BSlope3
   
    Dim as integer dz1,dz2,dz3
    Dim as single zSlope1,zSlope2,zSlope3
   
    '' interpolate 0 to 1 (pos + color)
   
    dZ1 = Triangle.Vertex(1).pos3d.z - Triangle.Vertex(0).pos3d.z
   
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dr1 = Triangle.Vertex(1).color.r - Triangle.Vertex(0).color.r
    dg1 = Triangle.Vertex(1).color.g - Triangle.Vertex(0).color.g
    db1 = Triangle.Vertex(1).color.b - Triangle.Vertex(0).color.b
   
    if dY1 <> 0 then
       
        dYDiv = 1/dY1
       
        zSlope1 = dZ1 * dYDiv
       
        Slope1 = dX1 * dYDiv
       
        RSlope1 = dR1 * dYDiv
        GSlope1 = dG1 * dYDiv
        BSlope1 = dB1 * dYDiv
       
    else
       
        zSlope1 = 0
       
        Slope1 = 0
       
        RSlope1 = 0
        GSlope1 = 0
        BSlope1 = 0
       
    End if
   
   
    '' interpolate 1 to 2
   
    dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z
   
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dr2 = Triangle.Vertex(2).color.r - Triangle.Vertex(1).color.r
    dg2 = Triangle.Vertex(2).color.g - Triangle.Vertex(1).color.g
    db2 = Triangle.Vertex(2).color.b - Triangle.Vertex(1).color.b
   
    if dY2 <> 0 then
       
        dYDiv = 1/dY2
       
        zSlope2 = dZ2 * dYDiv
       
        Slope2 = dX2 * dYDiv
       
        RSlope2 = dR2 * dYDiv
        GSlope2 = dG2 * dYDiv
        BSlope2 = dB2 * dYDiv
       
    else
       
        zSlope2 = 0
       
        Slope2 = 0
       
        RSlope2 = 0
        GSlope2 = 0
        BSlope2 = 0
       
    End if
   
   
    '' interpolate 0 to 2
   
    dZ3 = Triangle.Vertex(0).pos3d.z - Triangle.Vertex(2).pos3d.z
   
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dr3 = Triangle.Vertex(0).color.r - Triangle.Vertex(2).color.r
    dg3 = Triangle.Vertex(0).color.g - Triangle.Vertex(2).color.g
    db3 = Triangle.Vertex(0).color.b - Triangle.Vertex(2).color.b
   
    if dY3 <> 0 then
       
        dYDiv = 1/dY3
       
        zSlope3 = dZ3 * dYDiv
       
        Slope3 = dX3 * dYDiv
       
        RSlope3 = dR3 * dYDiv
        GSlope3 = dG3 * dYDiv
        BSlope3 = dB3 * dYDiv
       
    else
       
        zSlope3 = 0
       
        Slope3 = 0
       
        RSlope3 = 0
        GSlope3 = 0
        BSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurR1=Triangle.Vertex(0).Color.r,_
                   CurG1=Triangle.Vertex(0).Color.g,_
                   CurB1=Triangle.Vertex(0).Color.b,_
                   CurR2=Triangle.Vertex(0).Color.r,_
                   CurG2=Triangle.Vertex(0).Color.g,_
                   CurB2=Triangle.Vertex(0).Color.b
   
    dim as single CurZ1=Triangle.Vertex(0).pos3d.z, CurZ2=Triangle.Vertex(0).pos3d.z
   
    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2, CurZ1, CurZ2)
       
        CurZ1 += zSlope1
        CurZ2 += zSlope3
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        CurR1 += RSlope1
        CurR2 += RSlope3
        CurG1 += GSlope1
        CurG2 += GSlope3
        CurB1 += BSlope1
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurZ1 = Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z
   
    CurX1 = Triangle.Vertex(1).pos2d.x
    CurR1 = Triangle.Vertex(1).color.r
    Curg1 = Triangle.Vertex(1).color.g
    Curb1 = Triangle.Vertex(1).color.b
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2, CurZ1, CurZ2)
       
        CurZ1 += zSlope2
        CurZ2 += zSlope3
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurR1 += RSlope2
        CurR2 += RSlope3
        CurG1 += GSlope2
        CurG2 += GSlope3
        CurB1 += BSlope2
        CurB2 += BSlope3
       
    Next
   
End Sub
       
Sub Calc_Gouraud_Shader(obj_ as objectT,L_ as LightT)
   
    Dim as single Dot1,Dot2,Dot3,NL
   
    Dim as objectT obj = obj_
    Dim as LightT L = L_
   
   
    '' normalizing Light vector
    NL = 1/Norm(L.pos3d)
   
    L.pos3d.x *= NL
    L.pos3d.y *= NL
    L.pos3d.z *= NL
   
    '' dot products
   
    For k as integer = 1 to obj.nb_triangles
       
        Dot1 = (obj.triangle(k).Vertex(0).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.z * L.pos3d.z)
        if Dot1<0 then Dot1 = 0
        if Dot1>1 then Dot1 = 1
       
        Dot2 = (obj.triangle(k).Vertex(1).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.z * L.pos3d.z)
        if Dot2<0 then Dot2 = 0
        if Dot2>1 then Dot2 = 1
       
        Dot3 = (obj.triangle(k).Vertex(2).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.z * L.pos3d.z)
        if Dot3<0 then Dot3 = 0
        if Dot3>1 then Dot3 = 1
       
        '' apply new color
        obj_.triangle(k).Vertex(0).Color.r = obj_.triangle(k).Vertex(0).OriginalColor.r*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.g = obj_.triangle(k).Vertex(0).OriginalColor.g*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.b = obj_.triangle(k).Vertex(0).OriginalColor.b*L.Intensity*Dot1
       
        obj_.triangle(k).Vertex(1).Color.r = obj_.triangle(k).Vertex(1).OriginalColor.r*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.g = obj_.triangle(k).Vertex(1).OriginalColor.g*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.b = obj_.triangle(k).Vertex(1).OriginalColor.b*L.Intensity*Dot2
       
        obj_.triangle(k).Vertex(2).Color.r = obj_.triangle(k).Vertex(2).OriginalColor.r*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.g = obj_.triangle(k).Vertex(2).OriginalColor.g*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.b = obj_.triangle(k).Vertex(2).OriginalColor.b*L.Intensity*Dot3
    next
   
   
   
End sub


'' texturing
Sub TLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal U1 as single,ByVal V1 as single,ByVal U2 as single,ByVal V2 as single,ByVal z1 as integer,ByVal z2 as integer,byval Texture as TextureT)
   
    dim as single dU,dV,dZ,U,V
    dim as single Uu1,Vv1,Uu2,Vv2
   
    dim as integer itx1,itx2,zz1,zz2
    Dim as single CurZ,xDiv
   
    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        itx1 = x1
        itx2 = x2
       
        Uu1 = u1
        Uu2 = u2
        Vv1 = v1
        Vv2 = v2
    else
        zz1 = z2
        zz2 = z1
       
        itx1 = x2
        itx2 = x1
       
        Uu1 = u2
        Uu2 = u1
        Vv1 = v2
        Vv2 = v1
    end if
   
    Uu1 *= Texture.Size.x
    Uu2 *= Texture.Size.x
    Vv1 *= Texture.Size.y
    Vv2 *= Texture.Size.y
   
    xDiv = 1/(itx2 - itx1)
   
    dZ = xDiv * (zz2 - zz1)
   
    dU = xDiv * (Uu2 - Uu1)
    dV = xDiv * (Vv2 - Vv1)
   
    CurZ = zz1
   
    U = Uu1
    V = Vv1
   
    dim as integer it
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
           
            it = y*xRes + i
           
            If (FOV - (-Camera.pos.z + CurZ))>0 then
               
                If Curz > ZBuffer(it) then
                   
                    ZBuffer(it) = CurZ
                   
                    ScrPtr[it] = Texture.GFX[int(U)+Texture.Size.x*int(V)]
                                       
                End if
           
            end if
       
        end if
       
        U += dU
        V += dV
       
        curZ += dZ
       
    Next
   
End Sub



Sub Texture_Map(ByVal Triangle as TriangleT, _
                ByVal Texture as TextureT)      '' UV's saved in the Triangle.Vertex Type
   
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1),Triangle.Vertex(0)
    end if
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(0)
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(1)
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as single Slope1,Slope2,Slope3, dYDiv
   
    dim as single dU1,dU2,dU3,dV1,dV2,dV3
    dim as single USlope1,USlope2,USlope3
    dim as single VSlope1,VSlope2,VSlope3
   
    Dim as integer dz1,dz2,dz3
    Dim as single zSlope1,zSlope2,zSlope3
   
    '' interpolate 0 to 1 (pos + color)
   
    dZ1 = Triangle.Vertex(1).pos3d.z - Triangle.Vertex(0).pos3d.z
   
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dU1 = Triangle.Vertex(1).U - Triangle.Vertex(0).U
    dV1 = Triangle.Vertex(1).V - Triangle.Vertex(0).V
   
    if dY1 <> 0 then
       
        dYDiv = 1/dY1
       
        zSlope1 = dZ1 * dYDiv
       
        Slope1 = dX1 * dYDiv
       
        USlope1 = dU1 * dYDiv
        VSlope1 = dV1 * dYDiv
       
    else
       
        zSlope1 = 0
       
        Slope1 = 0
       
        USlope1 = 0
        VSlope1 = 0
       
    End if
   
   
    '' interpolate 1 to 2
   
    dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z
   
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dU2 = Triangle.Vertex(2).U - Triangle.Vertex(1).U
    dV2 = Triangle.Vertex(2).V - Triangle.Vertex(1).V
   
    if dY2 <> 0 then
       
        dYDiv = 1/dY2
       
        zSlope2 = dZ2 * dYDiv
       
        Slope2 = dX2 * dYDiv
       
        USlope2 = dU2 * dYDiv
        VSlope2 = dV2 * dYDiv
       
    else
       
        zSlope2 = 0
       
        Slope2 = 0
       
        USlope2 = 0
        VSlope2 = 0
       
    End if
   
   
    '' interpolate 0 to 2
   
    dZ3 = Triangle.Vertex(0).pos3d.z - Triangle.Vertex(2).pos3d.z
   
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dU3 = Triangle.Vertex(0).U - Triangle.Vertex(2).U
    dV3 = Triangle.Vertex(0).V - Triangle.Vertex(2).V
   
    if dY3 <> 0 then
       
        dYDiv = 1/dY3
       
        zSlope3 = dZ3 * dYDiv
       
        Slope3 = dX3 * dYDiv
       
        USlope3 = dU3 * dYDiv
        VSlope3 = dV3 * dYDiv
       
    else
       
        zSlope3 = 0
       
        Slope3 = 0
       
        USlope3 = 0
        VSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurU1=Triangle.Vertex(0).U, CurU2=Triangle.Vertex(0).U,_
                  CurV1=Triangle.Vertex(0).V, CurV2=Triangle.Vertex(0).V
   
    dim as single CurZ1=Triangle.Vertex(0).pos3d.z, CurZ2=Triangle.Vertex(0).pos3d.z
   
    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        TLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture)
       
        CurZ1 += zSlope1
        CurZ2 += zSlope3
       
        CurU1 += USlope1
        CurU2 += USlope3
        CurV1 += VSlope1
        CurV2 += VSlope3
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurZ1 = Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z
   
    CurU1 = Triangle.Vertex(1).U
    CurV1 = Triangle.Vertex(1).V
   
    CurX1 = Triangle.Vertex(1).pos2d.x
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        TLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture)
       
        CurZ1 += zSlope2
        CurZ2 += zSlope3
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurU1 += USlope2
        CurU2 += USlope3
        CurV1 += VSlope2
        CurV2 += VSlope3
       
    Next
   
End Sub



Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)
   
    dim as single tmpX,tmpY,TmpZ,normX,normY,normZ
    dim as single VnormX,VnormY,VnormZ
   
    For i as integer = 1 to obj.NB_triangles
        For j as integer = 0 to 2
           
            '' Points
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tx) - obj.Triangle(i).Vertex(j).pos3d.z * sin(Tx)
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Tx) + obj.Triangle(i).Vertex(j).pos3d.y * sin(Tx)
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
           
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Ty) - obj.Triangle(i).Vertex(j).pos3d.x * sin(Ty)
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Ty) + obj.Triangle(i).Vertex(j).pos3d.z * sin(Ty)
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
           
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Tz) - obj.Triangle(i).Vertex(j).pos3d.y * sin(Tz)
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tz) + obj.Triangle(i).Vertex(j).pos3d.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
                   
       
            '' Vertex normals
            VnormY = obj.Triangle(i).Vertex(j).Gouraud_Normal.y * cos(Tx) - obj.Triangle(i).Vertex(j).Gouraud_Normal.z * sin(Tx)
            VnormZ = obj.Triangle(i).Vertex(j).Gouraud_Normal.z * cos(Tx) + obj.Triangle(i).Vertex(j).Gouraud_Normal.y * sin(Tx)
            obj.Triangle(i).Vertex(j).Gouraud_Normal.y = VnormY
            obj.Triangle(i).Vertex(j).Gouraud_Normal.z = VnormZ
           
            VnormZ = obj.Triangle(i).Vertex(j).Gouraud_Normal.z * cos(Ty) - obj.Triangle(i).Vertex(j).Gouraud_Normal.x * sin(Ty)
            VnormX = obj.Triangle(i).Vertex(j).Gouraud_Normal.x * cos(Ty) + obj.Triangle(i).Vertex(j).Gouraud_Normal.z * sin(Ty)
            obj.Triangle(i).Vertex(j).Gouraud_Normal.x = VnormX

            VnormX = obj.Triangle(i).Vertex(j).Gouraud_Normal.x * cos(Tz) - obj.Triangle(i).Vertex(j).Gouraud_Normal.y * sin(Tz)
            VnormY = obj.Triangle(i).Vertex(j).Gouraud_Normal.y * cos(Tz) + obj.Triangle(i).Vertex(j).Gouraud_Normal.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).Gouraud_Normal.x = VnormX
            obj.Triangle(i).Vertex(j).Gouraud_Normal.y = VnormY
            obj.Triangle(i).Vertex(j).Gouraud_Normal.z = VnormZ
        Next
       
        '' normals
        NormY = obj.Triangle(i).Normal.y * cos(Tx) - obj.Triangle(i).Normal.z * sin(Tx)
        NormZ = obj.Triangle(i).Normal.z * cos(Tx) + obj.Triangle(i).Normal.y * sin(Tx)
        obj.Triangle(i).Normal.y = NormY
        obj.Triangle(i).Normal.z = NormZ
       
        NormZ = obj.Triangle(i).Normal.z * cos(Ty) - obj.Triangle(i).Normal.x * sin(Ty)
        NormX = obj.Triangle(i).Normal.x * cos(Ty) + obj.Triangle(i).Normal.z * sin(Ty)
        obj.Triangle(i).Normal.x = NormX
       
        NormX = obj.Triangle(i).Normal.x * cos(Tz) - obj.Triangle(i).Normal.y * sin(Tz)
        NormY = obj.Triangle(i).Normal.y * cos(Tz) + obj.Triangle(i).Normal.x * sin(Tz)
           
        obj.Triangle(i).Normal.x = NormX
        obj.Triangle(i).Normal.y = NormY
        obj.Triangle(i).Normal.z = NormZ
       
    Next
   
End sub

Function FPS() as integer
   
    dim as integer _curfps
   
    If Timer - FPS_t <= 1 then
       
        FPS_counter += 1
       
        Return -1
       
    else
       
        _CurFPS = FPS_Counter/(Timer - FPS_t)
       
        FPS_counter = 0
        FPS_t = Timer
       
        Return _CurFPS
       
    End if
End Function
       
       
       
#Define Cls_zBuffer For klm as integer =0 to SCREEN_MEM-1 : Zbuffer(klm) = -MAX_Z : next



















'' Test

'' Loading objects

Dim as ObjectT Pyramid, Face

Pyramid.nb_triangles = 4
Pyramid.Center.z = 0
Pyramid.Center.y = 0
Pyramid.Center.x = -100

Face.Nb_triangles = 2
Face.Center.x = 100

For i as integer = 1 to Pyramid.nb_triangles
    for j as integer = 0 to 2
       
        Read Pyramid.triangle(i).Vertex(j).pos3d.x, Pyramid.triangle(i).Vertex(j).pos3d.y,Pyramid.triangle(i).Vertex(j).pos3d.z
        Read Pyramid.triangle(i).Vertex(j).OriginalColor.r,Pyramid.triangle(i).Vertex(j).OriginalColor.g,Pyramid.triangle(i).Vertex(j).OriginalColor.b
        Read Pyramid.triangle(i).Vertex(j).U, Pyramid.triangle(i).Vertex(j).V
       
        Pyramid.triangle(i).Vertex(j).pos3d.x *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.y *= 4
        Pyramid.triangle(i).Vertex(j).pos3d.z *= 4
       
    next
next

For i as integer = 1 to Face.nb_triangles
    for j as integer = 0 to 2
       
        Read Face.triangle(i).Vertex(j).pos3d.x, Face.triangle(i).Vertex(j).pos3d.y,Face.triangle(i).Vertex(j).pos3d.z
        Read Face.triangle(i).Vertex(j).OriginalColor.r,Face.triangle(i).Vertex(j).OriginalColor.g,Face.triangle(i).Vertex(j).OriginalColor.b
        Read Face.triangle(i).Vertex(j).U, Face.triangle(i).Vertex(j).V
       
        Face.triangle(i).Vertex(j).pos3d.x *= 4
        Face.triangle(i).Vertex(j).pos3d.y *= 4
        Face.triangle(i).Vertex(j).pos3d.z *= 4
       
    next
next

'' Setting Light and stuff, Precalculating normals

dim as single t=.02

Dim as LightT Light

Light = SetLight(1,0,0,1)

PreCalc_object_normals Pyramid

Set_Camera 0,0,0



'' Texture loading for texture mapping
Dim as TextureT PlasmaTexture

Set_Texture_Size PlasmaTexture,50,50

dim as integer colorexp

For i as integer = 0 to 49
    For j as integer = 0 to 49
       
        colorexp = 125+125*sin((sqr(((25-i)/2)^2+((25-j)/3)^2))*cos(i/20+j/20))
       
        PlasmaTexture.GFX[i+j*PlasmaTexture.Size.x] = rgb(colorexp,colorexp,colorexp)

    Next
Next


Do
   
    Screenlock : cls
   
    Paint(0,0),BGND_COLOR
   
    CLS_zBuffer
   
    Project_object Pyramid
    Project_object Face
   
    Calc_Gouraud_Shader Pyramid, Light
    'Calc_Gouraud_Shader Pyramid2, Light
   
    For i as integer = 1 to Pyramid.nb_triangles
       
        'if Pyramid.triangle(i).Normal.Z>0 then
            Gouraud_Fill Pyramid.triangle(i)
        'end if
       
        'if Pyramid2.triangle(i).Normal.Z>0 then
            Texture_Map Face.triangle(i),PlasmaTexture
        'end if
       
    Next
   
    Rotate(Pyramid,t/2,t,0)
    Rotate(Face,0,-t,0)
   
    '' DEBUG : Show Texture
    For i as integer = 0 to 49
        For j as integer = 0 to 49
           
            ScrPtr[i+j*xRes] = PlasmaTexture.GFX[i+j*PlasmaTexture.Size.x]
   
        Next
    Next
   
    '' DEBGU : Show FPS
    CurFPS = FPS
    If CurFPS <> -1 and CurFPS <>0 then
        WindowTitle "["+str(CurFPS)+" FPS] 3D stuff"
    elseIf CurFPS = 0 then
        WindowTitle "3D Stuff"
    End if
   
    screenunlock : sleep 1,1
   
loop until multikey(&h01)


'' PYRAMID
'' triangle 1
Data 0,30,0,250,50,50,0,0
Data 20,0,-20,250,50,50,1,1
Data -20,0,-20,250,50,50,0,1

'' triangle 2
Data 0,30,0,250,50,250,0,0
Data 0,0,20,250,50,250,1,1
Data 20,0,-20,250,50,250,0,1

'' triangle 3
Data 0,30,0,50,50,250,0,0
Data -20,0,-20,50,50,250,1,1
Data 0,0,20,50,50,250,0,1

'' triangle 4
Data 20,0,-20,50,250,50,0,0
Data 0,0,20,50,250,50,1,1
Data -20,0,-20,50,250,50,0,1


'' Face
'' triangle 1
Data -20,20,0,250,250,250,0,0
Data 20,20,0,250,250,250,1,0
Data -20,-20,0,250,250,250,0,1

'' triangle 2
Data 20,20,0,250,250,250,1,0
Data 20,-20,0,250,250,250,1,1
Data -20,-20,0,250,250,250,0,1


Attached : Screenshot + Rar(Exe + Code)
Title: Re: Gouraud Filling (Update : Texture Mapping)
Post by: Hezad on August 24, 2008
Update :D (post copied for fb's forums, lazy to rewrite it ^^)

Little Update :)

Well, I decided to test my stuff on cubes instead of pyramids, it's way more easy to see if something's wrong in the lighting/shading/texture mapping !

I think light and projection are now better but there are glitches on the right cube (non textured to test the shading). I guess it's a problem with inversed normals on some triangles but I tried to rearrange those triangles points without any amelioration :S I'm on it.

(http://narky00.free.fr/screen_gouraud_Tameliorated.jpg)

Download : Rar (Src+Exe+Texture) (http://narky00.free.fr/Gouraud_Texture_better.rar)

Any idea about the glitch ?
Title: Re: Gouraud Filling (Update : Texture Mapping)
Post by: Shockwave on August 24, 2008
Textures are working really nicely, well done :)

For a cube the normals are easy to do, they would either be (X,Y,Z);
(  1,0,0)
(-1,0,0)
(0,1,0)
(0,-1,0)
(0,0,1)
(0,0,-1)

Make sure that the winding order for all the triangles are the same too ie all points in clockwise or all points counter clockwise :)

I am at my parents hose atm and havent been able to look at the code, only the exes :)
Title: Re: Gouraud Filling (Update : Texture Mapping)
Post by: Hezad on August 24, 2008
thanks for your reply :)

QuoteFor a cube the normals are easy to do, they would either be (X,Y,Z);
(  1,0,0)
(-1,0,0)
(0,1,0)
(0,-1,0)
(0,0,1)
(0,0,-1)
The point is I don't specify myself the normals, they are all precalculated for each triangle :p

QuoteMake sure that the winding order for all the triangles are the same too ie all points in clockwise or all points counter clockwise

Well here is precisely the problem :p I declared all my vertices in a clockwise order ! I verified all the vertex data but an error in the vertices order could pass through my verification and I don't see what's going on :p

*/me continue to search the error*

QuoteI am at my parents hose atm and havent been able to look at the code, only the exes

no problem :D


edit : err... Wait a minute ...  :inspired: !! I think I inverted the Z-Axis in some vertices data statements !
Title: Re: Gouraud Filling (Update : Texture Mapping)
Post by: Shockwave on August 24, 2008
Quote from: Hezad on August 24, 2008
edit : err... Wait a minute ...  :inspired: !! I think I inverted the Z-Axis in some vertices data statements !

And therefore the normals will be pointing the wrong way :)
Title: Re: Gouraud Filling (Update : Texture Mapping)
Post by: Hezad on August 24, 2008
Well erm.. So after a big verification, my Z-axis IS inversed. In fact, he's in the other direction I thought :p And Vertices must be declared Counter-clockwise ! uhuh So now It's way easier to understand and visualize where my vertices are.

But !

I suppressed my cube and started a new one to make sure there are no errors. Well the cube looks better but there is still a bug : On some faces (the majority in fact), we can see the diagonal line when 2 triangles meet on a face : There should be a gradient but the demarcation is pretty visible (see screenshot) ???

I guess there is a problem in the Vertex Normal Calc but now i need to find it :p If you have any idea of what can cause that ...



edit : Well I found the origin of the problem : If I calc my normals on each loop instead of rotating the precalculated ones, It works perfectly ! So the problem comes from the way I rotate my normals !

Re-edit : O_o in fact, I just have to run the precalc_normals sub twice, and then the rotation is ok.
That's really weird :p But for now it works :D



Re-Re-edit :
aaand, I fouuund it !! Rah what a dumb I am :whack: A simple inattention bug :p Ok so now it works, without any trick behind ^^

Next step : Gouraud shading on texture mapped polys
Title: Re: Gouraud Filling (Update : Texture Mapping)
Post by: Hezad on August 25, 2008
Update !

Now Texture mapped objects support Gouraud shading  :updance:


Code (Needs the texture in the attached Rar file) :



''  =======================---. 3D Stuff .---=======================
''
''      Gouraud Shading + Z-Buffer rendering
''
''                      Code : Hezad  [   Hezad0 )at( Gmail.com    ]
''
''
''  Thanks to Relsoft for his awesome tutorial :
''                 [  http://www.phatcode.net/articles.php?id=214  ]
''
''
''  ================================================================


#Define Norm(a) sqr(a.x*a.x + a.y*a.y + a.z*a.z)

Const MAX_TRIANGLES_PER_OBJECT = 500
Const MAX_OBJECTS = 10
Const xRes = 640, yRes = 480

Const SCREEN_MEM = xRes * yRes

Const BGND_COLOR = rgb(50,50,70)

Const FOV = 512, ZOOM = 512

Const MAX_Z = 1000

Const PI = 3.1415926535897932384626
Const PIx2 = 2*PI

Const ToRad = PIx2/360
Const ToDeg = 360/PIx2



Dim shared as integer ptr ScrPtr
ReDim shared as integer ZBuffer(SCREEN_MEM)

Screenres xRes,yRes,32,2

ScrPtr = Screenptr

Dim shared as single FPS_t,CurFPS,FPS_counter

Type Vector
    as single x,y,z
End Type

Type ColorT
    as integer r,g,b
End Type

Type TextureT
   
    as vector Size

    as integer ptr GFX
   
End Type

Sub Set_Texture_Size(Text as TextureT, x as integer, y as integer)
   
    If Text.GFX <> 0 then deallocate Text.GFX
   
    Text.Size.x = x
    Text.Size.y = y-1
   
    Text.GFX = Callocate(x*y,sizeof(integer))
   
End Sub

Type VertexT
   
    as Vector Pos3d
   
    as Vector Gouraud_Normal
   
    as Vector Pos2d
   
    as ColorT Color
   
    as ColorT OriginalColor
   
    as single U,V
   
End Type

Type TriangleT
   
    as Vector Normal
   
    as integer idZ
   
    as VertexT Vertex(2)
   
    as Vector Parent_Center
   
End Type

Type ObjectT
   
    as TriangleT Triangle(MAX_TRIANGLES_PER_OBJECT)
   
    as Vector Center
   
    as integer nb_triangles
   
End Type

Type LightT
   
    Pos3d as Vector
   
    Intensity as single
   
End Type

Type CameraT
   
    Pos as Vector
   
    Angle as Vector
   
End Type


Dim shared as CameraT Camera

Sub Set_Camera(x as integer, y as integer, z as integer)
   
    Camera.Pos.x = x
    Camera.Pos.y = y
    Camera.Pos.z = z
   
End Sub

Sub Sort_Object_Z(obj as ObjectT)
   
    For i as integer = 1 to obj.nb_Triangles
       
        obj.triangle(i).IdZ = i
        if i>1 then
            if obj.triangle(i).vertex(0).pos3d.z + obj.triangle(i).vertex(1).pos3d.z + obj.triangle(i).vertex(2).pos3d.z < obj.triangle(i-1).vertex(0).pos3d.z + obj.triangle(i-1).vertex(1).pos3d.z + obj.triangle(i-1).vertex(2).pos3d.z then

                Swap obj.triangle(i), obj.triangle(i-1)
                i=1
               
            end if
        end if
       
    Next
   
End Sub

Sub Calc_Triangle_Normal(Tri as TriangleT)
   
    dim as single ax,bx,ay,by,az,bz,NNorm
   
    ax = Tri.Vertex(1).pos3d.x - Tri.Vertex(0).pos3d.x
    bx = Tri.Vertex(2).pos3d.x - Tri.Vertex(1).pos3d.x
    ay = Tri.Vertex(1).pos3d.y - Tri.Vertex(0).pos3d.y
    by = Tri.Vertex(2).pos3d.y - Tri.Vertex(1).pos3d.y
    az = Tri.Vertex(1).pos3d.z - Tri.Vertex(0).pos3d.z
    bz = Tri.Vertex(2).pos3d.z - Tri.Vertex(1).pos3d.z
   
    Tri.Normal.x = ay * bz - az * by
    Tri.Normal.y = az * bx - ax * bz
    Tri.Normal.z = ax * by - ay * bx
   
    '' Normalize
    NNorm = 1/Norm(Tri.Normal)
   
    Tri.Normal.x *= NNorm
    Tri.Normal.y *= NNorm
    Tri.Normal.z *= NNorm
   
End Sub

#Define SamePnt(a,b) iif ((a.pos3d.x = b.pos3d.x) and (a.pos3d.y = b.pos3d.y) and (a.pos3d.z = b.pos3d.z),1,0)

Sub PreCalc_Object_Normals(obj as ObjectT)
   
    dim as single tmpXNorm,TmpYNorm,TmpZNorm
   
    For k as integer = 1 to obj.nb_triangles
        '' face normal
        Calc_Triangle_Normal(obj.Triangle(k))
   
    Next
   
    for k as integer = 1 to obj.nb_triangles
       
        '' "Vertex normals"
        for i as integer = 0 to 2
            TmpXNorm = 0
            TmpYNorm = 0
            TmpZNorm = 0
           
            for j as integer = 1 to obj.nb_triangles
                if SamePnt(obj.triangle(k).Vertex(i),obj.triangle(j).Vertex(0)) or _
                    SamePnt(obj.triangle(k).Vertex(i),obj.triangle(j).Vertex(1)) or _
                    SamePnt(obj.triangle(k).Vertex(i),obj.triangle(j).Vertex(2)) then
                   
                    TmpXNorm += obj.triangle(j).Normal.x
                    TmpYNorm += obj.triangle(j).Normal.y
                    TmpZNorm += obj.triangle(j).Normal.z
                   
                end if
            next
               
            dim as single _Normm = 1/sqr(TmpXNorm*TmpXNorm+TmpYNorm*TmpYNorm+TmpZNorm*TmpZNorm)
           
            TmpXNorm *= _Normm
            TmpYNorm *= _Normm
            TmpZNorm *= _Normm
           
            obj.triangle(k).Vertex(i).Gouraud_Normal.x = TmpXNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.y = TmpyNorm
            obj.triangle(k).Vertex(i).Gouraud_Normal.z = TmpzNorm
           
        next
       
    next
   
End Sub

Function SetLight(x as single,y as single, z as single,intens as single) as LightT
   
    Dim as LightT Tmp
   
    Tmp.pos3d.x = x
    Tmp.pos3d.y = y
    Tmp.pos3d.z = z
   
    Tmp.Intensity = intens
   
    Return Tmp
   
End Function

Declare Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)


'' Projection ============================

Sub Project_Object(Obj as ObjectT)
   
    dim as single aX,aY,aZ
   
    if obj.nb_triangles = 0 then exit Sub
   
    For i as integer = 1 to obj.nb_triangles
       
        obj.Triangle(i).Parent_Center = obj.Center
       
        For j as integer = 0 to 2
           
            if obj.Triangle(i).Vertex(j).pos3d.z = Camera.pos.z - obj.Center.z + FOV then obj.Triangle(i).Vertex(j).pos3d.z+=.0001
           
            obj.Triangle(i).Vertex(j).pos2d.x = xRes/2 + (FOV * (-Camera.pos.x + obj.Center.x + obj.Triangle(i).Vertex(j).pos3d.x)) / (ZOOM - (Camera.pos.z + obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
            obj.Triangle(i).Vertex(j).pos2d.y = yRes/2 - (FOV * (-Camera.pos.y + obj.Center.y + obj.Triangle(i).Vertex(j).pos3d.y)) / (ZOOM - (Camera.pos.z + obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z))
           
        Next
       
    Next
   
End Sub

'' ========================================

Sub GLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal r1 as integer,ByVal g1 as integer,ByVal b1 as integer,ByVal r2 as integer,ByVal g2 as integer,ByVal b2 as integer,ByVal z1 as integer,ByVal z2 as integer)
   
    dim as single dR,dG,dB, dZ
    dim as single R,g,b
   
    dim as integer itx1,itx2,zz1,zz2
    Dim as single CurZ,xDiv
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
   
    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        itx1 = x1
        itx2 = x2
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        zz1 = z2
        zz2 = z1
       
        itx1 = x2
        itx2 = x1
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    xDiv = 1/(itx2 - itx1)
   
    dR = xDiv * (sr2 - sr1)
    dG = xDiv * (sg2 - sg1)
    dB = xDiv * (sb2 - sb1)
   
    dZ = xDiv * (zz2 - zz1)
   
    r = sr1
    g = sg1
    b = sb1
   
    CurZ = zz1
   
    dim as integer it
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
           
            it = y*xRes + i
           
            If (FOV - (-Camera.pos.z + CurZ))>0 then
               
                If Curz > ZBuffer(it) then
                   
                    ZBuffer(it) = CurZ
                   
                    ScrPtr[it] = rgb(r,g,b)
                   
                    '' UnComment to show the ZBuffer Gfx :D
                    'dim as integer zBufferCol = 255*cast(single,(abs(ZBuffer(it))/MAX_Z))
                    'ScrPtr[it] = rgb(zBufferCol,zBufferCol,zBufferCol)
                   
                End if
           
            end if
       
        end if
       
        curZ += dZ
       
        r += dR
        g += dG
        b += dB
       
    Next
   
End Sub

Sub Gouraud_Fill(ByVal Triangle as TriangleT)
   
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1),Triangle.Vertex(0)
    end if
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(0)
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(1)
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as integer dr1,dg1,db1,dr2,dg2,db2,dr3,dg3,db3
    dim as single Slope1,Slope2,Slope3, dYDiv
    dim as single RSlope1, GSlope1,BSlope1
    dim as single RSlope2, GSlope2,BSlope2
    dim as single RSlope3, GSlope3,BSlope3
   
    Dim as integer dz1,dz2,dz3
    Dim as single zSlope1,zSlope2,zSlope3
   
    '' interpolate 0 to 1 (pos + color)
   
    dZ1 = Triangle.Vertex(1).pos3d.z - Triangle.Vertex(0).pos3d.z
   
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dr1 = Triangle.Vertex(1).color.r - Triangle.Vertex(0).color.r
    dg1 = Triangle.Vertex(1).color.g - Triangle.Vertex(0).color.g
    db1 = Triangle.Vertex(1).color.b - Triangle.Vertex(0).color.b
   
    if dY1 <> 0 then
       
        dYDiv = 1/dY1
       
        zSlope1 = dZ1 * dYDiv
       
        Slope1 = dX1 * dYDiv
       
        RSlope1 = dR1 * dYDiv
        GSlope1 = dG1 * dYDiv
        BSlope1 = dB1 * dYDiv
       
    else
       
        zSlope1 = 0
       
        Slope1 = 0
       
        RSlope1 = 0
        GSlope1 = 0
        BSlope1 = 0
       
    End if
   
   
    '' interpolate 1 to 2
   
    dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z
   
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dr2 = Triangle.Vertex(2).color.r - Triangle.Vertex(1).color.r
    dg2 = Triangle.Vertex(2).color.g - Triangle.Vertex(1).color.g
    db2 = Triangle.Vertex(2).color.b - Triangle.Vertex(1).color.b
   
    if dY2 <> 0 then
       
        dYDiv = 1/dY2
       
        zSlope2 = dZ2 * dYDiv
       
        Slope2 = dX2 * dYDiv
       
        RSlope2 = dR2 * dYDiv
        GSlope2 = dG2 * dYDiv
        BSlope2 = dB2 * dYDiv
       
    else
       
        zSlope2 = 0
       
        Slope2 = 0
       
        RSlope2 = 0
        GSlope2 = 0
        BSlope2 = 0
       
    End if
   
   
    '' interpolate 0 to 2
   
    dZ3 = Triangle.Vertex(0).pos3d.z - Triangle.Vertex(2).pos3d.z
   
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dr3 = Triangle.Vertex(0).color.r - Triangle.Vertex(2).color.r
    dg3 = Triangle.Vertex(0).color.g - Triangle.Vertex(2).color.g
    db3 = Triangle.Vertex(0).color.b - Triangle.Vertex(2).color.b
   
    if dY3 <> 0 then
       
        dYDiv = 1/dY3
       
        zSlope3 = dZ3 * dYDiv
       
        Slope3 = dX3 * dYDiv
       
        RSlope3 = dR3 * dYDiv
        GSlope3 = dG3 * dYDiv
        BSlope3 = dB3 * dYDiv
       
    else
       
        zSlope3 = 0
       
        Slope3 = 0
       
        RSlope3 = 0
        GSlope3 = 0
        BSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurR1=Triangle.Vertex(0).Color.r,_
                   CurG1=Triangle.Vertex(0).Color.g,_
                   CurB1=Triangle.Vertex(0).Color.b,_
                   CurR2=Triangle.Vertex(0).Color.r,_
                   CurG2=Triangle.Vertex(0).Color.g,_
                   CurB2=Triangle.Vertex(0).Color.b
   
    dim as single CurZ1=Triangle.Vertex(0).pos3d.z, CurZ2=Triangle.Vertex(0).pos3d.z
   
    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2, CurZ1, CurZ2)
       
        CurZ1 += zSlope1
        CurZ2 += zSlope3
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        CurR1 += RSlope1
        CurR2 += RSlope3
        CurG1 += GSlope1
        CurG2 += GSlope3
        CurB1 += BSlope1
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurZ1 = Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z
   
    CurX1 = Triangle.Vertex(1).pos2d.x
    CurR1 = Triangle.Vertex(1).color.r
    Curg1 = Triangle.Vertex(1).color.g
    Curb1 = Triangle.Vertex(1).color.b
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        GLine(CurX1,CurX2,y,CurR1,CurG1,CurB1,CurR2,CurG2,CurB2, CurZ1, CurZ2)
       
        CurZ1 += zSlope2
        CurZ2 += zSlope3
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurR1 += RSlope2
        CurR2 += RSlope3
        CurG1 += GSlope2
        CurG2 += GSlope3
        CurB1 += BSlope2
        CurB2 += BSlope3
       
    Next
   
End Sub
       
Sub Calc_Gouraud_Shader(obj_ as objectT,L_ as LightT)
   
    Dim as single Dot1,Dot2,Dot3,NL
   
    Dim as objectT obj = obj_
    Dim as LightT L = L_
   
   
    '' normalizing Light vector
    NL = 1/Norm(L.pos3d)
   
    L.pos3d.x *= NL
    L.pos3d.y *= NL
    L.pos3d.z *= NL
   
    '' dot products
   
    For k as integer = 1 to obj.nb_triangles
       
        Dot1 = (obj.triangle(k).Vertex(0).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(0).Gouraud_Normal.z * L.pos3d.z)
        if Dot1<0 then Dot1 = 0
        if Dot1>1 then Dot1 = 1
       
        Dot2 = (obj.triangle(k).Vertex(1).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(1).Gouraud_Normal.z * L.pos3d.z)
        if Dot2<0 then Dot2 = 0
        if Dot2>1 then Dot2 = 1
       
        Dot3 = (obj.triangle(k).Vertex(2).Gouraud_Normal.x * L.pos3d.x) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.y * L.pos3d.y) + _
               (obj.triangle(k).Vertex(2).Gouraud_Normal.z * L.pos3d.z)
        if Dot3<0 then Dot3 = 0
        if Dot3>1 then Dot3 = 1
       
        '' apply new color
        obj_.triangle(k).Vertex(0).Color.r = obj_.triangle(k).Vertex(0).OriginalColor.r*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.g = obj_.triangle(k).Vertex(0).OriginalColor.g*L.Intensity*Dot1
        obj_.triangle(k).Vertex(0).Color.b = obj_.triangle(k).Vertex(0).OriginalColor.b*L.Intensity*Dot1
       
        obj_.triangle(k).Vertex(1).Color.r = obj_.triangle(k).Vertex(1).OriginalColor.r*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.g = obj_.triangle(k).Vertex(1).OriginalColor.g*L.Intensity*Dot2
        obj_.triangle(k).Vertex(1).Color.b = obj_.triangle(k).Vertex(1).OriginalColor.b*L.Intensity*Dot2
       
        obj_.triangle(k).Vertex(2).Color.r = obj_.triangle(k).Vertex(2).OriginalColor.r*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.g = obj_.triangle(k).Vertex(2).OriginalColor.g*L.Intensity*Dot3
        obj_.triangle(k).Vertex(2).Color.b = obj_.triangle(k).Vertex(2).OriginalColor.b*L.Intensity*Dot3
    next
   
   
   
End sub


'' texturing
Sub TLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal U1 as single,ByVal V1 as single,ByVal U2 as single,ByVal V2 as single,ByVal z1 as integer,ByVal z2 as integer,byval Texture as TextureT)
   
    dim as single dU,dV,dZ,U,V
    dim as single Uu1,Vv1,Uu2,Vv2
   
    dim as integer itx1,itx2,zz1,zz2
    Dim as single CurZ,xDiv
   
    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        itx1 = x1
        itx2 = x2
       
        Uu1 = u1
        Uu2 = u2
        Vv1 = v1
        Vv2 = v2
    else
        zz1 = z2
        zz2 = z1
       
        itx1 = x2
        itx2 = x1
       
        Uu1 = u2
        Uu2 = u1
        Vv1 = v2
        Vv2 = v1
    end if
   
    Uu1 *= Texture.Size.x
    Uu2 *= Texture.Size.x
    Vv1 *= Texture.Size.y
    Vv2 *= Texture.Size.y
   
    xDiv = 1/(itx2 - itx1)
   
    dZ = xDiv * (zz2 - zz1)
   
    dU = xDiv * (Uu2 - Uu1)
    dV = xDiv * (Vv2 - Vv1)
   
    CurZ = zz1
   
    U = Uu1
    V = Vv1
   
    dim as integer it
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
           
            it = y*xRes + i
           
            If (FOV - (-Camera.pos.z + CurZ))>0 then
               
                If Curz > ZBuffer(it) then
                   
                    ZBuffer(it) = CurZ
                   
                    ScrPtr[it] = Texture.GFX[int(U)+Texture.Size.x*int(V)]
                                       
                End if
           
            end if
       
        end if
       
        U += dU
        V += dV
       
        curZ += dZ
       
    Next
   
End Sub



Sub Texture_Map(ByVal Triangle as TriangleT, _
                ByVal Texture as TextureT)      '' UV's saved in the Triangle.Vertex Type
   
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1),Triangle.Vertex(0)
    end if
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(0)
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(1)
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as single Slope1,Slope2,Slope3, dYDiv
   
    dim as single dU1,dU2,dU3,dV1,dV2,dV3
    dim as single USlope1,USlope2,USlope3
    dim as single VSlope1,VSlope2,VSlope3
   
    Dim as integer dz1,dz2,dz3
    Dim as single zSlope1,zSlope2,zSlope3
   
    '' interpolate 0 to 1 (pos + color)
   
    dZ1 = Triangle.Vertex(1).pos3d.z - Triangle.Vertex(0).pos3d.z
   
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dU1 = Triangle.Vertex(1).U - Triangle.Vertex(0).U
    dV1 = Triangle.Vertex(1).V - Triangle.Vertex(0).V
   
    if dY1 <> 0 then
       
        dYDiv = 1/dY1
       
        zSlope1 = dZ1 * dYDiv
       
        Slope1 = dX1 * dYDiv
       
        USlope1 = dU1 * dYDiv
        VSlope1 = dV1 * dYDiv
       
    else
       
        zSlope1 = 0
       
        Slope1 = 0
       
        USlope1 = 0
        VSlope1 = 0
       
    End if
   
   
    '' interpolate 1 to 2
   
    dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z
   
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dU2 = Triangle.Vertex(2).U - Triangle.Vertex(1).U
    dV2 = Triangle.Vertex(2).V - Triangle.Vertex(1).V
   
    if dY2 <> 0 then
       
        dYDiv = 1/dY2
       
        zSlope2 = dZ2 * dYDiv
       
        Slope2 = dX2 * dYDiv
       
        USlope2 = dU2 * dYDiv
        VSlope2 = dV2 * dYDiv
       
    else
       
        zSlope2 = 0
       
        Slope2 = 0
       
        USlope2 = 0
        VSlope2 = 0
       
    End if
   
   
    '' interpolate 0 to 2
   
    dZ3 = Triangle.Vertex(0).pos3d.z - Triangle.Vertex(2).pos3d.z
   
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dU3 = Triangle.Vertex(0).U - Triangle.Vertex(2).U
    dV3 = Triangle.Vertex(0).V - Triangle.Vertex(2).V
   
    if dY3 <> 0 then
       
        dYDiv = 1/dY3
       
        zSlope3 = dZ3 * dYDiv
       
        Slope3 = dX3 * dYDiv
       
        USlope3 = dU3 * dYDiv
        VSlope3 = dV3 * dYDiv
       
    else
       
        zSlope3 = 0
       
        Slope3 = 0
       
        USlope3 = 0
        VSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurU1=Triangle.Vertex(0).U, CurU2=Triangle.Vertex(0).U,_
                  CurV1=Triangle.Vertex(0).V, CurV2=Triangle.Vertex(0).V
   
    dim as single CurZ1=Triangle.Vertex(0).pos3d.z, CurZ2=Triangle.Vertex(0).pos3d.z
   
    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        TLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture)
       
        CurZ1 += zSlope1
        CurZ2 += zSlope3
       
        CurU1 += USlope1
        CurU2 += USlope3
        CurV1 += VSlope1
        CurV2 += VSlope3
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurZ1 = Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z
   
    CurU1 = Triangle.Vertex(1).U
    CurV1 = Triangle.Vertex(1).V
   
    CurX1 = Triangle.Vertex(1).pos2d.x
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        TLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture)
       
        CurZ1 += zSlope2
        CurZ2 += zSlope3
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurU1 += USlope2
        CurU2 += USlope3
        CurV1 += VSlope2
        CurV2 += VSlope3
       
    Next
   
End Sub


'' Texture Mapping + Gouraud shader
'' texturing
Sub TSLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal U1 as single,ByVal V1 as single,ByVal U2 as single,ByVal V2 as single,ByVal z1 as integer,ByVal z2 as integer,byval Texture as TextureT, r1 as integer,r2 as integer,g1 as integer,g2 as integer,b1 as integer,b2 as integer)
   
    dim as single dR,dG,dB
    dim as single R,g,b
   
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
   
    dim as single dU,dV,dZ,U,V
    dim as single Uu1,Vv1,Uu2,Vv2
   
    dim as integer itx1,itx2,zz1,zz2
    Dim as single CurZ,xDiv
   
    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        itx1 = x1
        itx2 = x2
       
        Uu1 = u1
        Uu2 = u2
        Vv1 = v1
        Vv2 = v2
       
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        zz1 = z2
        zz2 = z1
       
        itx1 = x2
        itx2 = x1
       
        Uu1 = u2
        Uu2 = u1
        Vv1 = v2
        Vv2 = v1
       
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    Uu1 *= Texture.Size.x
    Uu2 *= Texture.Size.x
    Vv1 *= Texture.Size.y
    Vv2 *= Texture.Size.y
   
    xDiv = 1/(itx2 - itx1)
   
    dR = xDiv * (sr2 - sr1)
    dG = xDiv * (sg2 - sg1)
    dB = xDiv * (sb2 - sb1)
   
    dZ = xDiv * (zz2 - zz1)
   
    dU = xDiv * (Uu2 - Uu1)
    dV = xDiv * (Vv2 - Vv1)
   
    CurZ = zz1
   
    U = Uu1
    V = Vv1
   
    r = sr1
    g = sg1
    b = sb1
   
    dim as integer it
    Dim as integer CurTex,Cr,Cg,Cb
   
    for i as integer = itx1 to itx2-1

        if y>0 and y<yRes and i>0 and i<xRes then
           
            it = y*xRes + i
           
            If (FOV - (-Camera.pos.z + CurZ))>0 then
               
                If Curz > ZBuffer(it) then
                   
                    ZBuffer(it) = CurZ
                   
                    CurTex = Texture.GFX[int(U)+Texture.Size.x*int(V)]
                   
                    dim as single _255 = 1/255
                   
                    Cb = CurTex and 255
                    Cg = (CurTex shr 8) and 255
                    Cr = (CurTex shr 16) and 255
                   
                    ScrPtr[it] = rgb(r*Cr*_255,g*Cg*_255,b*Cb*_255)
                                       
                End if
           
            end if
       
        end if
       
        U += dU
        V += dV
       
        r += dR
        g += dG
        b += dB
       
        curZ += dZ
       
    Next
   
End Sub

Sub Texture_Map_Shaded(ByVal Triangle as TriangleT, _
                       ByVal Texture as TextureT)      '' UV's saved in the Triangle.Vertex Type
   
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1),Triangle.Vertex(0)
    end if
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(0)
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(1)
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as single Slope1,Slope2,Slope3, dYDiv
   
    dim as single dU1,dU2,dU3,dV1,dV2,dV3
    dim as single USlope1,USlope2,USlope3
    dim as single VSlope1,VSlope2,VSlope3
   
    Dim as integer dz1,dz2,dz3
    Dim as single zSlope1,zSlope2,zSlope3
   
    dim as integer dr1,dg1,db1,dr2,dg2,db2,dr3,dg3,db3
    dim as single RSlope1, GSlope1,BSlope1
    dim as single RSlope2, GSlope2,BSlope2
    dim as single RSlope3, GSlope3,BSlope3
   
    '' interpolate 0 to 1 (pos + color)
   
    dZ1 = Triangle.Vertex(1).pos3d.z - Triangle.Vertex(0).pos3d.z
   
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dU1 = Triangle.Vertex(1).U - Triangle.Vertex(0).U
    dV1 = Triangle.Vertex(1).V - Triangle.Vertex(0).V
   
    dr1 = Triangle.Vertex(1).color.r - Triangle.Vertex(0).color.r
    dg1 = Triangle.Vertex(1).color.g - Triangle.Vertex(0).color.g
    db1 = Triangle.Vertex(1).color.b - Triangle.Vertex(0).color.b
   
    if dY1 <> 0 then
       
        dYDiv = 1/dY1
       
        zSlope1 = dZ1 * dYDiv
       
        Slope1 = dX1 * dYDiv
       
        USlope1 = dU1 * dYDiv
        VSlope1 = dV1 * dYDiv
       
        RSlope1 = dR1 * dYDiv
        GSlope1 = dG1 * dYDiv
        BSlope1 = dB1 * dYDiv
       
    else
       
        zSlope1 = 0
       
        Slope1 = 0
       
        USlope1 = 0
        VSlope1 = 0
       
        RSlope1 = 0
        GSlope1 = 0
        BSlope1 = 0
    End if
   
   
    '' interpolate 1 to 2
   
    dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z
   
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dU2 = Triangle.Vertex(2).U - Triangle.Vertex(1).U
    dV2 = Triangle.Vertex(2).V - Triangle.Vertex(1).V

    dr2 = Triangle.Vertex(2).color.r - Triangle.Vertex(1).color.r
    dg2 = Triangle.Vertex(2).color.g - Triangle.Vertex(1).color.g
    db2 = Triangle.Vertex(2).color.b - Triangle.Vertex(1).color.b

    if dY2 <> 0 then
       
        dYDiv = 1/dY2
       
        zSlope2 = dZ2 * dYDiv
       
        Slope2 = dX2 * dYDiv
       
        USlope2 = dU2 * dYDiv
        VSlope2 = dV2 * dYDiv
       
        RSlope2 = dR2 * dYDiv
        GSlope2 = dG2 * dYDiv
        BSlope2 = dB2 * dYDiv
       
    else
       
        zSlope2 = 0
       
        Slope2 = 0
       
        USlope2 = 0
        VSlope2 = 0
       
        RSlope2 = 0
        GSlope2 = 0
        BSlope2 = 0
    End if
   
   
    '' interpolate 0 to 2
   
    dZ3 = Triangle.Vertex(0).pos3d.z - Triangle.Vertex(2).pos3d.z
   
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dU3 = Triangle.Vertex(0).U - Triangle.Vertex(2).U
    dV3 = Triangle.Vertex(0).V - Triangle.Vertex(2).V

    dr3 = Triangle.Vertex(0).color.r - Triangle.Vertex(2).color.r
    dg3 = Triangle.Vertex(0).color.g - Triangle.Vertex(2).color.g
    db3 = Triangle.Vertex(0).color.b - Triangle.Vertex(2).color.b

    if dY3 <> 0 then
       
        dYDiv = 1/dY3
       
        zSlope3 = dZ3 * dYDiv
       
        Slope3 = dX3 * dYDiv
       
        USlope3 = dU3 * dYDiv
        VSlope3 = dV3 * dYDiv
       
        RSlope3 = dR3 * dYDiv
        GSlope3 = dG3 * dYDiv
        BSlope3 = dB3 * dYDiv
       
    else
       
        zSlope3 = 0
       
        Slope3 = 0
       
        USlope3 = 0
        VSlope3 = 0
       
        RSlope3 = 0
        GSlope3 = 0
        BSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurU1=Triangle.Vertex(0).U, CurU2=Triangle.Vertex(0).U,_
                  CurV1=Triangle.Vertex(0).V, CurV2=Triangle.Vertex(0).V
   
    dim as single CurR1=Triangle.Vertex(0).Color.r,_
                   CurG1=Triangle.Vertex(0).Color.g,_
                   CurB1=Triangle.Vertex(0).Color.b,_
                   CurR2=Triangle.Vertex(0).Color.r,_
                   CurG2=Triangle.Vertex(0).Color.g,_
                   CurB2=Triangle.Vertex(0).Color.b
   
    dim as single CurZ1=Triangle.Vertex(0).pos3d.z, CurZ2=Triangle.Vertex(0).pos3d.z
   
    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        TSLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture,CurR1,CurR2,CurG1,CurG2,CurB1,CurB2)
       
        CurZ1 += zSlope1
        CurZ2 += zSlope3
       
        CurU1 += USlope1
        CurU2 += USlope3
        CurV1 += VSlope1
        CurV2 += VSlope3
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        CurR1 += RSlope1
        CurR2 += RSlope3
        CurG1 += GSlope1
        CurG2 += GSlope3
        CurB1 += BSlope1
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurZ1 = Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z
   
    CurU1 = Triangle.Vertex(1).U
    CurV1 = Triangle.Vertex(1).V
   
    CurX1 = Triangle.Vertex(1).pos2d.x
   
    CurR1 = Triangle.Vertex(1).color.r
    Curg1 = Triangle.Vertex(1).color.g
    Curb1 = Triangle.Vertex(1).color.b
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        TSLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture,CurR1,CurR2,CurG1,CurG2,CurB1,CurB2)
       
        CurZ1 += zSlope2
        CurZ2 += zSlope3
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurU1 += USlope2
        CurU2 += USlope3
        CurV1 += VSlope2
        CurV2 += VSlope3
       
        CurR1 += RSlope2
        CurR2 += RSlope3
        CurG1 += GSlope2
        CurG2 += GSlope3
        CurB1 += BSlope2
        CurB2 += BSlope3
       
    Next
   
End Sub

Sub Rotate(obj as objectT,Tx as single, Ty as single, Tz as single)
   
    dim as single tmpX,tmpY,TmpZ,normX,normY,normZ
    dim as single VnormX,VnormY,VnormZ
   
    For i as integer = 1 to obj.NB_triangles
        For j as integer = 0 to 2
           
            '' Points
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tx) - obj.Triangle(i).Vertex(j).pos3d.z * sin(Tx)
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Tx) + obj.Triangle(i).Vertex(j).pos3d.y * sin(Tx)
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
           
            TmpZ = obj.Triangle(i).Vertex(j).pos3d.z * cos(Ty) - obj.Triangle(i).Vertex(j).pos3d.x * sin(Ty)
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Ty) + obj.Triangle(i).Vertex(j).pos3d.z * sin(Ty)
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
           
            TmpX = obj.Triangle(i).Vertex(j).pos3d.x * cos(Tz) - obj.Triangle(i).Vertex(j).pos3d.y * sin(Tz)
            TmpY = obj.Triangle(i).Vertex(j).pos3d.y * cos(Tz) + obj.Triangle(i).Vertex(j).pos3d.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).pos3d.x = TmpX
            obj.Triangle(i).Vertex(j).pos3d.y = TmpY
            obj.Triangle(i).Vertex(j).pos3d.z = TmpZ
                   
       
            '' Vertex normals
            VnormY = obj.Triangle(i).Vertex(j).Gouraud_Normal.y * cos(Tx) - obj.Triangle(i).Vertex(j).Gouraud_Normal.z * sin(Tx)
            VnormZ = obj.Triangle(i).Vertex(j).Gouraud_Normal.z * cos(Tx) + obj.Triangle(i).Vertex(j).Gouraud_Normal.y * sin(Tx)
            obj.Triangle(i).Vertex(j).Gouraud_Normal.y = VnormY
            obj.Triangle(i).Vertex(j).Gouraud_Normal.z = VnormZ
           
            VnormZ = obj.Triangle(i).Vertex(j).Gouraud_Normal.z * cos(Ty) - obj.Triangle(i).Vertex(j).Gouraud_Normal.x * sin(Ty)
            VnormX = obj.Triangle(i).Vertex(j).Gouraud_Normal.x * cos(Ty) + obj.Triangle(i).Vertex(j).Gouraud_Normal.z * sin(Ty)
            obj.Triangle(i).Vertex(j).Gouraud_Normal.x = VnormX

            VnormX = obj.Triangle(i).Vertex(j).Gouraud_Normal.x * cos(Tz) - obj.Triangle(i).Vertex(j).Gouraud_Normal.y * sin(Tz)
            VnormY = obj.Triangle(i).Vertex(j).Gouraud_Normal.y * cos(Tz) + obj.Triangle(i).Vertex(j).Gouraud_Normal.x * sin(Tz)
           
            obj.Triangle(i).Vertex(j).Gouraud_Normal.x = VnormX
            obj.Triangle(i).Vertex(j).Gouraud_Normal.y = VnormY
            obj.Triangle(i).Vertex(j).Gouraud_Normal.z = VnormZ
        Next
       
        '' normals
        NormY = obj.Triangle(i).Normal.y * cos(Tx) - obj.Triangle(i).Normal.z * sin(Tx)
        NormZ = obj.Triangle(i).Normal.z * cos(Tx) + obj.Triangle(i).Normal.y * sin(Tx)
        obj.Triangle(i).Normal.y = NormY
        obj.Triangle(i).Normal.z = NormZ
       
        NormZ = obj.Triangle(i).Normal.z * cos(Ty) - obj.Triangle(i).Normal.x * sin(Ty)
        NormX = obj.Triangle(i).Normal.x * cos(Ty) + obj.Triangle(i).Normal.z * sin(Ty)
        obj.Triangle(i).Normal.x = NormX
       
        NormX = obj.Triangle(i).Normal.x * cos(Tz) - obj.Triangle(i).Normal.y * sin(Tz)
        NormY = obj.Triangle(i).Normal.y * cos(Tz) + obj.Triangle(i).Normal.x * sin(Tz)
           
        obj.Triangle(i).Normal.x = NormX
        obj.Triangle(i).Normal.y = NormY
        obj.Triangle(i).Normal.z = NormZ
       
    Next
   
End sub

Function FPS() as integer
   
    dim as integer _curfps
   
    If Timer - FPS_t <= 1 then
       
        FPS_counter += 1
       
        Return -1
       
    else
       
        _CurFPS = FPS_Counter/(Timer - FPS_t)
       
        FPS_counter = 0
        FPS_t = Timer
       
        Return _CurFPS
       
    End if
End Function

Function GenerateCube(x0 as integer,y0 as integer,z0 as integer,Size as integer) as objectT
   
    Dim as ObjectT obj
    Dim as Single Size_2 = Size/2
   
    obj.Center.x=x0
    obj.Center.y=y0
    obj.Center.z=z0
   
    obj.nb_triangles = 12 '' 12 !
   
    '' Defining color white/grey
    For i as integer = 1 to obj.nb_triangles
        for j as integer = 0 to 2
            obj.triangle(i).Vertex(j).originalColor.r = 255
            obj.triangle(i).Vertex(j).originalColor.g = 255
            obj.triangle(i).Vertex(j).originalColor.b = 255
        next
    next
   
   
    '' Face 1
    obj.triangle(1).Vertex(0).U = 1
    obj.triangle(1).Vertex(0).V = 0
    obj.triangle(1).Vertex(0).pos3d.x = Size_2
    obj.triangle(1).Vertex(0).pos3d.y = Size_2
    obj.triangle(1).Vertex(0).pos3d.z = Size_2
   
    obj.triangle(1).Vertex(2).U = 0
    obj.triangle(1).Vertex(2).V = 0
    obj.triangle(1).Vertex(2).pos3d.x = -Size_2
    obj.triangle(1).Vertex(2).pos3d.y = Size_2
    obj.triangle(1).Vertex(2).pos3d.z = Size_2

Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on August 25, 2008
That looks really, really, really nice.

Well done :)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 25, 2008
thanks a lot :)

Here's just a little pic resulting from a glitch I had when coding a GenerateSphere() sub, I like it :p

(http://freefile.kristopherw.us/uploads/hezad/glitchnice.jpg)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 26, 2008
Update :


Vector Light replaced by Omni Light


(http://freefile.kristopherw.us/uploads/hezad/screen_gouraud_omnilight.jpg)
~20 rotating cubes with an omni light at the center of the screen

image hosted on Kristopher Windsor's image hosting service :p
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on August 26, 2008
Where's the file? :) Looks nice but I want to see it in action :D
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 26, 2008
uhuh oki :D

here it is ^^

and the code :

edit : erm the code must be too long, the code marks cut it :S but if anyone wants to see it, it's in the .rar :)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on August 26, 2008
That's working really nicely :)

This computer is a P4 3.0 Ghz, but the computer is quite old and the frame rate is perfectly ok here, it does a fairly steady 29 - 32 fps.

I suspect that it would be a lot faster without the lightsourcing :) The lightsourcing looks very nice though.
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 26, 2008
thanks :) Well yeah, if I get off the light calculations and do only a texture mapping i get ~40FPS instead of 30 normally :)

But if cubes are a bit larger or nearer, it's getting worse lol I get ~10 FPS and even 3/4 FPS if they fill the whole screen :S

optimization .. :p
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on August 26, 2008
Optimization.. Well, this is coming I guess. and there are a few things that you can do to optimise it.

I think that what you have done there is achieved by less than 1% of people who learn programming, you've progressed a lot and I bet Relsoft will be really happy when he sees what you made since you read his tutorial, he is a member here and I know he likes to show people how to code things.

Anyway onto your demo, the two things that are a problem are the speed and texture warping.

Texture warping is unavoidable with affine texture mapping like you are doing.
It is more noticeable when there is a lot of perspective on the surface you are drawing, for example, some vertex is deep into the screen and other vertices are close, the texture seems to slide around the polygon.

There are solutions, firstly one of the fastest ways is to subdivide the surface into several smaller triangles and subdivide the UV's too, so in practice you might render


    /\
  /    \
------


Like this;


     *
    /_\
  / \/ \
  ------


(apologies for the dodgy ascii), this means that the warping effect is less noticeable.

Or you could use a perspective correct algorithm,  with this algorithm and as a side effect you get the z-co-ordinate of each pixel for your Z-Buffer.

Another thing you will probably want to look into is clipping and definately mip-mapping.

Both will increase your speed, clipping algorithms will enable you to chop up polygons and thereby discard geometary that is not visible.

Mip mapping you will generate a set of texture maps ranging from small size to large for your polygon and perform the cross-product equation on your polygon to determine it's area so that you can select the texture map that best fits the area of the polygon you are about to draw.

Smaller texture maps are faster, using mip mapping will also help avoid "dancing pixels" which are typically visible on small polygons where the routine is accessing a large texture map. These artifacts appear as flickering dots.

Anyway that's probably more than enough to be going along with, I reccomend that you start off with perspective correct textures :) Please do post if you need help or just to let us know how you are getting along. I for one am really enjoying this topic!
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 26, 2008
Hey :) I was coding something to enjoy my engine start (:p) but I didn't read what you just written while I was doing that, so the thing i'll attach with this post hasn't any of the optimizations you just told me.

So first, i'll answer you :)

QuoteI think that what you have done there is achieved by less than 1% of people who learn programming, you've progressed a lot and I bet Relsoft will be really happy when he sees what you made since you read his tutorial, he is a member here and I know he likes to show people how to code things.

Well, thanks ! this kind of comments surely puts me on the motivation track ! And Relsoft already commented it on the freebasic forums.
Even if I know it's far from a perfect thing, I have to admit I'm pretty proud (yeah auto-satisfaction .. :p) of what I have done so far but you know, sometimes you're so glad about some self-made stuff that you don't see crucial flaws. Until now, I think I saw them and you (and other people) really helped me to fix them.

So once again, thanks !!


QuoteTexture warping is unavoidable with affine texture mapping like you are doing.
It is more noticeable when there is a lot of perspective on the surface you are drawing, for example, some vertex is deep into the screen and other vertices are close, the texture seems to slide around the polygon.

(...)
Totally agree, I really need to code the corrected perspective stuff. It's already on my todo list and I spent some time with my friend "internet" to understand it :p Anyway, your explications are totally helpful and I think it'll help me to work on it !

Oh ! And don't worry about the ASCII diagram, It totally makes sense :)


QuoteAnother thing you will probably want to look into is clipping
Right ! But before, I need to make sure I totally understand/recode my coordinates system :the Z-buffer algo + the projection routine work but use some really weird Z constants and/or subtractions. So I really need to see what's going on in there before thinking about clipping :p
(By clipping, I guess you're speaking about not calculating poly's outside the viewing frustum  ?)


Quoteand definately mip-mapping.
QuoteMip mapping you will generate a set of texture maps ranging from small size to large for your polygon and perform the cross-product equation on your polygon to determine it's area so that you can select the texture map that best fits the area of the polygon you are about to draw.

Smaller texture maps are faster, using mip mapping will also help avoid "dancing pixels" which are typically visible on small polygons where the routine is accessing a large texture map. These artifacts appear as flickering dots.

!! I heard of mip-mapping so often .. I didn't understand in which way it could be helpful.. Now I do ! It has to be on my todo list :D Once again, Really clear explanation :)


QuoteAnyway that's probably more than enough to be going along with, I reccomend that you start off with perspective correct textures Smiley Please do post if you need help or just to let us know how you are getting along. I for one am really enjoying this topic!

perspective correct texture mapping is the first item on my list :D
I will post if I have questions (and I'm pretty sure i'll have :p), thanks !
And I'm glad you like this topic, really ! :)



To finish, I'll post the thing I spoke about at the beginning of this post, the Plasma Cube ! :D Well I already saw that kind of stuff pretty often but whatever ^^

Attached : Screenshot + Rar(Src+Exe)

thanks again Shockwave !

Bye :)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Jim on August 26, 2008
The other thing you need to look at speedwise is you are doing an incredible number of Single->Int conversions in your code,  potentially a dozen per pixel.  That is not a fast thing to do, so you might want to think about how you can do it all in integers.
If you backface cull, you won't see all the insides of the objects too - that will halve the number of faces you are considering.

Jim
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 27, 2008
Quoteyou might want to think about how you can do it all in integers

Yeah sure ! thanks I didn't think about that.


QuoteIf you backface cull, you won't see all the insides of the objects too
hm ? I don't understand :p Since faces are only drawn once, If I backface cull a face, it won't be calculated anyway, no ?
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on August 27, 2008
Quote from: Hezad on August 27, 2008
QuoteIf you backface cull, you won't see all the insides of the objects too
hm ? I don't understand :p Since faces are only drawn once, If I backface cull a face, it won't be calculated anyway, no ?

You can either use the normal to backface cull (is it pointing in or out) or just use the cross-product equation to determine if the points are clockwise or counter clockwise (clue: the result is either positive or negative), then you can decide whether to draw the polygon or not.

This means that for a cube which is made from 12 triangles, potentially you will only need to draw a maximum of 6 of them.

Rotating the vertices and doing a cross product calc is cheap when compared to drawing a texture mapped polygon, so what Jim is saying is that the calcs of rotating the object and doing that simple check to see if you should draw the polygon will speed things up a lot.

Quote(By clipping, I guess you're speaking about not calculating poly's outside the viewing frustum  ?)

I mean not drawing polys or parts of polys that lie outside the view frustrum. :)

Btw, the plasma cube looks really great! Nice colour choice.
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Jim on August 27, 2008
If you clip the polygons, either in 3d (better) or in 2d screen space, then you can remove ALL the If checks that test if the triangle starts off the screen or if a pixel is off the screen.  Doing all that stuff up front makes the hard bit of drawing triangles simpler.

Jim
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Clyde on August 27, 2008
Yeah very cool plasma cube dude.

Welldone on your efforts,
Clyde
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 28, 2008
QuoteRotating the vertices and doing a cross product calc is cheap when compared to drawing a texture mapped polygon, so what Jim is saying is that the calcs of rotating the object and doing that simple check to see if you should draw the polygon will speed things up a lot.

QuoteIf you clip the polygons, either in 3d (better) or in 2d screen space, then you can remove ALL the If checks that test if the triangle starts off the screen or if a pixel is off the screen.  Doing all that stuff up front makes the hard bit of drawing triangles simpler.

So if I understand correctly (and sorry if it's not the case :p), I should verify if the current poly (or pixel?) is in the viewing frustum (in 3d) and then, calculate (Poly Normal [Dot] View Normal) to make sure the poly is visible (and if it's not, I "forget" it for the projection/lighting/rendering calculations) ?

thanks for your answers and for the comments about the plasma cube :)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Xalthorn on August 28, 2008
Wow, this is some seriously impressive work and from what I gather by reading through the posts, you are a literal sponge for information.  You're able to keep progressing by answering sensible questions and acting on the responses.  Something to be encouraged and celebrated.

:clap:

Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Jim on August 28, 2008
Quote
If you clip the polygons, either in 3d (better) or in 2d screen space, then you can remove ALL the If checks that test if the triangle starts off the screen or if a pixel is off the screen.  Doing all that stuff up front makes the hard bit of drawing triangles simpler.
QuoteSo if I understand correctly (and sorry if it's not the case :p), I should verify if the current poly (or pixel?) is in the viewing frustum (in 3d) and then, calculate (Poly Normal [Dot] View Normal) to make sure the poly is visible (and if it's not, I "forget" it for the projection/lighting/rendering calculations) ?
Not really.
In 2d clipping, you do the whole process that you are currently doing, but just before you render the triangle you work out whether any of it is off screen.  If it is, then you can clip the triangle against the screen's rectangle and that will give you a set of triangles which can be rendered without any If testing.

In 3d clipping, you do that process in 3d against the pyramid which is your viewing frustum (imagine a square-based pyramid with the point at the camera and the rectangular base being your far-clip plane).  You can work out, before projection, which polygons are inside that volume.  The advantage of this is that you don't get range error problems when you do the perspective calculation - and you can't do 2d clipping without doing perspective - so you save yourself some real problems.

Jim

Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 28, 2008
Xalthorn > thanks :D Well I ask questions so if people answer me, I have to take their answers into account :)

Jim > ok thanks, I start to understand your point :p But sorry for this last question but since I am not sure about it, I prefer to ask :
when you say
Quoteyou can clip the triangle against the screen's rectangle and that will give you a set of triangles which can be rendered without any If testing.
Do you mean I discard all the triangle if a part of it must be clipped (but it could appear suddenly in the screen if so, no ?) or shall I "just" don't draw the pixels out of screen ?
Because right now, Just before drawing the pixel, I do a "basic" 2D clipping :
If x>0 and x<xRes and y>0 and y<yRes then putPixel

So do you mean doing this but in 3D ? Or Did I totally misunderstand once again ? Sorry for insisting with it but in fact I guess I don't understand how I could verify whether the poly is in screen/viewing frustum or not without any if testing ..
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on August 28, 2008
:)

3D clipping means that you will have to chop up some triangles, it's different to just simple clipping to the viewport, you need to be able to calculate where your triangles intersect the view frustrum and possibly split them into more triangles.

For example consider this triangle;

|\
|  \
|    \
|      \
|        \
|          \
|_______\


and it intersects  the view frustrum;


  |\
_|_\_______________
  |    \
  |      \
  |        \
  |          \
  |_______\


Can be chopped up like this;



 
___________________
  |   / \
  | /     \
  |_____\


The part of the triangle that is outside the viwe area is discarded and two triangles are computed, includin new uv co-ordinates.

Not as hard as it sounds, a clue is you need to work out where the edges of the triangle intersect the clipping planes so that you can chop up the triangle :)

If you need more detailed help, ask away :)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on August 29, 2008
aah okay now I got it, thanks ^^ I didn't understand the fact that I have to "create" new triangles when the current triangle have a part clipped.

Okay, so now I got it, i'll work on it. I still didn't coded the perspective correct mapping (I had a few tries but I didn't interpolate 1/z,u/z and v/z in a good way).

Anyway, I won't be at home for a week so I won't be able to work on it but once i'll get back, correct texture mapping and 3D clipping will be the first things i'll work on. And then, working on reducing the single>integer conversions. And then, maybe mip mapping :D

Still, while I were trying the perspective correct texture mapping, I reorganized a bit the code. So, a little update before the big stuff :

- It supports any number of lights (Add_Light, Set_Light)
- The projection/Z-buffer now recoded with a comprehensible Z-axis handling (It was needed for the 3d clipping and the correct texture mapping)
- New handling of shader calc and filling with user-friendly subs (Set_shader(), Set_Texture()) : if no texture is assigned to an object, it will automatically be rendered with the selected shader)

Thanks for your explanations everyone and see ya in a week or so !
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on August 29, 2008
It's cool that you continue to work on this :) It's been a nice topic so far, hopefully we'll see you implimenting clipping and mip mapping soon.

You will definately need to clip by chopping up triangles because you'll need to clip the triangles according to their depth as well as the x,y position :)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Jim on August 29, 2008
QuoteIf x>0 and x<xRes and y>0 and y<yRes then putPixel
If I had 2 tris filling the screen, that would mean I did this test 640x480 times with every single pixel passing he test, but if I knew the two tris were on screen before I tried drawing them I could remove all these 4 checks from every pixel.  That would be a massive speed up!  The chopping up might sound complicated, but it's worth wroking it out.

Jim
PS. btw, you want
QuoteIf x>=0 and x<xRes and y>=0 and y<yRes then putPixel
otherwise you won't be able to plot anything on the first scanline or the left column of pixels.
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 07, 2008
Hey :) I'm back :D

Well, I wanted to post a perspective correct mapping to celebrate the thing but nothing to do, I tried at least 4 or 5 times to rewrite the sub, it doesn't work...

With the advices provided here and the documentation on the internet, I understood that I should interpolate u/z and v/z (and 1/z but since I already know Z from the Z-Buffer calc, I also tried to use it instead of 1/z), and then multiply by the current Z to get the good uv coords. But erm.. It doesn't work >< It makes the program to crash (I guess the uv coords are too large for the texture ptr). Here is the code used in the subs, I hope it's readable :

Sub TSLine(ByVal x1 as integer,ByVal x2 as integer, y as integer,ByVal U1 as single,ByVal V1 as single,ByVal U2 as single,ByVal V2 as single,ByVal z1 as integer,ByVal z2 as integer,byval Texture as TextureT, r1 as integer,r2 as integer,g1 as integer,g2 as integer,b1 as integer,b2 as integer,_z1 as single,_z2 as single)
   
    dim as single dR,dG,dB
    dim as single R,g,b
   
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
   
    dim as single dU,dV,dZ,U,V
    dim as single Uu1,Vv1,Uu2,Vv2
   
    dim as integer itx1,itx2,zz1,zz2
    Dim as single CurZ,xDiv,Cur_Z,_zz1,_zz2,d_Z
   
    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        _zz1 = _z1
        _zz2 = _z2
       
        itx1 = x1
        itx2 = x2
       
        Uu1 = u1
        Uu2 = u2
        Vv1 = v1
        Vv2 = v2
       
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        zz1 = z2
        zz2 = z1
       
        _zz1 = _z2
        _zz2 = _z1
       
        itx1 = x2
        itx2 = x1
       
        Uu1 = u2
        Uu2 = u1
        Vv1 = v2
        Vv2 = v1
       
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    'Uu1 *= Texture.Size.x
    'Uu2 *= Texture.Size.x
    'Vv1 *= Texture.Size.y
    'Vv2 *= Texture.Size.y
   
    xDiv = 1/(itx2 - itx1)
   
    dR = xDiv * (sr2 - sr1)
    dG = xDiv * (sg2 - sg1)
    dB = xDiv * (sb2 - sb1)
   
    dZ = xDiv * (zz2 - zz1)
    d_Z = xDiv * (_zz2 - _zz1)
   
    dU = xDiv * (Uu2 - Uu1)
    dV = xDiv * (Vv2 - Vv1)
   
    CurZ = zz1
    Cur_Z = _zz1
   
    U = Uu1
    V = Vv1
   
    r = sr1
    g = sg1
    b = sb1
   
    dim as single ValU,ValV
    dim as integer it
    Dim as integer CurTex,Cr,Cg,Cb
   
   
    it = y*xRes + itx1
   
    for i as integer = itx1 to itx2-1

        if y>=0 and y<yRes and i>=0 and i<xRes then
           
            If (FOV - (-Camera.pos.z + CurZ))>0 then
               
                If Curz < ZBuffer(it) then
                   
                    ZBuffer(it) = CurZ
                   
                    'ValU = U*CurZ   '' (U/Z)*Z
                    'ValV = V*CurZ   '' (V/Z)*Z
                   
                    ValU = U/Cur_Z  '' (U/Z)/(1/Z)
                    ValV = V/Cur_Z  '' (V/Z)/(1/Z)
                   
                    CurTex = Texture.GFX[int(ValU*Texture.Size.x)+Texture.Size.x*int(ValV*Texture.Size.y)]
                   
                    Cb = ((CurTex and 255))*b*0.003921  '' 1/255
                    Cg = ((CurTex shr 8) and 255)*g*0.003921
                    Cr = ((CurTex shr 16) and 255)*r*0.003921
                   
                    ScrPtr[it] = rgb(Cr,Cg,Cb)
                   
                end if
            end if
       
        end if
       
        it+=1
       
        U += dU
        V += dV
       
        Cur_Z += d_Z
       
        r += dR
        g += dG
        b += dB
       
        curZ += dZ
       
    Next
   
End Sub
   
Sub Texture_Map_Shaded(ByVal Triangle as TriangleT, _
                       ByVal Texture as TextureT)      '' UV's saved in the Triangle.Vertex Type
   
   
    '' Sorting Y values
    if Triangle.Vertex(1).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(1),Triangle.Vertex(0)
    end if
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(0).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(0)
    end if
   
    if Triangle.Vertex(2).Pos2d.y < Triangle.Vertex(1).Pos2d.y then
        swap Triangle.Vertex(2),Triangle.Vertex(1)
    end if
   
    dim as integer dx1,dy1,dx2,dy2,dx3,dy3
    dim as single Slope1,Slope2,Slope3, dYDiv
   
    dim as single dU1,dU2,dU3,dV1,dV2,dV3
    dim as single USlope1,USlope2,USlope3
    dim as single VSlope1,VSlope2,VSlope3
   
    Dim as integer dz1,dz2,dz3,d_z1,d_z2,d_z3
    Dim as single zSlope1,zSlope2,zSlope3
    Dim as single _zSlope1,_zSlope2,_zSlope3
   
    dim as integer dr1,dg1,db1,dr2,dg2,db2,dr3,dg3,db3
    dim as single RSlope1, GSlope1,BSlope1
    dim as single RSlope2, GSlope2,BSlope2
    dim as single RSlope3, GSlope3,BSlope3
   
    '' interpolate 0 to 1 (pos + color)
   
    dZ1 = Triangle.Vertex(1).pos3d.z - Triangle.Vertex(0).pos3d.z
    d_z1 = 1/(Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z) - 1/(Triangle.Vertex(0).pos3d.z+Triangle.Parent_Center.z)
   
    dX1 = Triangle.Vertex(1).pos2d.x - Triangle.Vertex(0).pos2d.x
    dY1 = Triangle.Vertex(1).pos2d.y - Triangle.Vertex(0).pos2d.y
   
    dU1 = Triangle.Vertex(1).U/(Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z) - Triangle.Vertex(0).U/(Triangle.Vertex(0).pos3d.z+Triangle.Parent_Center.z)
    dV1 = Triangle.Vertex(1).V/(Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z) - Triangle.Vertex(0).V/(Triangle.Vertex(0).pos3d.z+Triangle.Parent_Center.z)
   
    dr1 = Triangle.Vertex(1).color.r - Triangle.Vertex(0).color.r
    dg1 = Triangle.Vertex(1).color.g - Triangle.Vertex(0).color.g
    db1 = Triangle.Vertex(1).color.b - Triangle.Vertex(0).color.b
   
    if dY1 <> 0 then
       
        dYDiv = 1/dY1
       
        zSlope1 = dZ1 * dYDiv
        _zSlope1 = d_Z1 * dYDiv
       
        Slope1 = dX1 * dYDiv
       
        USlope1 = dU1 * dYDiv
        VSlope1 = dV1 * dYDiv
       
        RSlope1 = dR1 * dYDiv
        GSlope1 = dG1 * dYDiv
        BSlope1 = dB1 * dYDiv
       
    else
       
        zSlope1 = 0
        _zSlope1 = 0
       
        Slope1 = 0
       
        USlope1 = 0
        VSlope1 = 0
       
        RSlope1 = 0
        GSlope1 = 0
        BSlope1 = 0
    End if
   
   
    '' interpolate 1 to 2
   
    dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z
    d_Z2 = 1/(Triangle.Vertex(2).pos3d.z+Triangle.Parent_Center.z) - 1/(Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z)
   
    dX2 = Triangle.Vertex(2).pos2d.x - Triangle.Vertex(1).pos2d.x
    dY2 = Triangle.Vertex(2).pos2d.y - Triangle.Vertex(1).pos2d.y
   
    dU2 = Triangle.Vertex(2).U/(Triangle.Vertex(2).pos3d.z+Triangle.Parent_Center.z) - Triangle.Vertex(1).U/(Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z)
    dV2 = Triangle.Vertex(2).V/(Triangle.Vertex(2).pos3d.z+Triangle.Parent_Center.z) - Triangle.Vertex(1).V/(Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z)

    dr2 = Triangle.Vertex(2).color.r - Triangle.Vertex(1).color.r
    dg2 = Triangle.Vertex(2).color.g - Triangle.Vertex(1).color.g
    db2 = Triangle.Vertex(2).color.b - Triangle.Vertex(1).color.b

    if dY2 <> 0 then
       
        dYDiv = 1/dY2
       
        zSlope2 = dZ2 * dYDiv
        _zSlope2 = d_Z2 * dYDiv
       
        Slope2 = dX2 * dYDiv
       
        USlope2 = dU2 * dYDiv
        VSlope2 = dV2 * dYDiv
       
        RSlope2 = dR2 * dYDiv
        GSlope2 = dG2 * dYDiv
        BSlope2 = dB2 * dYDiv
       
    else
       
        zSlope2 = 0
        _zSlope2 = 0
       
        Slope2 = 0
       
        USlope2 = 0
        VSlope2 = 0
       
        RSlope2 = 0
        GSlope2 = 0
        BSlope2 = 0
    End if
   
   
    '' interpolate 0 to 2
   
    dZ3 = Triangle.Vertex(0).pos3d.z - Triangle.Vertex(2).pos3d.z
    d_Z3 = 1/(Triangle.Vertex(0).pos3d.z+Triangle.Parent_Center.z) - 1/(Triangle.Vertex(2).pos3d.z+Triangle.Parent_Center.z)
   
    dX3 = Triangle.Vertex(0).pos2d.x - Triangle.Vertex(2).pos2d.x
    dY3 = Triangle.Vertex(0).pos2d.y - Triangle.Vertex(2).pos2d.y
   
    dU3 = Triangle.Vertex(0).U/(Triangle.Vertex(0).pos3d.z+Triangle.Parent_Center.z) - Triangle.Vertex(2).U/(Triangle.Vertex(2).pos3d.z+Triangle.Parent_Center.z)
    dV3 = Triangle.Vertex(0).V/(Triangle.Vertex(0).pos3d.z+Triangle.Parent_Center.z) - Triangle.Vertex(2).V/(Triangle.Vertex(2).pos3d.z+Triangle.Parent_Center.z)

    dr3 = Triangle.Vertex(0).color.r - Triangle.Vertex(2).color.r
    dg3 = Triangle.Vertex(0).color.g - Triangle.Vertex(2).color.g
    db3 = Triangle.Vertex(0).color.b - Triangle.Vertex(2).color.b

    if dY3 <> 0 then
       
        dYDiv = 1/dY3
       
        zSlope3 = dZ3 * dYDiv
        _zSlope3 = d_Z3 * dYDiv
       
        Slope3 = dX3 * dYDiv
       
        USlope3 = dU3 * dYDiv
        VSlope3 = dV3 * dYDiv
       
        RSlope3 = dR3 * dYDiv
        GSlope3 = dG3 * dYDiv
        BSlope3 = dB3 * dYDiv
       
    else
       
        zSlope3 = 0
        _zSlope3 = 0
       
        Slope3 = 0
       
        USlope3 = 0
        VSlope3 = 0
       
        RSlope3 = 0
        GSlope3 = 0
        BSlope3 = 0
       
    End if
   
   
    '' Drawing
   
    '' Top
   
    dim as single CurX1=Triangle.Vertex(0).pos2d.x, CurX2=Triangle.Vertex(0).pos2d.x
    dim as single CurU1, CurU2,_
                  CurV1, CurV2
   
    dim as single CurR1=Triangle.Vertex(0).Color.r,_
                   CurG1=Triangle.Vertex(0).Color.g,_
                   CurB1=Triangle.Vertex(0).Color.b,_
                   CurR2=Triangle.Vertex(0).Color.r,_
                   CurG2=Triangle.Vertex(0).Color.g,_
                   CurB2=Triangle.Vertex(0).Color.b
   
    dim as single CurZ1=Triangle.Vertex(0).pos3d.z, CurZ2=Triangle.Vertex(0).pos3d.z
    dim as single Cur_Z1, Cur_Z2
   
    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z
   
    CurU1 = Triangle.Vertex(0).U/CurZ1
    CurV1 = Triangle.Vertex(0).V/CurZ1
    CurU2 = Triangle.Vertex(0).U/CurZ1
    CurV2 = Triangle.Vertex(0).V/CurZ1
   
    Cur_Z1 = 1/CurZ1
    Cur_Z2 = 1/CurZ2
   
    'BufferIter = Triangle.Vertex(0).pos2d.y * xRes
   
    For y as integer = Triangle.Vertex(0).pos2d.y to Triangle.Vertex(1).pos2d.y-1
       
        TSLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture,CurR1,CurR2,CurG1,CurG2,CurB1,CurB2,Cur_Z1,Cur_Z2)
       
        CurZ1 += zSlope1
        CurZ2 += zSlope3
       
        Cur_Z1 += _zSlope1
        Cur_Z2 += _zSlope3
       
        CurU1 += USlope1
        CurU2 += USlope3
        CurV1 += VSlope1
        CurV2 += VSlope3
       
        CurX1 += Slope1
        CurX2 += Slope3
       
        CurR1 += RSlope1
        CurR2 += RSlope3
        CurG1 += GSlope1
        CurG2 += GSlope3
        CurB1 += BSlope1
        CurB2 += BSlope3
       
        'BufferIter += xRes
       
    Next
   
    '' down
   
    CurZ1 = Triangle.Vertex(1).pos3d.z+Triangle.Parent_Center.z
    Cur_Z1 = 1/CurZ1
   
    CurU1 = Triangle.Vertex(1).U/CurZ1
    CurV1 = Triangle.Vertex(1).V/CurZ1
   
    CurX1 = Triangle.Vertex(1).pos2d.x
   
    CurR1 = Triangle.Vertex(1).color.r
    Curg1 = Triangle.Vertex(1).color.g
    Curb1 = Triangle.Vertex(1).color.b
   
    For y as integer = Triangle.Vertex(1).pos2d.y to Triangle.Vertex(2).pos2d.y-1
       
        TSLine(CurX1,CurX2,y,CurU1,CurV1,CurU2,CurV2, CurZ1, CurZ2, Texture,CurR1,CurR2,CurG1,CurG2,CurB1,CurB2,Cur_Z1,Cur_Z2)
       
        CurZ1 += zSlope2
        CurZ2 += zSlope3
       
        Cur_Z1 += _zSlope2
        Cur_Z2 += _zSlope3
       
        CurX1 += Slope2
        CurX2 += Slope3
       
        CurU1 += USlope2
        CurU2 += USlope3
        CurV1 += VSlope2
        CurV2 += VSlope3
       
        CurR1 += RSlope2
        CurR2 += RSlope3
        CurG1 += GSlope2
        CurG2 += GSlope3
        CurB1 += BSlope2
        CurB2 += BSlope3
       
    Next
   
End Sub


Can you see what's wrong in there ?
In the code, variables like _z or d_z or Cur_Z means 1/z (respectively  1/z, d(1/z) and Current1/Z) and I kept the name U and V to contain U/z and V/z.

Thanks in advance :)


(update in code)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on September 07, 2008
You have almost certainly answered your own question, because you are not interpolating 1/z, but instead using your own z-buffer calculation and then multiplying that by a factor to use in your equation, then it' more than likely trying to fetch texels from way outside your texture map.

I sat down to read through the source, I am sorry, it's blurry to me with a lot of similar variables that I can't seem to keep track of easily. Anyway..

My advice to you would be to stick to interpolating 1/z.

In a nutshell, you should be doing something similar to this;

Interpolate U/Z, interpolate V/Z and interpolate 1/Z

To get your texels;

((U/Z) / (1/Z)) * Z = U
((V/Z) / (1/Z)) * Z = V

Something like that. It's been a long time since I did it, but I think that's about right.

It's not the fastest way of doing this but you should probably get it working this way first.
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: hellfire on September 07, 2008
Some notes:

    if x1<x2 then
        zz1 = z1
        zz2 = z2
       
        _zz1 = _z1
        _zz2 = _z2
       
        itx1 = x1
        itx2 = x2
       
        Uu1 = u1
        Uu2 = u2
        Vv1 = v1
        Vv2 = v2
       
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        zz1 = z2
        zz2 = z1
       
        _zz1 = _z2
        _zz2 = _z1
       
        itx1 = x2
        itx2 = x1
       
        Uu1 = u2
        Uu2 = u1
        Vv1 = v2
        Vv2 = v1
       
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if

better use a vertexstructure and just swap the pointers.
you don't even have to care about this if all polygons are defined in equal orientation and backface-culling is used to remove all polygons not facing the viewer (so the orientation of x1/x2 will never change).


    xDiv = 1/(itx2 - itx1)
   
    dR = xDiv * (sr2 - sr1)
    dG = xDiv * (sg2 - sg1)
    dB = xDiv * (sb2 - sb1)
   
    dZ = xDiv * (zz2 - zz1)

these gradients across your polygon are linear, this means they are the same for every scanline.
just calculate them once for the longest scanline (biggest deltax: highest precision).
this also means you just have to keep track of u,v,z of the left side of your polygon.

if y>=0 and y<yRes
you don't have to do this for every pixel of a scanline.
check this before you enter the inner loop.

if i>=0 and i<xRes then
don't do this for every pixel either.
instead just test how many pixels are "out" on the left side and update u,v,z (eg. deltaU*pixelsToSkip).
the pixels on the right can just be skipped.
when you interpolating x along your polygon edges, the starting "x" for each scanline is actually a floating-point value.
you should take this into account and update the starting u,v,z of each scanline by a fraction of your deltas (how much is missing from the actual floating-point "x" to the rounded integer-pixel-position).
this is necessary for "y", too and can incorporate clipping on the x/y-axes for free.

If (FOV - (-Camera.pos.z + CurZ))>0 then
if this is some approach of z-clipping, drop it. it doesn't work.
you must perform z-clipping before doing perspective division.
when z gets near 0 (and even flips sign right afterwards) your 2d-coordinates break.

ValU = U/Cur_Z
ValV = V/Cur_Z

this won't result in useful framerates because division is expensive.
instead calculate 1/z (and respective u,v) every 8 or 16 pixels and interpolate linearly between two pairs of perspective correct u,v. if you manage the latter with integer-precision only, you can get the division for free (fpu and cpu run in parallel), but you'll probably need some assembly here.
You can even interpolate u,v bi-linearly (across scanlines, for eaxmple between 4 points of a 8x8-grid):
on an average you just need to calculate two (depending on deltax) additional u,v on a scanline and can skip perspective divide for the next 7 scanlines.
If you want to look deeper into how certain division-algorithms (http://en.wikipedia.org/wiki/Division_(digital)) work, you can even approximate the next 1/z (since deltaz is usually small) by a single successive iteration and using the previous result as a starting point.

Cb = ((CurTex and 255))*b*0.003921  '' 1/255
Cg = ((CurTex shr 8) and 255)*g*0.003921
Cr = ((CurTex shr 16) and 255)*r*0.003921

as jim pointed out already, the float-to-integer conversion is expensive, too (and the precision is not necessary).
keep the interpolated r,g,b as integers with some additional fixed-point-precision (for example multiply all values by 2^16):
Cb = ((CurTex and 255) * (b shr 16) shr 8
(but this is essentially what they invented mmx (http://www.tommesani.com/MMXPrimer.html) for)
                   
CurTex = Texture.GFX[int(ValU*Texture.Size.x)+Texture.Size.x*int(ValV*Texture.Size.y)]
Same here (interpolate in integers). You can also try to make your textures' width a power of two, so you can replace the multiply by a shift.

Quoteuse the cross-product equation to determine if the points are clockwise or counter clockwise
Rotating the vertices and doing a cross product calc is cheap [...]
The cross-product of the rotated vertices of a triangle gives you the transformed normal-vector of the face.
you can compare it to the view-vector (by a dot-product) to see if it's facing in the same direction.
In camera-space the view-vector is simply (0,0,1) so you just have to look at the sign of the z-component of your (transformed) face-normal (which you can get by just a single dot-product).
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 08, 2008
Hey :)

Shockwave : Well, sorry for the blurry source ^^ Concerning the 1/z interpolation, the point is I also tried it :/ and several times ! But I don't know, maybe I just have to start again this sub..

hellfire : Thanks for all those useful advises/optimizations, I note them all for a future optimization session :p (for now I'm stuck with the perspective correct mapping stuff).
Just a note concerning the number of divisions in the sub : It's just a thing to test the texture mapping ; once it'll work, i'll reduce them drastically :)

Thanks ! I continue my peregrinations in the world of texture mapping :p



edit : Well, I started again the Sub coding but even if I payed attention to the u/z,v/z and 1/z interpolation, there's nothing to do, the result is .. Well let's say it's still not what I was expecting :p Here is a zip with the code and the compiled stuff. So after some reflexion, here is my thoughts :

- Maybe there's a problem with the world origin (Z origin is almost on the cubes line in what you can see in the exe), I guess signs are inverting somewhere ..
- I'm interpolating u/z,v/z and 1/z exactly like I was interpolating u,v, and z (excepted the fact that calculations are based on u/z,v/z and 1/z instead of u,v and z of course) and maybe I shouldn't, I don't know.

Anyway, I'm still on it and I can't wait to start optimizing and to code the clipping with the view frustum :)

edit-edit : In the exe, you can see the floor is rotating, it's totally normal :p it's here to test the mapping.
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Jim on September 08, 2008
Code review looks OK, all except this bit

    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z

which I don't understand at all.  You need to make sure the z you have divided everything by is the z you use when you interpolate.


                ValU = (U_z/Cur_Z) '' ((U/Z)/(1/Z))
                ValV = (V_z/Cur_Z) '' ((V/Z)/(1/Z))

Good optimization, but you can still do

ooz = 1/z
ValU = U_z * ooz
ValV = V_z * ooz


Jim

Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Shockwave on September 09, 2008
Seems like you're nearly there :) Keep going!
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: hellfire on September 09, 2008
A bit offtopic but might be handy if you start using meshes with more polygons:
You don't need O(n^2) for calculating per-vertex-normals.
Here's a short snippet from a C++ source:

// set vertex-normals to 0
for (i = 0; i < numVertices; i++)
  vertices[i].normal.set(0.0, 0.0, 0.0);

for (i = 0; i < numTriangles; i++)
{
  // 3 indices of triangle i into the vertex array
  int idx1= face[i][0];
  int idx2= face[i][1];
  int idx3= face[i][2];

  // get coordinates
  const Vector& v1= vertices[ idx1 ].pos;
  const Vector& v2= vertices[ idx2 ].pos;
  const Vector& v3= vertices[ idx3 ].pos;

  // calculate normal vector
  Vector n= (v2-v1) % (v3-v1)  // cross product

  // add face-normal to all adjacent vertices
  vertices[idx1].normal+= n;
  vertices[idx2].normal+= n;
  vertices[idx3].normal+= n;

  // store the face normal
  faceNormals[i]= n.normalize();
}

// normalize the vertex-normals
for (i = 0; i < numVertices; i++)
  vertices[i].normal.normalize();

Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 09, 2008
Quote from: Jim on September 08, 2008
Code review looks OK, all except this bit

    CurZ1 += Triangle.Parent_Center.z
    CurZ2 += Triangle.Parent_Center.z

which I don't understand at all.  You need to make sure the z you have divided everything by is the z you use when you interpolate.


well in fact when I start my interpolation calcs, I use :
dZ2 = Triangle.Vertex(2).pos3d.z - Triangle.Vertex(1).pos3d.z

Even if I was using
dZ2 = (Triangle.Vertex(2).pos3d.z+TriangleCenter) - (Triangle.Vertex(1).pos3d.z+TriangleCenter)
it'd be the same result.

But I have to start interpolation on the good Z value by adding the objectCenter to it : each object vertex have their coordinates in object Space (Origin = object Center)

I tested to add triangleCenter to the vertices before calculating slopes but it's worse :S But strangely (or not?), for the 1/z interpolation, if I use 1/(z+objCenter), it doesn't work at all


Quote

                ValU = (U_z/Cur_Z) '' ((U/Z)/(1/Z))
                ValV = (V_z/Cur_Z) '' ((V/Z)/(1/Z))

Good optimization, but you can still do

ooz = 1/z
ValU = U_z * ooz
ValV = V_z * ooz


ah yeah, didn't noticed this one :p thanks


Shockwave : I'm still on it !  ;D


hellfire : ah cool :)  as far as I can understand, it calculates the normal on a vertex only 1 time instead of NbAdjacentTriangles times ?
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: hellfire on September 09, 2008
I just compiled your source, added a texture and it looks fine.
What's the problem?...
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 09, 2008
o_O well, Its way better looking for you, it's strange. Anyway, look on the cubes side, we can see a non-well-textured triangle :S

Still, I can't understand why your floor's good looking while mine is completely distorted in the middle...  you only changed your texture ? Same Size ?
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: hellfire on September 10, 2008
When I compile and run it on a different pc, the floor is black.
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Jim on September 10, 2008
My guess to that, division by 0 somewhere, or division overflow.

Jim


Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 10, 2008
- division by zero is really probable ! But since it wasn't making my computer to crash, I though the compiler was handling this. But as I see, it really depends about the computer .. grah :S

- division overflow ?

Thanks for the feedback, step by step, i'm sure i'll find where the bug comes from :)
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 10, 2008
YEEEEEYYYYYY !!!

Well I got something I think :D
I added an arbitrary number while dividing by Z :


_z1 = 1/(Triangle.Vertex(0).pos3d.z+triangle.parent_center.z+1000)
_z2 = 1/(Triangle.Vertex(1).pos3d.z+triangle.parent_center.z+1000)
_z3 = 1/(Triangle.Vertex(2).pos3d.z+triangle.parent_center.z+1000)


Since you spoke about division by 0, I expected it to be in the middle of the plane (its origin) so I added a number (here, 1000) to the divisor and it seems to work for me. The perspective mapping is still not perfect though :/ Anyway, it's way better :D

Here is a screen and a zip(code+exe+textures)



edit : Anyway, it's not a good solution since I could have large objects but far from the observer, And for those objects, I'd need to change the +1000 to a bigger value (it's a totally arbitrary number).. So in fact, i'm just moving the problem here .. I need to find a solution really resolving the problem :/
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: hellfire on September 10, 2008
Well done!

"z=0" should be at the position of the camera.
anything that's behind it will be clipped anyway and your problem is solved.
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 10, 2008
yeah I though about it but my projection equation is :

zDiv = 1/(ZOOM + obj.Triangle(i).Vertex(j).ViewPos.z)

obj.Triangle(i).Vertex(j).pos2d.x = xRes*.5 + FOV * obj.Triangle(i).Vertex(j).ViewPos.x * zDiv
obj.Triangle(i).Vertex(j).pos2d.y = yRes*.5 - FOV * obj.Triangle(i).Vertex(j).ViewPos.y * zDiv


Do you think I can get out the ZOOM term without any distortion in some big objects ?

(Well, I just tested, And geting off the zoom term is way better in fact, FOV is much more effective)

A last question about the mapping though : Did you notice there are still some texture deformations when the triangles are close ? Is it normal (an effect I can't avoid) or is there still something not working here ?

thanks again !
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Clyde on September 10, 2008
welldone dude. :)

Just something I noticed, is when you run the exe straight from the archive, without extracting everything, on certain parts of your cubes theres a cyan pixel,
Title: Re: Gouraud Filling (Update : Gouraud Shaded Textures)
Post by: Hezad on September 10, 2008
thanks :)

QuoteJust something I noticed, is when you run the exe straight from the archive, without extracting everything, on certain parts of your cubes theres a cyan pixel,

huh ? lol it's weird, I DL'ed the archive from here and I don't see any cyan pixels. But it looks like there are some surprises on different computers :S
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Shockwave on September 10, 2008
Possibly Clyde has a fucked up pixel on his monitor, I'd guess that he is using an LCD screen and it might be time to buy a new one :)

The mapping works really well by the way :)

Well done!
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 10, 2008
QuoteDid you notice there are still some texture deformations when the triangles are close?
Yes, you definatly have some serious pixelshaking going on.
I can't tell if it's because of missing precision or if there's something wrong with your interpolation - your code is a bit "cryptic" to read there ;)
But it's easy to find out: Use only a single polygon for the floor-plane (of course the shading won't work properly then).
To be honest, you should seriously clean and structure your code a bit, use meaningful names for variables, encapsulate parts and split the source-files.
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 10, 2008
Shockwave : thanks ^^ Well I hope for clyde it's a bug in the code but I hope for my code it's a dead pixel on his monitor ... Strange situation :p

hellfire : Yeah sorry about the interpolation code, I wrote it a bit like a draft and I didn't organized and/or rewritten it yet. I'll try the "single polygon for the floor-plane" :p (in fact I used several planes for the floor to tile the texture since I didn't write the tiletexture routine yet :p)

To be honest, you should seriously clean and structure your code a bit, use meaningful names for variables, encapsulate parts and split the source-files.

I totally agree -_- the point is, organizing code is my main problem ! I often get into some inextricable mess with my code !

For the 3d engine, I though about a new type : SceneType in which I could store all the objects in the current Scene, and use it to render the whole scene. (for now, I must specify in my subs which object I wanna render... not very practical). Do you think it could improve the organization/readability of my code ?

Anyway, before working on this, I have red a lot of stuff here I have to code (optimizations mainly)

*Working on the frustum clipping*


edit : Just cheated the clipping to test something, here's a preview :) aah the 20 FPS are coming back ^^
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 10, 2008
I dug out some old software-rendering code this evening to check how I did things myself back then, especially the perspective mapping (I attached a little example, you can use the mouse to rotate the object).

QuoteI though about a new type in which I could store all the objects in the current Scene, and use it to render the whole scene.
I would separate rendering and scene-management.
For the latter I use a simple tree to manage objects with hierachical-structures.
There's one base-type for all different kinds of objects (eg. camera, light, mesh, etc) which basically contains a current transformation-matrix.
Each mesh is assigned to a material-object which manages vertex-processing and rendering.
Each material has an own vertex-structure (and stores or generates all vertex-parameters, eg. texture-coordinates for environment mapping) and a polyfiller to draw the resulting polygons.
Clipping is done via Sutherland/Hodgeman in 3D Camera-Space as a template-class, so it can process different vertex-types.
To perform the actual rendering, I just have to iterate a list of materials (actually there two, one for opaque and one for transparent objects) and everything is done.

Some more technical aspects:
When performing clipping, your triangle will become a convex polygon with an arbitray number of vertices. For the beginning you'll probably just split it back into triangles.
However, most of the time your meshes will contain a reasonable number of quads (usually 3d-modelling is done by deforming grids). You should consider taking this into account in your polyfiller, so you don't have to draw two "half quads" all the time. Handling arbitrary convex polygons sounds difficult, but your polyfiller will become much more straight forward.
Here's some pseudo-code that might be helpful (based on Mats' article (http://www.exaflop.org/docs/fatmap)):

struct Vertex
{
   float x,y,z;
   float u,v;
};

void drawPoly(Vertex *vtx, int vertices)
{
   // we want to draw the polygon from top to bottom:
   // find the vertices with minimum and maximum y-coordinate
   Vertex *min_vtx = &vtx[0];
   Vertex *max_vtx = &vtx[0];
   for(int n=1; n<vertices; n++, vtx++)
   {
      if (vtx[n].y < min_vtx->y) min_vtx = vtx;
      if (vtx[n].y > max_vtx->y) max_vtx = vtx;
   }

   int min_y = ceil(min_vtx->y);
   int max_y = ceil(max_vtx->y);

   // calculate deltas (on first triangle)
   float delta1=  vtx[1].y - vtx[2].y;
   float delta2=  vtx[0].y - vtx[2].y;
   float d =  1.0 / ((vtx[0].x - vtx[2].x) * delta1 - (vtx[1].x - vtx[2].x) * delta2);
   delta1*= d;
   delta2*= d;
   float deltau=  (vtx[0].u - vtx[2].u)*delta1 - (vtx[1].u - vtx[2].u)*delta2;
   float deltav=  (vtx[0].v - vtx[2].v)*delta1 - (vtx[1].v - vtx[2].v)*delta2;
   float deltaz=  (vtx[0].z - vtx[2].z)*delta1 - (vtx[1].z - vtx[2].z)*delta2;

   // we are now tracing the left and right sides of the polygon

   // initially start both sides at the top vertex
   Vertex *left_vtx = min_vtx;
   Vertex *right_vtx = min_vtx;

   // the height of the left and right slopes (initially 0, forces calculation when entering the loop)
   int right_height=0;
   int left_height=0;

   // left and right slopes
   float left_x, left_dx; // x-coordinate
   float left_z, left_dz; // z-coordinate
   float left_u, left_du; // texture U
   float left_v, left_dv; // texture V

   float right_x, right_dx;

   // draw polygon top->bottom
   // the loop isn't really required here since we terminate in the slope-calculation when reaching the bottom-vertex.
   for(int y=min_y; y<max_y; y++)
   {
      // at the end of right slope: calculate next one
      while (left_height <= 0)
      {
         // at the bottom: done.
         if(left_vtx == max_vtx) return;

         // the polygon is expected to be counter-clockwise
         // starting from the top, the left side has increasing vertices, right side has decreasing vertices
         Vertex *v1= left_vtx;
         left_vtx++;
         // if we're at the end of the vertex-list, wrap to the front
         if (left_vtx >= vtx+vertices) left_vtx= vtx;
         Vertex *v2= left_vtx;

         // the actual number of scanlines for this slope
         left_height = ceil(v2->y) - ceil(v1->y);

         // can be 0 which means the next vertex lies in the same scanline: go straight to the next slope
         if (left_height == 0)
            continue;

         // calculate slope v1 -> v2
         // this is the delta for all vertex-attributes to get to the enxt scanline
         float inv_height= 1.0 / (v2->y - v1->y);
         left_dx = (v2->x - v1->x) * inv_height;
         left_dz = (v2->z - v1->z) * inv_height;
         left_du = (v2->u - v1->u) * inv_height;
         left_dv = (v2->v - v1->v) * inv_height;

         // subpixel-correction:
         // the vertex' y-coordinate is some floating-point number, while the scanline is at an integer y-coordinate (rounded up)
         // so we are already a bit further on the y-axis and the vertex-attributes should be updated accordingly:
         // add a fraction of the y-delta to compensate
         float prestep= ceil(v1->y) - v1->y;
         left_x = v1->x + prestep * left_dx;
         left_z = v1->z + prestep * left_dz;
         left_u = v1->u + prestep * left_du;
         left_v = v1->v + prestep * left_dv;
      }

      // right slope: same procedure
      while (right_height <= 0)
      {
         if(right_vtx == max_vtx) return;

         // walk backwards through the vertex array
         Vertex *v1= right_vtx;
         right_vtx--;
         if(right_vtx < vtx) right_vtx= vtx+vertices-1;
         Vertex *v2= right_vtx;

         right_height = ceil(v2->y) - ceil(v1->y);
         if (right_height == 0)
            continue;

         float inv_height= 1.0 / (v2->y - v1->y);
         right_dx = (v2->x - v1->x) * inv_height;

         float prestep = ceil(v1->y) - v1->y;
         right_x = v1->x + prestep * right_dx;
      }

      // round x-coordinates to whole pixels
      int x1 = ceil(left_x);
      int x2 = ceil(right_x);

      // subtexel correction:
      // since we rounded to the next integer coordinate, we are already a bit further on the x-axis
      // update attributes accordingly:
      float prestep = x1 - left_x;
      float u = left_u + (prestep * deltau);
      float v = left_v + (prestep * deltav);
      float z = left_z + (prestep * deltaz);

      // draw the scanline:
      for (int x=x1;x<x2;x++)
      {
         // ...
         u+=deltau;
         v+=deltav;
         z+=deltaz;
      }

      // interpolate left and right slopes
      left_height--;
      left_x += left_dx;
      left_u += left_du;
      left_v += left_dv;
      left_z += left_dz;

      right_height--;
      right_x += right_dx;
   }
}

Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 11, 2008
hey nice cube :D Thanks a lot for the code  :clap: I'm reading it right now :) I haven't a lot of C/C++ knowledge but I should understand the code after several readings. But there's something I don't understand :
AFAIK, Sutherland/Hodgman algorithm works on 2d polygons, so I'll need to project all polygons before using it, no ? So, should I code a quick clip with the view frustum (getting off the list all polys lying totally outside the view) and THEN, using the sutherland/hodgman algo on the remaining triangles to process the ones which are ON the frustum ( eg: two vertices inside, another one outside) ?

Sorry for asking so many questions ;o but before attacking optimizations and code organization with all the tips I red there, I'd like to have a good clipping code.

Oh and while I'm there, my perspective correct mapping is definitely not as correct as I'd like it to be .. The mapping on cubes is totally ok, but the one on the floor really looks like an affine texture mapping in fact (however, it's exactly the same sub used for the cubes and the floor ... strange o_O)

So my current ToDo list :
- Fixing the floor mapping bug
- Coding clipping

Future todo list :
- Optimize code/reorganize it
- TileTexture routine
- Mip-Mapping
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 11, 2008
Sutherland/Hodgeman is usually applied in 2D-Coordinates, but it works just as well in 3D-Coordinates.
What I do is to transform all vertices into camera space, perform backface-culling and clip all remaining polygons to the view-frustum; in camera space this is extremely easy:
What's still left transformation-wise is just the perspective division:
x'= x / z
y'= y / z

Coordinates (x', y') in the range -1..+1 are inside the view frustum; thus:
x > z : out on right
-x> z: out on left
y > z: out on bottom
-y> z: out on top
near > z: out on front
This resembles the clip-flags typically used by Sutherland/Hodgeman and is actually cheaper than performing clipping previous to transformation (which results in an additional dot-product per clipping-plane).

Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Jim on September 11, 2008
Division by 0...

1/0 = +infinity
-1/0 = -infinity
in fact
p/0 = +infinity
-p/0 = -infinity

Once you get an infinity in the fpu things will screw up.  Happens when your z is 0.  Often you can see if this is the case by adding
asm { finit }
to the inner loop in your code.  That resets the fpu and clears up the error, but it's slow, and you don't want to do this.

Division overflow...

With integers this can be a big problem...when you divide a 64bit number by a 32bit number, the result is expected to fit in 32bits, so...
00000001:00000000/1 is a division overflow, because the results is 00000001:00000000 which is over 32 bits long.
With float, the problem can still occur.   It happens when the z is very near 0 but the x or y are large, so

z = 0.000000001 x = 10000, y = 10000.
That would give x/z = 10000000000000000, which is possible to represent as a float, but probably not what you're expecting in your calcs.  And if you fed that to your triangle routine as an x coordinate you're going to take a very long time to draw a span!!

So, as hellfire has been trying to push you, you need to look at (at the very least) cohen sutherland hodgeman clipping to clip your polygons at z= some sensible value like 1 (near plane clip), and then to 3d clip (or at least 2d clip) after that.  That keeps the x,y,z in a sensible range, z will never be 0 or tiny, and you avoid all the the precision and range problems :)

Jim

PS, Yes, ZOOM should be removed from your perspective calc.  It has no real life analogy, it's a fudge to get round the above problems.
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 14, 2008
Thanks for those informations :) I got it now ! Sorry for the time taken before responding but I wanted to finish clipping before posting a new message. But erm, since it's will take more time I expected, the less I can do is answering ! Just for info, what I got for the "clipping order" :

- First clipping (What poly is totally inside the view frustum ?)
- Backface culling on those polys (not implemented yet)
- Sutherland/hodgeman clipping for the triangles crossing the frustum (I'm pulling my hair out on this :p)


k++ to shockwave, hellfire and Jim. Thanks a lot !
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 14, 2008
Quoteclipping [...] I'm pulling my hair out on this :p
Actually it's quite easy ;)
Let's assume your vertices are already transformed into camera-space and all that's left is perspective division.
You first check for all vertices if it's outside of any planes of the view frustum and store the result in a bitmask.
As described above, the vertex/plane-test is simple in camera-space:

   #define CLIP_TOP 1
   #define CLIP_LEFT 2
   #define CLIP_RIGHT 4
   #define CLIP_BOTTOM 8
   #define CLIP_FRONT 16
   #define CLIP_BACK 32

   clip=0
   if -x>z then clip = clip or CLIP_LEFT
   if x>z then clip= clip or CLIP_RIGHT
   if y>z then clip= clip or CLIP_BOTTOM
   if -y>z then clip= clip or CLIP_TOP
   if z<znear then clip= clip or CLIP_FRONT

Note: I assume the negative y-axis going up
"And'ing" the clipflags of all vertices of a polygon tells if it's completely outside of any clip-plane (result is non-zero: skip it).
"Or'ing" the clipflags tells if the polygon needs any clipping at all (result is zero: draw it without clipping)

Now you go through the vertices of your polygon and check each edge (adjacent pair of vertices) if they are on different sides of a frustum-plane:
(I was too lazy to convert the syntax to freebasic)

   // dst: list of pointers to output vertices
   // src: list of pointers to input vertices
   // srcNum: number of input vertices
   int clipLeft(Vertex **dst, Vertex **src, int srcNum)
   {
      Vertex     *v1, *v2;
      int        n,num=0;
      int        c1,c2;

      v1= src[srcNum-1]; // start with the last vertex so we don't have to handle array-wrap
      for (n=0;n<srcNum;n++) // go through the whole polygon-array
      {
         v2= src[n]; // second vertex to check
       
         // extract the clip-information for the current plane (here: left)
         c1= v1->clip & CLIP_LEFT;
         c2= v2->clip & CLIP_LEFT;

         // if v1 is inside the frustum, just make a copy (pointer: cheap)
         if (c1==0) dst[num++]= v1;

         // if the two vertices are on different sides, create a new vertex on the plane
         if (c1!=c2)
         {
            // v'=v1+t*(v2-v1) intersects the clip-plane:
            // here: -v'.x=v'.z (see clipflags)
            float t= (v1->x + v1->z) / (v1->z + v1->x - v2->x - v2->z)
            // linear interpolation
            *scratch= v1 + t*(v2-v1);
            // update clipmask - remember that only the remaining and previously-set clip-planes are necessary to be checked
            scratch->clip= scratch->clipmask();

            dst[num++]= scratch++; // add to output (and point "scratch" to the next free vertex)
         }

         v1= v2; // next edge
      }

      return num; // number of output-vertices
   }

the output-array "dst" just stores pointers to the actual vertices because most of them just get copied and we just have to store the pointer then (some linked-list might work well, too).
"scratch" is a pool of temporary vertices so we don't have to allocate new memory (which is costly).
Just repeat the process for every clipping-plane. You just have to work out the edge/plane intersection-points (parameter "t"):
right: v.x = v.z
bottom: v.y= v.z
top: -v.y= v.z

For the resulting vertices all that's left to do is perspective transformation:

   z= 1.0 / z
   x= x*z
   y= y*z
   // the clipping-process ensured that all x,y are now in a range of -1..+1
   // we just have to scale them to the screen-dimensions:
   x= (1.0 + x) * xres * 0.5
   y= (1.0 + y) * yres * 0.5

Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 14, 2008
hey :)

QuoteYou first check for all vertices if it's outside of any planes of the view frustum and store the result in a bitmask.
As described above, the vertex/plane-test is simple in camera-space:

The method with bitmask is really enjoyable, I guess I'll use it ! Thanks :)

QuoteNow you go through the vertices of your polygon and check each edge (adjacent pair of vertices) if they are on different sides of a frustum-plane:
(I was too lazy to convert the syntax to freebasic)

The code is self explaining, even in .. C++ I guess ? Well, it will be more than useful for me, k++ to you for that !

by the way, Won't there be any problem for the triangles crossing 2 or 3 planes at the same time ? (eg : A very big poly crossing Left, ZFar and Up planes)
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 14, 2008
QuoteWon't there be any problem for the triangles crossing 2 or 3 planes at the same time ?
No, because you will always clip against one plane at a time.
In the "worst" case you clip so much off on one side that the polygon doesn't overlap a neighbouring side anymore.
So it's just important to recheck the flags after clipping.

Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 15, 2008
Well okay, so I have work to do now ^^

thanks again !
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 15, 2008
Just a little message while i'm working on it :

I just implemented the clipping part without chopping new triangles. I was expecting nothing special when I ran it to see if there were any problem ...

I have between 18 and 24 FPS !!! :updance:

(yeah it's good news for me ^^ I had between 1 and 9 FPS when testing clipping before hellfire's snippets). And I didn't even implemented the triangles chopping nor the backface culling :p

On those words, I return to FBide ! :D

thanks !
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Shockwave on September 15, 2008
You should be able to get some really useful framerates when you are further along with the backface culling, and expect another speed up when you impliment mip maps too, not to mention a big improvement in image quality.
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 17, 2008
Shockwave > Well, You won't see this message soon since you may not come here for some time 'cause of your leg but anyway, I have to finish the clipping part and the optimizations in code. And then, I'll work on mip-mapping :)


hellfire > Just saw the updates you made in your post about clipping, thanks :) But even if I really understood how to get the new vertices thanks to your code (I even implemented it), I don't see how I will "transform" those vertices into triangles. In the interpolation part of the clipping, I interpolated U and V and 1/Z coordinates too so the filling part can work. But I don't know how to select 3 good vertices to build a new triangle :/

I tried a naive way :
Newtriangle=( newvert(i),newvert(i+1),newvert(i+2) )
but of course it don't work.

If you need to see the code, I'll post it :)

thanks again for your implication.
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 17, 2008
Let's assume you have some mesh (vertices in random order):

0     1
+---+
|\  |
| \ |
|  \|
+---+
3     2


And a list of indices building triangles from the vertcies:
2,0,3 1,0,2
The order of the indices is counter-clockwise for all triangles (or the other way round, doesn't matter).

Since you use the same order to traverse the edges in the clipping-process, the resulting polygon is defined in counter-clockwise order, too:

     0 +
      / \
     /   \
  1 +     + 5
    |     |
  2 +     |
     \    |
    3 +---+ 4


A possible (no-overlapping) triangulation would be:
0,1,2  0,2,3  0,3,4  0,4,5
Does that already look like an algorithm? :)

for i = 0 to numVertices-2
  drawTriangle v(0), v(i), v(i+1)

This always works as long as your polygon is convex (which it is).

So you were pretty close actually :D
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 18, 2008
Hey :)

QuoteDoes that already look like an algorithm? Smiley

hell yeah ^^

Well, I putted all this in my mind and I coded ... well, erm..


the point is ...


Well, It seems like it don't work lol

I set down the FOV to see where the triangles were chopped but it seems like they are not at all chopped :/ However, the triangles Count is much higher so there do are chopped triangles (much more : ~400 without chopping and 800/900 when chopped o_O)

I join the code + exe, you can move the camera with keyboard arrows. Once again, the perspective is deformed but it's normal, I set down the FOV to see the "borders" of the clipping.

I guess there is a problem in my perspective transformation so the x/z clipping doesn't work ?

here is just the perspective transformation routine :

'' projection
        for j as integer = 0 to 2

            obj.Triangle(i).Vertex(j).ViewPos.x = obj.Center.x + obj.Triangle(i).Vertex(j).pos3d.x - Camera.pos.x
            obj.Triangle(i).Vertex(j).ViewPos.y = obj.Center.y + obj.Triangle(i).Vertex(j).pos3d.y - Camera.pos.y
            obj.Triangle(i).Vertex(j).ViewPos.z = obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z - Camera.pos.z


            zDiv = FOV/(TriangleList(i).Vertex(j).ViewPos.z)
                       
            TriangleList(i).Vertex(j).pos2d.x = xRes*.5 + TriangleList(i).Vertex(j).ViewPos.x * zDiv
            TriangleList(i).Vertex(j).pos2d.y = yRes*.5 - TriangleList(i).Vertex(j).ViewPos.y * zDiv
        next


Some posts ago, you spoke about [-1;1] coords after Camera space transformation (Which is sorta the Vertex.ViewPos = triangle.center+vertex-Camera stuff you see in the code above, I have not implemented the camera rotation yet) but in my code, it's not in [-1;1] ..

In fact I'm starting to wonder seriously if I'm not totally lost between the camera space transformation, the 2d projection and the clipping ..

Of course, I'm not asking for you to look at the code in the zip, it's just to give some news. Still, if you see something wrong in the perspective code I posted, please tell me :)

thanks again .. and sorry for all those repeating questions :/

ps : Anyway, If I really don't manage to handle all those problems (Clipping, perspective correct mapping which still don't work, Code not well organized, ..), I may start again from scratch and force myself to use matrices transformation and organize way better the code.
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 18, 2008
There's a logical error in your "Clip" function.
The principle is to take the input polygon (triangle) and clip it against the first clipping plane.
This results in a new polygon (vertices stored in "DestList").
Then you take the *new* (partially clipped) polygon and clip it against the next plane (and so on).

At the moment you're always clipping the *source* triangle and somehow collect all resulting vertices in "DestList".
Doesn't really make sense.

Your usage of "FOV" is a bit obscure to me.
You should try to understand that the field-of-view (commonly known as "zoom") is nothing else than a 2D (x,y) scaling in camera-space.
I expect that this scaling (including aspect-ratio, too) is already applied (contained in the transformation-matrix) when entering clipping. The clipflag-test assumes that the frumtum-planes are at x=z / y=z:
I'm not transforming the view-frustum according to the fov (which would result in arbitrary clipping planes and a more expensive clip-test). Instead I use a default frustum (which matches a fov of 90 degrees) and transform the vertices accordingly (they need to be transformed anyway).
I recommend to completely remove "FOV" and simply integrate it into the transformation:

            obj.Triangle(i).Vertex(j).ViewPos.x = (obj.Center.x + obj.Triangle(i).Vertex(j).pos3d.x - Camera.pos.x) * zoom
            obj.Triangle(i).Vertex(j).ViewPos.y = (obj.Center.y + obj.Triangle(i).Vertex(j).pos3d.y - Camera.pos.y) * zoom * aspectratio
            obj.Triangle(i).Vertex(j).ViewPos.z = (obj.Center.z + obj.Triangle(i).Vertex(j).pos3d.z - Camera.pos.z)


Since you clipped your polygons in normalized space (-1..+1), you must also scale them to screen-size when performing perspective transformation:
            zDiv = 1.0 / TriangleList(i).Vertex(j).ViewPos.z
            TriangleList(i).Vertex(j).pos2d.x = xRes*.5 + TriangleList(i).Vertex(j).ViewPos.x * zDiv * xRes*0.5
            TriangleList(i).Vertex(j).pos2d.y = yRes*.5 - TriangleList(i).Vertex(j).ViewPos.y * zDiv * yRes*0.5

Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 20, 2008
hey :)

ok, I see the point, I changed the FOV in zoom and aspect ratio and "putted" them in the camera space transformation. I understood the point with the logical error too. big k++ to you for all your help !! I continue to work on it and I'll post stuff once there'll be new stuff :)

thanks again !!
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Shockwave on September 22, 2008
It may not be there yet Hezad but the speed increase already is nice, it was running at around 20fps before, it's not at about 36fps (default zoom), it gets much faster if I zoom it out and crashes if I zoom it in ;)
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 22, 2008
hehe cool for the speed increase :P And 'glad to see you back shockwave :D

Some news : I'm almost there !!

After days of trying to find a bug making my program to continue to crash, I think I got something ! Now the program don't crash anymore, and it even looks like the Left plane clipping works perfectly. Here is the program if you wanna test (You can still move with the keyboard arrows) :D

Well, there are still glitches with the bottom, right and top clipping .. But I'm glad, I thought I were really stuck here :p



EDIT :
Another good new !! I fixed the Perspective correct mapping bug, now the texture mapping works perfectly :D It looks ... so much better hehe ! The attached file is a version without this correction, i'll post this version later. Just have to finish the clipping, to code the backface culling and to optimize/reorganize a bit the code and then ... Mip Mapping !  :D  (or maybe the camera handling before :p)
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 22, 2008
You're really making great progress!
I'm just wondering why you sometimes start your arrays at index 0 and sometimes at index 1 ?
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 22, 2008
uhuh I edited my message at the same time ^^

thanks !

Concerning the arrays, some are starting at index 1 because I used an iteration variable the fill the array which starts to be incremented before the assigning values to the array.

eg : Iter+=1 : Array(iter) = stuff

so the array is used from 1 instead of 0 :P
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: hellfire on September 22, 2008
Quotesome are starting at index 1
and you don't find that confusing?
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 22, 2008
Well ... lol not really, I'm pretty habituated, I do it all the time (yeah it's not a good habit :s ..)

hum.. But it'd be a good thing to change this habit .. let's start right now :p
Title: Re: Gouraud Filling (Update : Perspective correct mapping)
Post by: Hezad on September 24, 2008
Hi again folks :)

well, I know capital letters are not really welcome on boards but .. this time it's so awesome ..


CLIPPING IS NOW FULLY WORKING !!!! YEYHOO !

Dudes, I don't know how many hours I spent to find what wasn't working, I was almost desperate about it ^^ So it is really, really a liberation to see those cubes moving without any glitches and at ~15 FPS  :updance:  :updance:  :updance:

Oh and like I said in a previous recent post, I found what wasn't working in perspective mapping too ! So The clipping works, but the perspective correct texture mapping also works perfectly (on my computer though .. your turn to say me if it works on yours :D )

Once again, thanks A LOT to hellfire who spent a lot of time to help me with clipping.

Title: Re: WIP 3D Engine (Update : Clipping + Perspective Mapping fully working)
Post by: hellfire on September 24, 2008
Well done! (http://www.c-plusplus.de/forum/images/smiles/live.gif)
Runs at ~30fps (Intel Core2 3Ghz) and ~20fps (Core2 2Ghz), I guess you can easily make it 60 without even touching assembler.
But before you get your hands dirty in optimization, please take some time to think about conception and class design.
Title: Re: WIP 3D Engine (Update : Clipping + Perspective Mapping fully working)
Post by: Hotshot on September 24, 2008
look great....run 25FPS ( mine is E6750)  The checkboard look great.

welldone

:cheers:
Title: Re: WIP 3D Engine (Update : Clipping + Perspective Mapping fully working)
Post by: Hezad on September 24, 2008
hellfire :
thanks :D So what would you advise me ? Using kind of a pipeline ?

eg :

- Objects Declaration (User Side)
- Objects Characteristics [texture, shader, rotation, translations] (User Side)
- Store everything in a Global Render List (Engine side)
- Calculate Shaders (Engine Side)
- Calculate Clipping + Projection (Engine Side)
- Render

and/or do you speak about totally changing the way I use my UDT's ?
Concerning the modularization, I thought about putting all engine subs in a .bi file and putting all the objects creation helping subs in another one.

I remember you spoke about materials but I thought I can create functions that create textures from procedural materials and a special type which could handle materials layers handling
e.g. in pseudo code :


Type Single_Material
   Tex as Texture
   Opacity as integer
   MapType as integer       '' (BumpMap, TextureMap, ColorMap, etc?)
End Type

Type Material
   Layers(NBLAYERS) as Single_Material
End type

CurrentObject = Apply_Material(Current_Material)



And then using the MapType variable before rendering to know which kind of Material to calculate.

Don't know if it's a good idea though :p


HotShot :

thanks :D
Title: Re: WIP 3D Engine (Update : Clipping + Perspective Mapping fully working)
Post by: Shockwave on September 24, 2008
Works really nice here too, no clipping bugs at all.

When Hellfire talks about classes he does mean types, yep. I will suggest once you have that implimented that you add backface culling and mip mapping which will give you a further speed boost.

It looks bloody good thought Hezad.

Fair play to you.
Title: Re: WIP 3D Engine (Update : Clipping + Perspective Mapping fully working)
Post by: hellfire on September 24, 2008
Some notes:

In software-rendering, poly- and pixel-throughput are precious, so you'll usually have many special-case-optimized polyfillers.
In my renderer I used a "Material" class which creates all required vertex-data from a mesh and contains the polygon-rasterization-code.

The 3D-models (or complete scenes including animation) are usually loaded from files (exported from your favourite 3d-package).
For testing & debugging reasons my "Mesh" class has some static methods to create some simple objects like cube, cylinder, sphere or torus:
Mesh *mesh= Mesh::createTorus(100, 40);
Here "Mesh" is one of several object-types (all derived from one base-class) that are stored in a scene-tree (the hierarchical structure might not be required for you yet) and basically contains all information that can be applied by the 3d-software.
To get things rendering, I just need to assign it to a material:
Material *mat= new TextureGouraud(scenetree, Texture("texturefilename") );
mat->addMesh(mesh);

Here "Texture" can either be a bitmap loaded from a file or something procedurally generated (it just needs to derive from the same interface-class).

For rendering I just parse the material-list (stored in the scene-tree) and draw everything that got assigned.
The material has a "transform"-method to put the object where it belongs (multiply vertices by mesh' and camera's matrix) and to create all vertex-attributes; here: calculating rgb-values from vertexnormals and lights (which are also stored in the scene-tree, accessible by the material).
When that's done, polygon's are culled, clipped (each material has a vertex-class with a method for interpolation) and drawn.

For animation, each object has several animation-tracks which store position, rotation, scaling at certain timestamps (and interpolate in between).
"Procedural" animation (like deforming vertices) is job of the "Material", too (since it resambles the vertex-shader part).
Other approaches would just manually updated the mesh' transformation-matrix.

QuoteWhen Hellfire talks about classes he does mean types
True. "Classes" were mentioned a couple of times in the freebasic documentation, but I just realized they don't exist: :-\
QuoteThis keyword is supported in the latest fbc compiler, however, its usefulness is limited since classes and inheritance are not yet supported.
This is a knockout for most structured design strategies...



Title: Re: WIP 3D Engine (Update : Clipping + Perspective Mapping fully working)
Post by: Hezad on September 25, 2008
Hey :)

Shockwave :
QuoteWorks really nice here too, no clipping bugs at all.

When Hellfire talks about classes he does mean types, yep. I will suggest once you have that implimented that you add backface culling and mip mapping which will give you a further speed boost.

Glad to hear it works :) Backface culling was implemented just after the previous release :) Mip-Mapping is on the todo list, right after the pure code optimization part :) I'll start optimization with using Integers instead of singles most of the time. Then, I'll read once again all the thread to remind myself stuff people told me about optimization but forgotten ;D and then, MIPMAPPING :D

Quote
It looks bloody good thought Hezad.
thanks a lot shockwave  :cheers:



hellfire :

QuoteIn software-rendering, poly- and pixel-throughput are precious, so you'll usually have many special-case-optimized polyfillers.
In my renderer I used a "Material" class which creates all required vertex-data from a mesh and contains the polygon-rasterization-code.

The 3D-models (or complete scenes including animation) are usually loaded from files (exported from your favourite 3d-package).
For testing & debugging reasons my "Mesh" class has some static methods to create some simple objects like cube, cylinder, sphere or torus:
Code:

Mesh *mesh= Mesh::createTorus(100, 40);

Here "Mesh" is one of several object-types (all derived from one base-class) that are stored in a scene-tree (the hierarchical structure might not be required for you yet) and basically contains all information that can be applied by the 3d-software.
To get things rendering, I just need to assign it to a material:
Code:

Material *mat= new TextureGouraud(scenetree, Texture("texturefilename") );
mat->addMesh(mesh);

Here "Texture" can either be a bitmap loaded from a file or something procedurally generated (it just needs to derive from the same interface-class).


Thanks for that, I just implemented a start for a material UDT which I can edit in a similar way of OpenGL surface creation (I'll put here an example at the end of the post). Added a naive bmp loader for textures too. I also replaced the objects creation functions with Object Type subs.


QuoteFor animation, each object has several animation-tracks which store position, rotation, scaling at certain timestamps (and interpolate in between).
"Procedural" animation (like deforming vertices) is job of the "Material", too (since it resambles the vertex-shader part).
Other approaches would just manually updated the mesh' transformation-matrix.

Animation is not planned for now :p But I didn't know I can use materials to implement it :)



QuoteQuote
When Hellfire talks about classes he does mean types
True. "Classes" were mentioned a couple of times in the freebasic documentation, but I just realized they don't exist: Undecided

Badly true :P But Freebasic's UDTs permits already some interesting stuff (Type Subs/functions, Constructors, destructors, properties, Operators), it's totally sufficient for what I wanna do now :)

QuoteThis keyword is supported in the latest fbc compiler, however, its usefulness is limited since classes and inheritance are not yet supported.
This is a knockout for most structured design strategies...

Well I guess so, but since I don't really know all classes stuff, UDT's are fine for me now ^^




Engine News :

Before starting optimizations,  I wanted to modularize and reorganize a bit my code to help comprehension. So here are the news :

- Modularization : code broken into smaller header files (Engine.bi, DataTypes.bi, Materials.bi)
- Texture BMP loader
- Backface Culling
- /!\ New material Type to handle .. well, materials :p
        + Effective way (I hope) to create/edit materials in code
- Texture tiling routine
- Changed textures of the example for realistic ones


An example of material editing :




Dim as ObjectT MyObject

MyObject.GenerateCube(x0,y0,z0,SideSize)

Dim as TextureT CubeTex

CubeTex.LoadBMP "Texture_Cube.bmp"


Dim as MaterialT CubeMaterial

CubeMaterial.Edit
   
    Mat_Shader GOURAUD_SHADER
   
    Mat_Texture CubeTex,tileX,tileY

    Mat_Diffuse r,g,b

    Mat_ambient r,g,b

CubeMaterial.End_Edit

MyObject.Assign_Material CubeMaterial



And a screenshot with the new textures attached
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: hellfire on September 25, 2008
Everything sounds plausible, I'm just wondering what this does:
Quote- Texture tiling routine
Usually you would make sure your texture-width/height is a power of two, scale your uv-coordinates and  and mask the appropriate bits ("and" instead of modulo).

Quotea material UDT which I can edit in a similar way of OpenGL
It's a good idea to follow standards, but remember that OpenGL's material-model was build around sgi's hardware-capabilities and is sometimes rather unflexible and you'll often want to take some shortcuts in order of optimization.
The important point is to offer the relevant features required to achieve certain effects.
If you want to get some ideas about how to use materials & lighting professionally, I can recommend this book:
(http://pixhost.eu/avaxhome/gfxworld/2006-12-06/1562059548.jpg) (http://www.3drender.com/light/index.html)
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Hezad on September 25, 2008
QuoteEverything sounds plausible, I'm just wondering what this does:
Quote
- Texture tiling routine
Usually you would make sure your texture-width/height is a power of two, scale your uv-coordinates and  and mask the appropriate bits ("and" instead of modulo).

I multiply (U,V) values of each vertex by NbTileX and NbTileY and then, while rendering, I check if final(u,v) is > 1. If it's the case, I do this :


if ValU>1 then ValU = ValU-int(ValU)
if ValU<0 then ValU=0
if ValV>1 then ValV = ValV-int(ValV)
if ValV<0 then ValV=0


It gives me ValU after int(ValU) repetitions.


QuoteIt's a good idea to follow standards, but remember that OpenGL's material-model was build around sgi's hardware-capabilities and is sometimes rather unflexible and you'll often want to take some shortcuts in order of optimization.
The important point is to offer the relevant features required to achieve certain effects.

Well, in fact I asked myself "what would I like to do for editing a material when coding with a 3d library ?" and I thought about this way. I have to admit I really didn't think about standards, This engine is not supposed to follow the standards since it surely won't be used excepted by myself ^^

QuoteIf you want to get some ideas about how to use materials & lighting professionally, I can recommend this book:
Some ideas, yep ! But I really don't want to code a professional product, I'd be incapable of doing that :p Just a personal learning tool and -hopefully- a nice gadget tool for people demos and/or code wanderings in the future :) But if I plan to buy a 3d book, I'll firstly look for this one :)
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Shockwave on September 26, 2008
Quote from: Hezad on September 25, 2008
QuoteIf you want to get some ideas about how to use materials & lighting professionally, I can recommend this book:
Some ideas, yep ! But I really don't want to code a professional product, I'd be incapable of doing that :p
I don't know about that.. All you need to do is what you've done. Break it down into bits and work on it a bit at a time, even appreciating that it's not a professional product you would surely find some useful things in that book to help you improve the engine even more :)

Quote from: Hezad on September 25, 2008
But if I plan to buy a 3d book, I'll firstly look for this one :)

Enter a competition and win it and I'll buy the book for you :)
The prize fund for the remakes comp is £50 because it's joint sponsored by me and Rbz :) Normally they are half that amount.
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Hezad on September 26, 2008
Quoteeven appreciating that it's not a professional product you would surely find some useful things in that book to help you improve the engine even more Smiley

Yeah i'm sure about it :) I just wanted to underline the fact that my objectives are not professionals at all : They are just "let's make something beautiful (hopefully) and functional".

It doesn't change the fact this book looks bloody interesting :P


QuoteEnter a competition and win it and I'll buy the book for you Smiley
The prize fund for the remakes comp is £50 because it's joint sponsored by me and Rbz Smiley Normally they are half that amount.

:D Neat idea ! I'm not sure I will enter the comp since I don't have a good demo-scene culture, but i'll look for demos not remade yet ; maybe there will be one I want to work on ^^
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Hezad on September 27, 2008
Hey :) It's me again !

I'm trying to write a GenerateSphere Procedure ; having a curved surface is pretty useful for testing shaders/lighting ^^ (yeah, I wanted to relax a bit before entering the frightening world of optimization :D)

The routine works almost well : the sphere shape is well generated, the U,V values too. But there's a problem with the shading ! I use the Gouraud shader and some parts are ok, but some other parts looks like it was shaded with a flat shader :S or worse : it can look like the vertices color are inverted on some triangles (a dark vertice becomes bright and vice versa). Here is a screenshot to illustrate what I'm saying.

Here is the procedure :

Sub ObjectT.GenerateSphere(x0 as integer, y0 as integer, z0 as integer,Radius as integer,nbfaces as integer)
       
        '' x = r sin(theta) cos(phi)
        '' z = r sin(theta) sin(phi)
        '' y = r cos(theta)

        this.Center.x = x0
        this.Center.y = y0
        this.Center.z = z0
       
        dim as single dTheta, dPhi,UAng,Vang,UAng2,Vang2
        dim as integer ActualTri = 1
       
        dTheta = PI/nbFaces : dPhi = PIx2/nbFaces
       
        for theta as single = 0 to PI step dTheta
            for Phi as single = 0 to PIx2 step dPhi
               
                Uang = phi/PIx2
                Vang = Theta/PI
                Uang2 = (phi+dPhi)/PIx2
                Vang2 = (Theta+dTheta)/PI
               
                if theta<>0 and theta<>PI then
                    triangle(ActualTri).Vertex(0).pos3d.x = Radius * sin(theta) * cos(Phi)
                    triangle(ActualTri).Vertex(0).pos3d.y = Radius * cos(theta)
                    triangle(ActualTri).Vertex(0).pos3d.z = Radius * sin(theta) * sin(phi)
                    triangle(ActualTri).Vertex(0).U = Uang
                    triangle(ActualTri).Vertex(0).V = Vang
                   
                    triangle(ActualTri).Vertex(1).pos3d.x = Radius * sin(theta+dTheta) * cos(Phi)
                    triangle(ActualTri).Vertex(1).pos3d.y = Radius * cos(theta+dTheta)
                    triangle(ActualTri).Vertex(1).pos3d.z = Radius * sin(theta+dTheta) * sin(phi)
                    triangle(ActualTri).Vertex(1).U = Uang
                    triangle(ActualTri).Vertex(1).V = Vang2
                   
                    triangle(ActualTri).Vertex(2).pos3d.x = Radius * sin(theta) * cos(Phi+dPhi)
                    triangle(ActualTri).Vertex(2).pos3d.y = Radius * cos(theta)
                    triangle(ActualTri).Vertex(2).pos3d.z = Radius * sin(theta) * sin(phi+dPhi)
                    triangle(ActualTri).Vertex(2).U = Uang2
                    triangle(ActualTri).Vertex(2).V = Vang
                   
                end if
               
                ActualTri +=1
               
                If Phi <> PIx2 then
                    triangle(ActualTri).Vertex(0).pos3d.x = Radius * sin(theta) * cos(Phi+dPhi)
                    triangle(ActualTri).Vertex(0).pos3d.y = Radius * cos(theta)
                    triangle(ActualTri).Vertex(0).pos3d.z = Radius * sin(theta) * sin(phi+dPhi)
                    triangle(ActualTri).Vertex(0).U = Uang2
                    triangle(ActualTri).Vertex(0).V = Vang
                   
                    triangle(ActualTri).Vertex(1).pos3d.x = Radius * sin(theta+dTheta) * cos(Phi)
                    triangle(ActualTri).Vertex(1).pos3d.y = Radius * cos(theta+dTheta)
                    triangle(ActualTri).Vertex(1).pos3d.z = Radius * sin(theta+dTheta) * sin(phi)
                    triangle(ActualTri).Vertex(1).U = Uang
                    triangle(ActualTri).Vertex(1).V = Vang2
                   
                    triangle(ActualTri).Vertex(2).pos3d.x = Radius * sin(theta+dTheta) * cos(Phi+dPhi)
                    triangle(ActualTri).Vertex(2).pos3d.y = Radius * cos(theta+dTheta)
                    triangle(ActualTri).Vertex(2).pos3d.z = Radius * sin(theta+dTheta) * sin(phi+dPhi)
                    triangle(ActualTri).Vertex(2).U = Uang2
                    triangle(ActualTri).Vertex(2).V = Vang2
                   
                end if
               
                ActualTri +=1
            next
        next
       
        this.nb_triangles = ActualTri
       
    End sub


Any idea of what could make this procedure to bug ?

Thanks :D
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Shockwave on September 27, 2008
It looks flat shaded tbh, it's probably a problem of normals.

Unit normals for a sphere are easy to generate fortunately, just generate a sphere with a radius of 1 for them :)
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Hezad on September 27, 2008
I'll try to calculate the normals by myself while generating the sphere then. But those normals should be okay since I have a procedure which precalculate automatically all normals :p

thanks for the advice anyway, I'll try it :)
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: hellfire on September 27, 2008
Hazad, you are creating 3 independant vertices for each triangle.
Later you're calculating the per-vertex-normal by averaging the normals of all triangles adjacent to the a vertex.
But there is just one triangle (because there are no shared vertices) and it's the same for each vertex of triangle.
So you got exactly the same normal for each vertex of a face - which results in perfect flatshading :)

You probably do not want to store three (unshared) vertices per triangle, but instead store each vertex only once and, additionally, create a triangle-table storing three indices into the list of shared vertices.

This will save you quite a lot of transformation-work because in the average case a mesh has only half as many vertices as triangles.
Since you're now creating 3 vertices per triangle, you have six times as many vertices as are actually required...

Example:

Vertices:

Vector vertices[12]= {
  0, 32.647, 0,
  29.2, 14.6, 0,
  9.0233, 14.6, -27.771,
  -23.623, 14.6, -17.163,
  -23.623, 14.6, 17.163,
  9.0233, 14.6, 27.771,
  23.623, -14.6, -17.163,
  -9.0233, -14.6, -27.771,
  -29.2, -14.6, 0,
  -9.0233, -14.6, 27.771,
  23.623, -14.6, 17.163,
  0, -32.647, 0
};


Faces:

int triangles[20*3]= {
  0, 1, 2,
  0, 2, 3,
  0, 3, 4,
  0, 4, 5,
  0, 5, 1,
  1,10, 6,
  2, 6, 7,
  3, 7, 8,
  4, 8, 9,
  5, 9,10,
  6, 2, 1,
  7, 3, 2,
  8, 4, 3,
  9, 5, 4,
10, 1, 5,
11, 7, 6,
11, 8, 7,
11, 9, 8,
11,10, 9,
11, 6,10
};

Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Hezad on September 28, 2008
Hey :)

I totally understood this :
QuoteYou probably do not want to store three (unshared) vertices per triangle, but instead store each vertex only once and, additionally, create a triangle-table storing three indices into the list of shared vertices.
thanks to the examples, and I like it :p

But there's something I don't understand :

QuoteLater you're calculating the per-vertex-normal by averaging the normals of all triangles adjacent to the a vertex.
But there is just one triangle (because there are no shared vertices) and it's the same for each vertex of triangle.

Why is there no shared vertices ? I mean, even if I don't use a vertex list/triangle table for now, there's a routine in my precalc_normals procedure which finds which vertex is shared with another (The way I do this is surely not the best way to do it, but I was expecting it to work at least :p)

And a last question, about the vertex list and the triangle table : Do you recommend me to use this approach in all the engine ? Or just while generating objects ? Because using it all along the code scares me a bit : I should change almost all the code when it comes to vertex handling !
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: hellfire on September 28, 2008
My mistake, in my explanating I forget that you don't have indices at all.

In your PreCalc_Object_Normals function you check for each vertex of a triangle, if there's any other triangle with the same vertex-position. In theory this should work - but it doesn't because in SamePnt you're comparing two floating-point values for equality - which fails.
That's because the fpu internally calculates at a precision of 80bit while you're storing values at single(32bit) precision, so you can assume every assignment to a variable as rounding.

Reordering two iteration of your loop results in something like this:

u1 = round (phi/PIx2)
u2 = round ((phi+dPhi)/PIx2)
phi= round (phi+dPhi)
...
u3 = round (phi/PIx2)

Here u1 and u3 are not equal because of different rounding (the difference is rather small though).
That's why you can't find any adjacent triangle when calculating the normals.

The usual aproach for comparing floating-point values is something like this:
if abs(a-b)<EPS then equal

Quoteabout the vertex list and the triangle table:
Do you recommend me to use this approach in all the engine ?
Absolutely yes because you have much few vertices to transform and require less memory.
When you're at some point going for 3d-acceleration, this approach is also supported in hardware (index-buffers).

Another thing I noticed is that your vertex-structure is rather big. I recommend to split it into multiple arrays because you're now piping a lot of useless data through the cache when accessing a vertex (the polyfiller for example is not interessted in untransformed 3d-coordinates or normals).
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Hezad on September 28, 2008
rah supa k++ to you ^^ it works perfectly ! thanks a lot for finding the problem. I didn't know this stuff about rounding, I'll remember that !!

QuoteAbsolutely yes because you have much few vertices to transform and require less memory.
When you're at some point going for 3d-acceleration, this approach is also supported in hardware (index-buffers).

Another thing I noticed is that your vertex-structure is rather big. I recommend to split it into multiple arrays because you're now piping a lot of useless data through the cache when accessing a vertex (the polyfiller for example is not interessted in untransformed 3d-coordinates or normals).

Noted, I'll work on that then :)
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Shockwave on September 28, 2008
Nice image :)

I thought it was a normals problem, glad you got it sorted out :) Can't wait to see it working!
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Hezad on September 29, 2008
Hey :D Well, I just finished to implement Mip Mapping :D

QuoteNice image Smiley

thanks ^^ I hope you'll like this version !

Well it's not filtered yet (or yes it is, but just an average of 4 colors on each stage) and there is still some flickering (not much though, excepted on the checkered sphere, it's awful oO but I think it's a problem with my MipMap generation routine).

I decided to set version numbers from now on, it'll be easier (even for me) to see what's new and when. Oh and I already heard some complaints (not here ^^) about the fact I don't respect the version number norms but whatever :P

So, here is the changes list :D :

Quotev0.02 Changes List



MAJOR :

- Added MipMapping handling !
  > Set_Mipmapping(ON/OFF) to set it.. well.. ON or OFF :D
  > LOD bias constant for now
  > No filtering yet (Bilinear planned)

- Added GenerateMipMap() procedure.
  > Generates MipMap GFX or loads it if file exists.

- Added Light Fall Off coefficient.
  > Permits Light intensity to fall off with distance between objects and lights.

- Added Object.GenerateSphere() procedure.
  > GenerateSphere (x0, y0, z0, Radius, Subdivisions)



Minor :

- TSLine and GLine subs replaced with macros.
- Some minor optimization.


I should work on optimization and on the vertex list / triangle table hellfire spoke about but I don't know yet how to start (I tried the vertex list/triangle table, but when I do this, I lost all information about U,V coordinates, normals, etc.. but I'm thinking about it :) ).
And concerning pure optimization, I still have lot of work to do, I haven't converted singles to integers, and I surely have some divisions/multiplications I could precalculate ...




edit : This version crashes on some computers ..
Title: Re: WIP 3D Engine (Update : Modularization + Materials)
Post by: Shockwave on September 29, 2008
It locked up here after opening the screen unfortunately mate.

Illegal array access? Drawing something off screen?
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 29, 2008
Argh, problems coming ^^

Hum .. Could you compile the code without using Mip mapping please ? (beginning of test_gouraud.bas, change Set_MipMapping ON to MipMapping OFF). If you can't compile, I'll compile a version without mipmapping.

QuoteDrawing something off screen?
Hum .. Well, normally, it shouldn't be possible thanks to clipping

QuoteIllegal array access?
or pointer.
That would be much more probable since the mipmap processing uses different textures sizes, it could use a wrong size and look for a color outside the pointer/array .. But now, why does it work for me then ?

thanks for feedback :)


edit : aargh wait a minute ! I just remembered the fact that the mipmap pointers are not deallocated !! I forgot :P It could be the problem source I guess ? I fix it and I repost the .rar here :)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Shockwave on September 29, 2008
I cant compile it unfortunately since I have an old version of FB here, but what you said about pointers sounds likely to be the cause.

Something else that you should look into are any new floating point stuff you've got in there too.

I once had a bug whereby I was trying to convert a floating point varialbe into a colour value eg;

rgb (float,float,float)

I hadnt realised they were floats and it was crashing, it took me a while to figure it out.

PCs are strange like that.

I can code super illegal shit on my notebook, writing past buffer limits etc and it just runs it happily. On here it dies straight away.

I'll test your program on the laptop in a while too :)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 29, 2008
QuoteI cant compile it unfortunately since I have an old version of FB here, but what you said about pointers sounds likely to be the cause.

No problem, I'll compile a "without Mipmapping" version :)

QuoteSomething else that you should look into are any new floating point stuff you've got in there too.

I once had a bug whereby I was trying to convert a floating point varialbe into a colour value eg;

rgb (float,float,float)

I hadnt realised they were floats and it was crashing, it took me a while to figure it out.

PCs are strange like that.

I can code super illegal shit on my notebook, writing past buffer limits etc and it just runs it happily. On here it dies straight away.

ah computers.. :P I'll check for the floats and colors stuff :)


QuoteI'll test your program on the laptop in a while too Smiley

cool :) grazie ^^


Now, I think I have something concerning the bug .. I wrote a destructor for my texture type to deallocate all the previously allocated pointers at the end but strangely, it makes the program to crash. It must be a sign ><
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Clyde on September 29, 2008
Something I noticed in your source you are using reserved keywords for commands etc, eg color in your type, and also pos. It's a good idea to call them something else, as the compiler may act up on you.

Also not sure if this'll work.


#include "fbgfx.bi" ' for constants


I'd put the comment part above the include. Also I tend to use #Include Once "includefile_example.bi".

Also, im not sure if it matters and theres a default, for calls to subs, with using ByVal before your variable name and its definition. eg Declare Sub Yo( ByVal PosX As Integer, ByVal PosY As Integer ). As in older versions there was either ByVal ( By Value ) or ByRef ( By Reference ). not 100% sure if that's been done away with in the newer FreeBASIC version.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 29, 2008
Hey :)

About the reserved keywords, I think they can be used in user defined types as variables but not as subs (unless you undef them). But you're right, I should have good habits :p

About passing variables ByVal or ByRef, I think its ByRef by default on FB 0.18.xx (the version I use), but since other people could compile with other versions, and since the default may evolve in the next versions, I should always specify ByVal or Byref (what I don't do everywhere actually)

These are reasonable advices and I'll follow them right now, it may help for debugging ;)

Concerning the comment on the include statement, I only use fbfgx.bi for the scancodes constants and for now I only use them in the test source file (and this part works).

Thanks for (having take ? taken ? taking? arg I don't know ><) a look in the source :)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Clyde on September 29, 2008
No worries mate, my few pennies worth that may assist you dude.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: hellfire on September 29, 2008
I can run the binary but when i press esc, visual studio asks me to select a debugger  ;D
When I recompile the source (with FreeBasic 0.2) it crashes right away,
when I disable mipmapping it works fine.
Error checking (-exx) reports:
QuoteAborting due to runtime error 12 ("segmentation violation" signal) in "materials.bi::LOADMIPMAP()"

Some notes about mipmapping:
What I've understood from a quick look at your source-code, you're now selecting a mipmap-level depending on the depth of a polygon.
Generally it's true that if a polygon is further away, it's also smaller and needs less texture-precision - but it's not a very clever approach (but some ps2 games use this way because the hardware doesn't support mipmapping and it's easy to do).
If you, for example, look from a steep angle at one side of a cube, this side is very narrow and only needs a low texture resolution - although the polygon might be very near to the viewer.

So let's assume you're interpolating texture-coordinates linearly (which is inevitable when you stop doing perspective divide for every pixel), a better approach is to look at the deltas for interpolating the texture-coordinates u & v across the scanline.
For example if one delta is >= 2.0 you'll always skip one texel when advancing to the next pixel. In such case you can savely use a mipmap with half the size.

For generating the mipmaps, you can simply scale the "current" mipmap to half the size on-the-fly (a simple 2x2 filter) so there's no reason to save all mip-levels.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 29, 2008
hey hellfire :) thanks for the report !!! I tried to run with -exx but (I must be lame ^^) I never found the report :P

Okay, so it confirms what I was thinking, a segmentation violation. And in LoadMipmap. erm....



erm ...


*brain on fire* ^^

Okay I recap :
- A segmentation fault in LoadMipMap (so, surely pointer error or array error ... you have to know I use an array of pointers, in the new Texture Type (which handle MipMapping) in this way :
Texture.MipMap(NbLODS) as integer ptr
in which I keep the mipmaps image ptr

- A crash once the gfx screen opens (even on my computer this time) when I set the destructor of TextureT to destroy all the GFX contained in this type (Original size + MipMaps)

erm.. I continue to search in LoadMipmap :P



edit :
another thing you have to know, I store my images in integer ptrs. So to load bmps, since bload don't work with this kind of pointers as-is, I must do that :
Bload FileName, MipMap(CurrLOD)-8

If I get off the "-8", it don't work. It may come from here too since I use a cheat to load the bmps. But if it's the case, why did it work before and not now ?
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: hellfire on September 29, 2008
Quotewhy did it work before and not now ?
Luck?  ;)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 29, 2008
.. In those cases, I totally hate luck ><

:P Well so I guess the next step is a custom Bmp Loader ..
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Jim on September 29, 2008
In your mipmap code, I think you need

        _pow2  = 1/(2^(CurrentLOD+1))
instead of
        _pow2  = 1/(2^CurrentLOD)

And remove the -8 from the bload
And change this bit
            MipMap(CurrLod) = Callocate(CurrX*CurrY-1+1000,Sizeof(integer))
I think the bload needs some more space for some reason.

Works for me then.

<edit>Look at the sample codes for bload and imagecreate
http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgBload (http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgBload)
http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgImagecreate (http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgImagecreate)
Seems you should use ImageCreate instead of Callocate and let FB worry about the image metadata size that it needs.

Jim
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 29, 2008
Ah ! Thanks a lot for that ! I'll upload the new exe with this post since I can't debug it at home (how to debug something which is working on your computer ?..  ;D)

strangely, getting off the "-8" on :
Bload fileN,this.GFX-8
makes my program to crash but inversely, the one you specified :

Bload MIPMAP_FOLDER+filen+"_MipMap"+str(CurrLOD)+".bmp",MipMap(CurrLOD)-8
doesn't make me crash if I get "-8" off.

Quote<edit>Look at the sample codes for bload and imagecreate
http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgBload
http://www.freebasic.net/wiki/wikka.php?wakka=KeyPgImagecreate
Seems you should use ImageCreate instead of Callocate and let FB worry about the image metadata size that it needs.

Well in fact I totally agree, I should use ImageCreate with Any ptrs instead of Callocate with integer ptrs, but I started to code the engine using integer ptrs since I use them to directly write on the screen buffer. (And since the pointers returned with imageCreate have some headers and/or data before the image data, I don't know how to deal with them, I don't really want to use pset or put excepted on pre-rendering operations (like mipmap files writing))


Here are the new exes with those modifications (2 versions)

(too large to be attached here, links below)

- Engine With Mip Mapping (http://freefile.kristopherw.us/uploads/hezad/engine3d_with_mipmapping.rar)
- Engine without Mip Mapping (http://freefile.kristopherw.us/uploads/hezad/engine3d_with_no_mipmapping.rar)


In those versions, the GFX destructor is commented since it stills make me crash.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: hellfire on September 29, 2008
It's working pretty well now, but when you're approaching the checkered sphere from a distance, there's one point (very probably where you change from one mipmap to another) where the texture-coordinates "jump".
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Jim on September 30, 2008
You have to get rid of all the -8 stuff.  There's no logic behind it.  You are effectively avoiding overwriting the end of your allocation and using -8 is making it overwrite the beginning instead.  Neither of these is good news at all, and hence the random crashes on different PCs.
The problem is Bload not just loads the pixels but it stores some other data too, like the x,y size and bpp.
My suggestion is you use ImageCreate to create a temp image for Bload to load into, then copy the image pixels into the memory you've Callocated.
Jim
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 30, 2008
QuoteIt's working pretty well now, but when you're approaching the checkered sphere from a distance, there's one point (very probably where you change from one mipmap to another) where the texture-coordinates "jump".

thanks ^^

About the "texture jump", there's no filtering yet so it could explain that I guess. I plan to code a bilinear filter :)


QuoteYou have to get rid of all the -8 stuff.  There's no logic behind it.  You are effectively avoiding overwriting the end of your allocation and using -8 is making it overwrite the beginning instead.  Neither of these is good news at all, and hence the random crashes on different PCs.
The problem is Bload not just loads the pixels but it stores some other data too, like the x,y size and bpp.
My suggestion is you use ImageCreate to create a temp image for Bload to load into, then copy the image pixels into the memory you've Callocated.
Jim

... OF COURSE !! arf why didn't I think about it ? it's a neat idea, thanks ! I'll code that tomorrow once I get up from bed ^^
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Shockwave on September 30, 2008
It's a real treat to see this working now :)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 30, 2008
It's a real treat to hear that ^^

Bload stuff is now fixed :D I'll post the next version once mipmap filtering is coded. I found a very useful link about little optimization tricks, I may also optimize a bit before the next version.

the link (lot of stuff for QB but lots of ideas useable with FB) :
http://www.phatcode.net/articles.php?id=232 (http://www.phatcode.net/articles.php?id=232)

An example :
I didn't know writing (a) instead of (b) was possible ! In fact it's pretty logical since logical operations return -1 if true and 0 if not in FB but I never thought about that :)

(a)
a=5
if a>2 then
b=6
end if


(b)
b=-(a>2)*6



hellfire > I just noticed your edit in the previous page :
QuoteSome notes about mipmapping:
What I've understood from a quick look at your source-code, you're now selecting a mipmap-level depending on the depth of a polygon.
Generally it's true that if a polygon is further away, it's also smaller and needs less texture-precision - but it's not a very clever approach (but some ps2 games use this way because the hardware doesn't support mipmapping and it's easy to do).
If you, for example, look from a steep angle at one side of a cube, this side is very narrow and only needs a low texture resolution - although the polygon might be very near to the viewer.

So let's assume you're interpolating texture-coordinates linearly (which is inevitable when you stop doing perspective divide for every pixel), a better approach is to look at the deltas for interpolating the texture-coordinates u & v across the scanline.
For example if one delta is >= 2.0 you'll always skip one texel when advancing to the next pixel. In such case you can savely use a mipmap with half the size.

I was precisely wondering about the fact that my "Z-based" mipmapping was wrong, thanks for pointing me on the deltas, I'll work on that for the next release.

QuoteFor generating the mipmaps, you can simply scale the "current" mipmap to half the size on-the-fly (a simple 2x2 filter) so there's no reason to save all mip-levels.
Won't it be really slow to iterate through all texels and "2x2-filter" them in the main loop ?
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: hellfire on September 30, 2008
QuoteWon't it be really slow to iterate through all texels and "2x2-filter" them in the main loop ?
What I meant is that there's no need to store the mipmaps to *disk* because generating the mipmaps at *startup* is pretty fast.
At the moment you're scaling the original texture to create each mipmap; instead you can simply scale the previous level to half the resolution.

Quotethanks for pointing me on the deltas
You're welcome :)
You also might want to consider creating anistropic mipmaps to take the ratio of the deltas into account.
This results in a matrix of textures with the "basic" mipmaps on the diagonal.
Example for a 256x256 map:

256x256, 256x128, 256x64, 256x32, 256x16, 256x8, 256x4, 256x2, 256x1
128x256, 128x128, 128x64, 128x32, 128x16, 128x8, 128x4, 128x2, 128x1
64x256,  64x128,  64x64,  64x32,  64x16,  64x8,  64x4,  64x2,  64x1
32x256,  32x128,  32x64,  32x32,  32x16,  32x8,  32x4,  32x2,  32x1
16x256,  16x128,  16x64,  16x32,  16x16,  16x8,  16x4,  16x2,  16x1
  8x256,   8x128,   8x64,   8x32,   8x16,   8x8,   8x4,   8x2,   8x1
  4x256,   4x128,   4x64,   4x32,   4x16,   4x8,   4x4,   4x2,   4x1
  2x256,   2x128,   2x64,   2x32,   2x16,   2x8,   2x4,   2x2,   2x1
  1x256,   1x128,   1x64,   1x32,   1x16,   1x8,   1x4,   1x2,   1x1

Looks like a lot of data at first, but "just" requires 4x as much space as the original texture.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on September 30, 2008
QuoteWhat I meant is that there's no need to store the mipmaps to *disk* because generating the mipmaps at *startup* is pretty fast.

oh okay :P I misunderstood you ^^ but for now I must say I like the idea of storing files the first time, and loading them after. However, I can't really tell you why .. Just a preference.

Quoteinstead you can simply scale the previous level to half the resolution.
Done some minutes ago ^^ totally rewritten the GenerateMipMap sub.

QuoteYou also might want to consider creating anistropic mipmaps to take the ratio of the deltas into account.
This results in a matrix of textures with the "basic" mipmaps on the diagonal.
Example for a 256x256 map:

Yep I red about it :) Still, I'll start by trying to finish the "basic" mip-mapping before ^^


And about it, I already have a new question troubling me :S

Every generated textures mipmap seem okay (reduced, blurred) excepted ... the checker one !
It's pretty normal, the box filter iterate every 2 pixels and since each checker square has a side proportional to 2, every new mipmap keep the same geometry.

But it's a problem !!
Since it's not blurred at all (excepted for the 2x2 mip-map and 1x1 mip-map which are simply grey), the texture looks always the same, whatever the distance/angle/dU/dV is !! And therefore, the Moiré effect stays here (and it's terrible, especially on floor) but worse : the limit between 4x4 and 2x2 mip-maps in 3D world is awful !
Is thought about using a texture with 3 squares per side instead of 2 but
1) it's not tilable and,
2) Anyway, it's not proportional to 2, so it doesn't fit in any texture size ><

Now I'm really wondering, I saw Mip-mapped checkered floors everywhere on the web, how do they do ?!!

Must I use some kind of 4*4 box filter ? or even odd*odd box filter ?
I guess bilinear filtering would solve the problem but I'm pretty sure (but I can be wrong) I saw some of those checkered floors with good mip-mapping using box filter on the net ..


edit : the attached images may seems to be actually blurred, but it's the jpg compression which do that.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: hellfire on October 01, 2008
I must admit your question bothered me for a moment, but the mipmaps of your checker-texture are perfectly okay.
You are using a texture which basically consists of 2x2 pixels (just scaled up to 64x64).
It's no surprise that you reach exactly this 2x2 texture when scaling it down again.

In the distance you can imaging each pixel of your polygon being filled by a rather big area of your texture. All texels in this area are summed up to a single color. Mipmapping reduces the size of this area to 2x2 texels but still your texture only contains black and white pixels.
At this point the bilinear filtering sums up the weighted black & white areas.
Since you're not yet doing bilinear filtering and just pick the nearest of the 4 surrounding pixels (which is either black or white), you still get usual sampling-artifacts just as if there wasn't any mipmapping.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on October 01, 2008
hum .. So I can't pass through bilinear filtering :D Working on it right now then :)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Jim on October 01, 2008
At some point your checker will go to grey.  This is GOOD, because it's the way the sparkles are avoided when your object moves a tiny amount when it is very far away.  If your scene goes grey where you don't want it to, then you're probably not calculating the mipmap level to use correctly.  The 1 pixel grey texture shouldn't be used until your polygon is 1 pixel in size or mapping only 1 texel's worth of uv area.

You probably should bilinear filter your texture while calculating your mipmaps.  But it's simple, every 2x2 in the source texture becomes 1 pixel in the destination one, so very simply you can just add them all together and divide by 4.

Sometimes it depends.  I remember a problem I had with a texture which was supposed to be a chain link fence.  At full size, it works fine, grey criss-cross with transparent holes.  The next mipmap layer down though, it just went solid.  So mipmaps are not for every texture, and if you use alpha for transparency or punch-through you might want to change the way the averaging/bilinear works.

Jim
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on October 01, 2008
QuoteBut it's simple, every 2x2 in the source texture becomes 1 pixel in the destination one, so very simply you can just add them all together and divide by 4.

Well that's exactly what I'm doing now ^^ And it's the reason the Checker doesn't blur himself while generating mipmaps :)

QuoteYou probably should bilinear filter your texture while calculating your mipmaps.
while generating ? Shouldn't I apply the filter while rendering the pre-generated mipmap ?



and while i'm here, sorry but I have *again* a question about the LOD. I wanted to use the texel deltas method to apply the correct mipmap. But whatever I do (I tried looots of different ways to access the dU and dV values since I interpolate U/z and V/z instead of U and V), the rendering is weird (especially on spheres, the flat surfaces seem almost okay). I can see I'm almost there, but it's still far from perfect.

I attach a screenshot of the colored LOD rendering : The greener, the higher is the LOD (Do you see how the LOD "spreads" on the middle sphere ?).

And that's the code I'm actually using to find the deltas :

dim as single LODCOEF = 128
               
dU = dU_z*tmp_Z*LODCOEF
dV = dV_z*tmp_Z*LODCOEF


with dU_z = (u/z2 - U/z1) / (x2 - x1)
and tmp_Z = 1/CurrentZ

(I tried several values for the LOD coef)

I also tried to replace tmp_Z by the z2 and z1 values and/or 1/z2 and 1/z1 values.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: hellfire on October 01, 2008
So, you have interpolated x, u/z, v/z, 1/z along the left & right edges of your polygon:

lx= left x-coordinate of your scanline
lu= u/z on left side
lv= v/z on left side
lz= 1/z on left side

rx= right x-coordinate of your scanline
ru= u/z on right side
rv= v/z on right side
rz= 1/z on right side

What you want to find is the delta from one pixel's perspective correct texture-coordinates to the next:
mipu= log2( lu/lz - (lu+deltau)/(lz+deltaz) )
mipv= log2( lv/lz - (lv+deltau)/(lz+deltaz) )

Now that's computationally a bit expensive to do per pixel.
So let's do things properly:
Split the scanline into pieces of, let's say, 8 pixels and calculate deltas to get from one set of (u/z, v/z, 1/z) to the one eight pixels further:
inv= 8.0 / (rx-lx)
du= (ru-lu) * inv
dv= (rv-lv) * inv
dz= (rz-lz) * inv

Calculate one pair of perspective correct texture-coordinates for the left and right side of the 8-pixel-span. Let's also make them integers (we multiply by 2^16 to keep some precision):

u1= lu/lz*65536.0
v1= lv/lz*65536.0

advance to the end of 8-pixel-span:
lu+=du
lv+=dv
lz+=dz

u2= lu/lz*65536.0
v2= lv/lz*65536.0

you can now interpolate linearly across the coordinate-pair:
ldu= (u2-u1) shr 3
ldv= (v2-v1) shr 3
(beware of freebasic's shift function)

here you can read the mipmap-level from the upper-word of the deltas (find fast ways for log2 here (http://www-graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup)).
Adjust the texture-coordinates depending on the miplevel (level 0 is the original texture).
Texture-tiling is for free with a simple bitmask.

masku= (1 shl (log2(texture_width) - miplevel))-1
maskv= (1 shl (log2(texture_height) - miplevel))-1
texshift= log2(texture_width) - miplevel

for x=0 to 7
  u= (u1 shr (16+miplevel)) and masku
  v= (v1 shr (16+miplevel)) and maskv
  dest(x)= mipmap[ (v shl texshift) + u ]
  u1+=ldu
  v1+=ldv

(all without warranty)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on October 01, 2008
Rah how could I thank you for your implication ?! Thank you so much for all that. I didn't understand everything from the tiling part but don't worry, i'll work on that :)

Concerning the Log2 calculus algorithm, I guess it'd be preferable to use the one for evenly distributed inputs values ? Anyway, the listing says :

if (tt = v >> 24)
{
  r = 24 + LogTable256[tt];
}
else if (tt = v >> 16)
{
  r = 16 + LogTable256[tt];
}
else if (tt = v >> 8)
{
  r = 8 + LogTable256[tt];
}
else
{
  r = LogTable256[v];
}


but tt is not defined before.. it's declared as a "register unsigned int" so I guess the "register" thing has to do with that since it can't be used if always equaling 0 ..
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: hellfire on October 01, 2008
"register" is a c++ keyword which proposes to keep the variable in a register.
Modern compilers are usually clever enough to decide that on their own ;) so just make "tt" an integer.
Notice that the expression inside the if() is an assignment and not a compare:

tt = v shr 24
if t<>0 then
  ...

Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on October 01, 2008
okay thanks :) I have lots to learn about C/C++ lol ...

I thought

if (tt = v >> 24)
{ ...


were equivalent to

if tt = v shr 24 then ...

instead of

tt = v shr 24
if t<>0 then ...


><
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.02]
Post by: Hezad on October 02, 2008
Hey :D Just a quick post since I'm in a hurry. Here is a temp release of the Engine with the Mipmapping finally working thanks to hellfire.

The optimization brought is not implemented yet, I wanted to try quickly the "expensive" method before to ensure I wasn't making any mistakes with the rest of the code. That's why this release is a "temp" one. The real 0.03 will have the optimizations hellfire spoke about and bilinear filtering. So here it is :)

Changes list :
Quotev0.03 temp Changes List



MAJOR :

- Mip Mapping fixed, now totally workind !
  > A gigantic thanks to hellfire (from dbf/gvy boards) which gave me the
  > code to find the current Level Of Details to use.

- Image loading (bmp loading with Bload) is fixed.
  > No more weird tricks with bload

- Added Render_Option()
  > Normal, Mipmap levels or Zbuffer

- GenerateMipMap() total rewrite
  > More efficient



Minor :

- Fixed a little bug in Z-buffer handling.
- More precise FPS calculus in the example source.


To Do next :
- LOD determination optimization
- bilinear filtering
- fixing some undetermined bug(s) in the clipping function.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: Shockwave on October 02, 2008
The rewrite worked nice and you'll be pleased to know that it did not crash here :)

It looks lovely actually and great that you added som options during the demo to turn things off an on, you can see straight away the mipmapping makes a nice difference to the quality :)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: Hezad on October 02, 2008
QuoteThe rewrite worked nice and you'll be pleased to know that it did not crash here Smiley

I am !  ;)


QuoteIt looks lovely actually and great that you added som options during the demo to turn things off an on, you can see straight away the mipmapping makes a nice difference to the quality Smiley

thanks :) I hope the bilinear filtering will add more quality to the rendering, but I'm a bit afraid of the FPS fall it will cause ..
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: Stonemonkey on October 02, 2008
I did some work on bi linear filtering a while ago and posted a little about it here: http://dbfinteractive.com/forum/index.php?topic=2285.0 there's a link to a bit about efficient blending there too.

EDIT:

There's also a little bit about the bleeding edges and clamping which can be useful for the filtering here: http://dbfinteractive.com/forum/index.php?topic=2342.0 although it's in c++.
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: Hezad on October 02, 2008
Yey ! Awesome trick :D thanks for sharing it, it'll definitely be useful :)


QuoteThere's also a little bit about the bleeding edges and clamping which can be useful for the filtering here: http://dbfinteractive.com/forum/index.php?topic=2342.0 although it's in c++.

Well I have to admit I haven't understood all the vocabulary used in this thread :P I know (I think :p) what are bleeding edges, but clamping ? A literal translation didn't help :)
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: Stonemonkey on October 02, 2008
The edge bleeding is where the pixels from the opposite edge of a texture are used in part of the filtering, clamping is used to stop the pixels from the opposite edge being used.

if you have a 16*16 texture and you read from coords 15.3,12.7 then you'd read from 4 texels (15,12) (16,12) (15,13) (16,13)

but the 16 wraps around to 0 and reads from the other side of the texture so you'd really want anything over 15 to still equal 15.

Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: Hezad on October 03, 2008
okay, got it, thanks :D
Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: hellfire on October 04, 2008
Quoteclamping is used to stop the pixels from the opposite edge being used.
In case your texture is tilable it's totally valid to adress texel from the opposite side when filtering.
In the other case (usually needed for projective textures or lightmaps) I usually made my texture a pixel bigger in each direction, so that the texel (u+1,v+1) is always present and I don't have to handle special cases; or I make sure that the right/bottom-boundary texel will never be addressed by the uv-coords.


Title: Re: WIP 3D Engine (Update : Mip Mapping) [v0.03]
Post by: Hezad on October 07, 2008
Update :D

So this is the v0.04 and there are lots of new stuff in there ! Stuff like camera handling, new tore object, etc ... But I let you see the changes list :

Quotev0.04 Changes List



MAJOR :

- Big Optimization with the Shaders calculations.
  > Shaders are now calculated only on visible triangles

- Big Optimization while computing mipMap level
  > MipMap level is now calculated only once per scanline

- Camera Rotation handling.
  > Camera can now rotate according to Y axis and X axis

- Added Init_Engine Sub.
  > User can now choose desired resolution

- Added GenerateTore Sub.
  > object.GenerateTore(x0,y0,z0,r1,r2,nbfaces)

- material Ambient is modified when Diffuse is modified.
  > Can still modify the ambiant after the diffuse modification.


Minor :

- Camera can now translate in world space or in View space
  > Move_Camera X,Y,Z, SPACE (IN_VIEWPOS or IN_WORLD)

- Fixed a bug with the Z-Buffer handling

- Fixed a bug in the untextured rendering


You can still move with the keyboard arrow keys but Left and right now handle the camera handle.

Filtering is still not done though, and I hope I will have some motivation to code it very soon (I can't stand the moiré patterns appearing on the checkered floor, even with mip-mapping)

You can DL it here : Download (http://freefile.kristopherw.us/uploads/hezad/engine3dv0_04.rar)


Quote
Todo List :

- real time texture bilinear filtering
- simple obj loader (vertices/faces, UV coords, normals)


Wonderings :
- Implementing phong shading ?
- Is there a way to do fast super sampling or any type of FSAA without Open GL/direct X ?
- Implementing environment mapping ?
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Shockwave on October 07, 2008
I cant really notice any speed improvement, it's still about the sam as before but.. It has much nicer colours and the ability to be able to spin the camera to look around makes a huge difference to this.

The torus is nifty too :)

Please do keep finding the motivation to improve it more and more :)
Every now and again we have one of these threads, someone will have a go at textures, raytracing or something, this has been one of the best threads of this sort we've had. People will learn a lot from it.
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: hellfire on October 07, 2008
Well done!

QuoteImplementing phong shading ?
It's easy. Instead of calculating lighting per vertex and interpolating the resulting rgb-components, you interpolate the xyz-components of your normal-vector and apply the vertex-lighting per pixel.
But you won't use it because it's just too slow.
Better:
QuoteImplementing environment mapping ?
There are lots of ways to do it (spherical, cubic, dual-parabolic) and even several representations of spherical coordinates.
Do you have specific questions?

QuoteIs there a way to do fast super sampling
Accelerators go the brute-force way: render at higher resolution and afterwards sample down to the original resolution; slow.
Another method is to find the siluette edges and render those as anti-aliased lines; this requires transparency, thus sorting polygons and trouble with the zbuffer.
None of these is really fun.
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Hezad on October 07, 2008
@shockwave

QuoteI cant really notice any speed improvement, it's still about the sam as before

lol I gained at least 3 FPS and since it was running at ~9/10 FPS, I really have a gain in speed ^^ But maybe my computer is too old, idk :)

QuoteIt has much nicer colours and the ability to be able to spin the camera to look around makes a huge difference to this.

The torus is nifty too Smiley

Please do keep finding the motivation to improve it more and more Smiley
Every now and again we have one of these threads, someone will have a go at textures, raytracing or something, this has been one of the best threads of this sort we've had. People will learn a lot from it.

Thanks :) 'glad you like it, and 'glad it'd be useful in any way. Plus, if people can enjoy the awesome help and advises I got here, it's really cool !


@hellfire
QuoteWell done!
Kiitos :D

QuoteImplementing phong shading ?
[...] you won't use it because it's just too slow.

That's exactly what I wanted to know :) Well, no phong shading then.

QuoteImplementing environment mapping ?
There are lots of ways to do it (spherical, cubic, dual-parabolic) and even several representations of spherical coordinates.
Do you have specific questions?

Well not really, I mainly wanted to know if it'd be useful or if it'd really enhance the graphics :) I thought about using either spherical or cubic mapping and using the provided texture to make a skybox/skysphere besides the environment mapping.


QuoteIs there a way to do fast super sampling
Accelerators go the brute-force way: render at higher resolution and afterwards sample down to the original resolution; slow.
Another method is to find the siluette edges and render those as anti-aliased lines; this requires transparency, thus sorting polygons and trouble with the zbuffer.
None of these is really fun.

Hum got it .. So, am I bounded to see crenelated lines ? or maybe the bilinear filtering (if well coded) will greatly improve the graphics and then make the viewer to forget the crenelated lines ?



Thanks for your replies mates :)

btw, could you say me how many FPS you get when you're looking from some distance to the objects and almost see them all (average) ?
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Shockwave on October 07, 2008
You know what, there is really very little difference here between a scene with over 600 polygons to one with very few.

Always between 9 and 12fps
P4 3.0Ghz, ATI X1650 SE

:)
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: stormbringer on October 08, 2008
@Hezad: yeah, Phong is very expensive... but you can fake it a lot. Have a look at these:

http://groups.csail.mit.edu/graphics/pubs/thesis_drdaniel2.pdf
http://igad.nhtv.nl/~bikker/articles/various/tut_lighting.htm
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Hezad on October 08, 2008
@Shockwave :
oh okay :P Well there must me some calculations done even on clipped triangles.. Or maybe the clipping function must be drastically optimized :)

@Stormbringer :

hey cool :) very interesting links, didn't red all the pdf yet but I'm doing it right now, thanks for pointing me on this !
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: hellfire on October 08, 2008
QuoteI thought about using [...] cubic mapping
good luck ;D
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Hezad on October 08, 2008
I feel an ounce of irony ^^
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Hezad on October 08, 2008
Some colors :D

edit : added another screenshot
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Shockwave on October 09, 2008
Psychedelic :)

Bottom picture looks like the sun is coming up over the scene
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Hezad on October 10, 2008
Quote
Bottom picture looks like the sun is coming up over the scene

hum.. now I wanna try some sunset moods :p I'll try env. mapping for that :D


before that, I just tried super sampling ... Well, ok it runs at 2 FPS but wow..the graphics look bloody smooth oO
Title: Re: WIP 3D Engine (Update : Optimization, Camera, Tore, ...) [v0.04]
Post by: Hezad on October 13, 2008
Update :) I'm working on the 0.05 right now, and here are some news :)

Mainly : Supersampling, HeightMap handling, Fog     :D

still a temp version though

v0.05 Changes List

Quote

MAJOR :

- Added Supersampling !
  > Switch it ON or OFF before Initiating the engine.  (Set_Super_Sampling ON/OFF)
  > Not for Real-time use

- Added Height-map loader.

- Smoothed Mip-map generation.

- Added Linear Fog Handling.

- Added Exponential (density) Fog Handling

- Working on Mouse camera handling


Minor :

- Added a skysphere in the example

- User can use fbgfx flags with the init_engine sub
  > e.g. GFX_FULLSCREEN   

- Fixed a bug in tiling

- Changed textures in the example.



In the zip attached, you'll find the code and a demo (Real_time.exe or Super_Sampled.exe). Warning, the Super_sampled version is incredibly slow. It's definitely not for real time purposes.

You can rotate view with mouse (well .. almost, there's something about the camera rotation I haven't resolved yet :p), move the camera with the UP/DOWN arrow keys.
You can change some options with the function keys.

Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Shockwave on October 13, 2008
Props to you for including all three fog modes supported by Opengl :) I guess that it would be easy to alter the fog density too?

Using the terrain to test the engine looks better, it looks very accomplished, the supersampled version ran at 3fps here, which surprisingly was quite useable. In fact it reminded me of flying around in the game "FA18 Interceptor" on the Amiga, sometimes when you were flying around and there were several things on the screen at once, it used to drop to less than 2fps! Still, that game really rocked.

The way that you have your mouse controls set up at the moment it's quite similar to the controls of an aircraft.

Am I right in thinking that all the triangles are both textured and gourad shaded?
Also I saw things like this in the source;


Memcpy(Destlist,TmpList,(tempNbV)*Sizeof(VertexT))


Does this mean that you are rendering things in a temporary buffer and then copying them to the framebuffer?

I really am impressed at how good the scenery looks though Hezad, you made a great job of it :)
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: hellfire on October 13, 2008
Really nice!
The "normal" version runs at 18fps while the supersampled one does 3, which is basically unusable ;)
If you have a look at the supersampled image you can see how few pixels actually benefit from it and how "global" supersampling merely wastes power.
The sky-sphere works amazingly well and integrates perfectly into the terrain!
Also the lighting achieves a very natural look.
I noticed you used gouraud shading on the sky, too, which probably doesn't make sense because the texture captures the surrounding (including the light-source) - actually you would set up your light-sources to match the sky-texture and local lights won't reach the sky.
I'm also pretty sure that you can easily optimize your code to do twice the framerate :)
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 13, 2008
@Shockwave :

Quoteall three fog modes supported by Opengl Smiley I guess that it would be easy to alter the fog density too?

An other fog mathod is planned : Perlin Noise Fog (I saw some methods using it on the net and it looks bloody realistic :D) And yes, the fog density is easily alterable (even in real-time if wanted). The fog's density can be changed when setting the fog :


Set_Fog METHOD, DENSITY=0

Method : Linear (Density has no effect) / Exponential (you just have to select the desired density)




QuoteUsing the terrain to test the engine looks better, it looks very accomplished, the supersampled version ran at 3fps here, which surprisingly was quite useable. In fact it reminded me of flying around in the game "FA18 Interceptor" on the Amiga, sometimes when you were flying around and there were several things on the screen at once, it used to drop to less than 2fps! Still, that game really rocked.

The way that you have your mouse controls set up at the moment it's quite similar to the controls of an aircraft.

Well, in fact the aircraft-style-mouse-control wasn't wanted ^^ 'still working on it :)


QuoteAm I right in thinking that all the triangles are both textured and gourad shaded?

You're right :) But the engine can still handle flat shaded and non shaded polys.

Quote[...] Does this mean that you are rendering things in a temporary buffer and then copying them to the framebuffer?
Well the line of code you copied is used when clipping : it copies a temporary list of vertices to the one which will be returned. The only "temporary" graphics buffer I use is the one used for super sampling


Quote
I really am impressed at how good the scenery looks though Hezad, you made a great job of it Smiley
Thanks  :cheers:



@hellfire

QuoteThe "normal" version runs at 18fps while the supersampled one does 3, which is basically unusable Wink
Sure :D In fact, I start to know what I want with this engine : Since I want to experiment a lot of stuff (even, non real time stuff), I'll work on two versions of the engine : A real-time one, and a CG one, the real-time could be used for games and the CG version could be used for some kind of a CG renderer.


QuoteIf you have a look at the supersampled image you can see how few pixels actually benefit from it and how "global" supersampling merely wastes power.

Well here, you're totally right. The super sampling doesn't really affects the rendering quality. But when objects are more "curved" or complex (spheres, tores, future loaded objects :p), the super-sampling brought a huge difference. I'll try to make a test image to compare both versions.


QuoteThe sky-sphere works amazingly well and integrates perfectly into the terrain!
Also the lighting achieves a very natural look.
Cool :D 'glad you liked the lighting, I wondered some time to find the good blend between fog color, ambiant color and light color :p


QuoteI noticed you used gouraud shading on the sky, too, which probably doesn't make sense because the texture captures the surrounding (including the light-source)
actually you would set up your light-sources to match the sky-texture and local lights won't reach the sky.
In fact, the sky in not shaded :) wherever the lights are, whatever the number of lights is, the sky will always look the same (normally), excepted when fog changes its apparent color of course :)

QuoteI'm also pretty sure that you can easily optimize your code to do twice the framerate Smiley
That would be bloody awesome ^^ But my coding skills appear really weak to myself when it has to do with optimization. I will do it, but it will take time I guess :)
First, I should use the stuff you wrote about : Using Vertex listings and triangle list instead of 3 vertices per triangle, it should drastically improve the speed, but I'm not very sure about how to convert my code to this yet.



Thanks a lot to both of you, your commentaries always motive me :)
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: hellfire on October 13, 2008
You'll gain the most by optimizing those functions which are called most often.
Since the number of pixels to process is by some magnitude larger than the number of vertices, you should start optimizing at your polyfillers, namely the TSLine-Macro.

Just have a look at the code you run for each pixel:  :o

        tmp_Z = 1/Cur_Z
       
        If tmp_z < ZBuffer(it) then
           
            ZBuffer(it) = tmp_Z
               
            ValU = (U_z*tmp_Z)
            ValV = (V_z*tmp_Z)
           
            if ValU>=1 then ValU = ValU-int(ValU)
            if ValU<0 then ValU = 0
            if ValV>=1 then ValV = ValV-int(ValV)
            if ValV<0 then ValV = 0
           
            If UseMipmapping = 0 then
               
                CurTex = Texture.GFX[int(ValU*Texture.SizeX)+Texture.SizeX*int(ValV*Texture.SizeY)]
               
                Cb = (CurTex and 255)
                Cg = ((CurTex shr 8) and 255)
                Cr = ((CurTex shr 16) and 255)
               
            else
               
                LODVal = iif(mipU>mipV,MipU,mipV)
               
                If LODVal>Texture.NbLOD then LODVal = Texture.NbLod
                If LodVal<0 then LODVal = 0
               
                If LODVal = 0 then
                       
                    CurTex = Texture.GFX[int(ValU*Texture.SizeX)+Texture.SizeX*int(ValV*Texture.SizeY)]
                   
                    Cb = CurTex and 255
                    Cg = (CurTex shr 8) and 255
                    Cr = (CurTex shr 16) and 255
                   
                else
                     
                    CurTex = Texture.MipMap(LODVal)[int(ValU*Texture.MipMapX(LODVal))+Texture.MipMapX(LODVal)*int(ValV*Texture.MipMapY(LODVal))]
                   
                    Cb = CurTex and 255
                    Cg = (CurTex shr 8) and 255
                    Cr = (CurTex shr 16) and 255
                   
                end if
               
            end if
               
            Cb *= b*0.003921  '' 1/255
            Cg *= g*0.003921
            Cr *= r*0.003921
           
            Select Case Render_Option_Val
            Case RENDER_NO_OPTION
               
                if Use_Fog then
                    select case fog_method
                    case LINEAR
                       
                        dim as single blendfunc = (MAX_Z-tmp_Z)/(MAX_Z-1)
                       
                        Cr = blendfunc * Cr + (1-blendfunc) * Fog_Color.r
                        Cg = blendfunc * Cg + (1-blendfunc) * Fog_Color.g
                        Cb = blendfunc * Cb + (1-blendfunc) * Fog_Color.b
                       
                    case EXPONENTIAL
                       
                        dim as single blendfunc = exp(-(tmp_Z/MAX_Z) * fog_Density)
                       
                        Cr = blendfunc * Cr + (1-blendfunc) * Fog_Color.r
                        Cg = blendfunc * Cg + (1-blendfunc) * Fog_Color.g
                        Cb = blendfunc * Cb + (1-blendfunc) * Fog_Color.b
                       
                    case HETEROGENOUS
                        '' placeholder
                    end select
                end if
               
                ScrPtr[it] = rgb(Cr,Cg,Cb)
               
            Case RENDER_MIPMAP
                dim as integer ldv = LODVal*127/Texture.NbLOD
                ScrPtr[it] = rgb(255-2*Ldv,255-ldv,255-ldv)
            Case RENDER_ZBUFFER
                dim as integer zc = tmp_Z*255/MAX_Z
                ScrPtr[it] = rgb(zc,zc,zc)
            end Select
           
        end if
       
        it+=1
           
        U_z += dU_z
        V_z += dV_z
           
        Cur_Z += d_Z
           
        r += dR
        g += dG
        b += dB

First of all you shouldn't be checking render-settings on a per-pixel basis, but do an optimized loop for each of them.
Secondly you can store the interpolated 1/z in your zbuffer (reasonably scaled to fill an integer) so you don't have to do a divsion even if the zbuffer-test fails.
Remove all floating-point-operations when dealing with colours.
Then have a look what I already wrote about subdividing your scanlines and interpolating linearly across these segments.
When that's done, your framerate has atleast doubled.
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 15, 2008
@hellfire :

You're totally right, I started to code the optimizations you wrote about, especially "one procedure per render type" and I gained 1-2 FPS on the most demanding rendering (Texture + gouraud shader + Fog + transparency) and at least 10 FPS on the Z-buffer rendering ^^

As promised, here is a screen comparing real-time rendering and super sampled rendering, I think it's way more visible with the checker tex.
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: hellfire on October 15, 2008
Quotecomparing real-time rendering and super sampled rendering
Remember that the anti-aliasing of edges inside the texture would already be handled by bilinear filtering...
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 15, 2008
Yep, I started to work on bilinear filtering but the slow down was way to strong. I must work on optimization and camera handling before working on filtering again :)
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: hellfire on October 17, 2008
If you want to get some more performance from multisampling, you might want to try non-regular grids with samples shared across pixels:
(http://www.abload.de/img/multisampling1cu.gif)
Although there are 4 samples for each output-pixel, you just have to calculate two new samples per pixel.
Handling the different sampling-positions inside the polyfiller is a bit tricky, though.
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 17, 2008
Thanks for the advice :)

Don't know yet if I'll use it (I'm still on my camera/optimization wanderings :p) but I'll definitely remember this  :D
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: relsoft on October 20, 2008
For static objects (Terrain,etc), I would suggest you do some space partitioning to speed things up quite a bit.

For outside areas with a free-look cam, you could use Octrees.  If you are going to limit your FOV from the top, Quadtrees would suffice.  Both would almost be coded exactly the same.

Here's a demo on how quadtrees could render stuff faster.

http://rel.betterwebber.com/mytutes/octreetute/octree_tute.zip

It's just sources and exes since I haven't coded anything since those. :*(
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 20, 2008
Thanks for that :) The point is I don't know yet what this engine's purpose will be ! The main purpose is of course learning, but eh, since It has to be coded, why not making it useful ? ;) In fact, I don't know if I want it to be a 3d engine designed for games, or for CG rendering ... Well, I guess in both cases, Space partitioning will help but I wanted to precise :D

thanks for the sources and the exes, I'll check that :)
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 28, 2008
Heya :)

Nothing new, I didn't work on it since the last posts  :-\ There are so much stuff to do for it to be useful .. I got unmotivated. But right now I want to get back into it, I have a lot of ideas to code with software rendering but I HAVE TO code everything needed before (optimization, camera handling, texture filtering ..).

Okay, so now i'm focusing about camera handling.

Have I to use matrices inevitably ? In fact, for translations, it's pretty easy, no matrices needed. But for rotations ... I use three angle parameters to rotate all the world when needed. But if it works for the Y axis (eg, player rotation), when I wanna rotate along the X axis, it doesn't rotate in the camera space but in world space (So if I rotate along the Y axis and THEN rotate in the X axis, the X rotation isn't done about the direction I'm aiming but along a constant world X axis.

hum... I guess what I just wrote is simply illegible ... Sorry for that .. To resume in a sentence, When I rotate along the Y axis, the X axis stays constant instead of rotating "with me".

(You can see that in the last preview I posted if you try to rotate the camera with your mouse).

So my question is, Can I avoid that with "simple" angles parameters in the Camera Type ? Or MUST I use matrices ?
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: hellfire on October 28, 2008
Using matrices is inevitable and you might be surprised to hear that you are using them already.
You are now rotating your vertices using euler angles, applying one after another.
Euler rotation is nothing else than a 2d-rotation matrix (because the axis of rotation is cartesian, one component of your vector keeps unaffected):
x'= cos(a) * x - sin(a) * y + 0 * z
y'= sin(a) * x + cos(a) * y + 0 * z
z'=      0 * x     +  0 * y + 1 * z

(exactcly what you do in "Rotate_Object")

Since a 2d-rotation (2x2 matrix) requires 4 multiplies/additions, you now already require a total of 12 mul/add (not containing translation) and your transformation will soon contain more components like scaling, shearing etc.
If you multiply the three matrices and translation to a single 4x3 matrix you just require 9 muls & 12 adds:
x'= Mxx * x - Mxy * y + Mxz * z  + Mxw
y'= Myx * x + Myy * y + Myz * z  + Myw
z'= Mzx * x + Mzy * y + Mzz * z  + Mzw

Rotating around cartesian axes is also rather unflexible, especially for camera-handling you'll preferably align your view along position- & target-vector (like gluLookAt does).
Try to understand that a matrix just defines the orientation (and origin) of a vector-space. The 3 vectors of the 3x3-part show were side/height/depth is. You can get from one space to another by multiplying with the corresponding matrix. get back again by multiplying with the inverse matrix.

Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 29, 2008
thanks hellfire, I think I start to see the point :) k++

So, for testing purposes, before implementing a LookAt camera, can I apply the 2d rotation matrix 3 times (each time for each axis) ?
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: hellfire on October 29, 2008
Quotecan I apply the 2d rotation matrix 3 times
That's basically what you already do.
If you want to keep the euler angles, I suggest to construct a 3x3 rotation-matrices for each axis and multiply them to a single matrix:
M = Mx * My * Mz
Notice that the order is important - traditionally one rotates around the "up"-axis first.

3x3 layout for (clockwise) rotation-matrices looks like this:

   1    0       0
   0  cos(x) -sin(x)
   0  sin(x)  cos(x)



cos(y)  0 -sin(y)
   0     1    0
sin(y)  0  cos(y)



cos(z) -sin(z)  0   
sin(z)  cos(z)  0   
   0       0     1

Notice that an angle of 0 results in an identity matrix.
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Clyde on October 29, 2008
Firstly welldone on your development Hezad, and secondly ( or should this be first ) congrats on the first to get over 170 ( at time of typing ) replies in a Coding category; plus on your very first post too - all with cool and handy stuff from peeps.

Karma++
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 30, 2008
@hellfire :
I don't particulary want to keep Euler angles, but I just want to understand what's going on before working on a look at procedure :)

QuoteI suggest to construct a 3x3 rotation-matrices for each axis and multiply them to a single matrix:
M = Mx * My * Mz

So, if I understand well, I "just" have to build the three 3x3 rotation matrices (the layouts you posted) in the good order and then multiply them. So, how can I use the resulting matrix ? Can I just rotate the viewpos by using numbers contained by the matrix ?



@Clyde :
Thanks a lot :) Credit goes to all the awesome information people posted here, especially hellfire !

btw, what do you mean by "peeps" ?
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Shockwave on October 30, 2008
QuoteSo, if I understand well, I "just" have to build the three 3x3 rotation matrices (the layouts you posted) in the good order and then multiply them. So, how can I use the resulting matrix ? Can I just rotate the viewpos by using numbers contained by the matrix ?

Yes :)
You rotate those points in the matrix and multiply everything with them.

And it will be quite fast too.
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on October 31, 2008
Thanks Shockwave :) I have a last question though : if I multiply the three 3x3 matrices, it will result in a 3x3 matrix no? If I'd have a 3x1 matrix, I could multiply each point coordinate by a value in the matrix

e.g. :
point.x *= matrix(1)
point.y *= matrix(2)
point.z *= matrix(3)


But since the resulting matrix is a 3x3 one, what have I to do with the 6 remaining numbers ? Or how to use a 3x3 matrix to handle coordinates using 1x3 numbers ? Rah once again I'm lost. Now I know how to handle simple matrices, but I still don't understand how to handle the transformations they describe :S

Anyway, I coded a little Header to handle matrices (addition, subtraction, multiplication, create empty matrix, create Identity matrix, and a cast operator to print matrices on screen) to use it with my engine.

I post it here, maybe it'll be useful for someone :

matrix.bi :

'' Matrix.bi
''
'' 2008 - Hezad
''
''
''  Contact : Hezad0@Gmail.com


#Define nl chr(10)+chr(13)

Type Matrix
    as integer SizeX, SizeY
   
    as single ptr Dat
   
    Declare Constructor(sx as integer, sy as integer)
   
    Declare Operator Cast() as string
   
    Declare Function Value(i as integer, j as integer) as single
    Declare Sub Set(i as integer, j as integer,value as single)
   
    Declare function Transpose() as matrix
   
    Declare Sub LoadIdentity()
    Declare Sub Empty()
End type

Constructor Matrix(sx as integer, sy as integer)

    this.SizeX = sx
    this.SizeY = sy
   
    Dat = Callocate(sx*sy+1,Sizeof(single))

End Constructor

Operator Matrix.Cast() as string
   
    dim as string finStr
    dim as integer iter
   
    for j as integer = 0 to this.SizeY-1
       
        finStr += "["
       
        For i as integer = 0 to this.SizeX-1
           
            finStr += " "+str(this.Dat[iter])
            iter+=1
       
        next
       
        finStr += " ]"+nl
       
    next
   
    return finStr
   
End Operator

Operator + (LM as matrix, RM as matrix) as matrix
   
    if LM.SizeX<>RM.SizeX or LM.SizeY<>RM.SizeY then exit Operator
   
    Dim as matrix newMat = matrix(LM.SizeX,LM.SizeY)
   
    dim as integer iter
   
    for i as integer = 0 to LM.SizeX-1
        for j as integer = 0 to RM.SizeY-1
           
            NewMat.dat[iter] = LM.dat[iter] + RM.Dat[iter]
            iter+=1
        next
    next
   
    return NewMat
   
end Operator   

Operator - (LM as matrix, RM as matrix) as matrix
   
    if LM.SizeX<>RM.SizeX or LM.SizeY<>RM.SizeY then exit Operator
   
    Dim as matrix newMat = matrix(LM.SizeX,LM.SizeY)
   
    dim as integer iter
   
    for i as integer = 0 to LM.SizeX-1
        for j as integer = 0 to RM.SizeY-1
           
            NewMat.dat[iter] = LM.dat[iter] - RM.Dat[iter]
            iter+=1
        next
    next
   
    return NewMat
   
end Operator

Operator *(LM as matrix, RM as matrix) as matrix
   
    if LM.SizeX<>RM.SizeY or LM.SizeY<>RM.SizeX then exit Operator
   
    Dim as matrix newMat = matrix(RM.SizeY,LM.SizeX)
   
    dim as single curVal
   
    dim as integer iter
   
    for j as integer = 0 to newMat.SizeY-1
        for i as integer = 0 to newMat.SizeX-1
           
            curVal = 0
           
            for k as integer = 0 to LM.SizeX-1
                   
                curVal += LM.dat[k+j*LM.SizeX] * RM.dat[i+k*RM.SizeX]
               
            next
           
            newMat.dat[iter] = curval
           
            iter+=1
           
        next
    next
   
    return NewMat
   
end Operator

Operator *(LM as matrix, RM as single) as matrix
   
    Dim as matrix newMat = matrix(LM.SizeX,LM.SizeY)
   
    dim as integer iter
   
    for i as integer = 0 to newMat.SizeX-1
        for j as integer = 0 to newMat.SizeY-1
           
            newMat.dat[iter] = RM * LM.dat[iter]
           
            iter+=1
           
        next
    next
   
    return NewMat
   
end Operator

Sub Matrix.Empty()
   
    for i as integer = 0 to this.SizeX*this.SizeY-1
        this.Dat[i] = 0
    next

end sub

Sub Matrix.LoadIdentity()
   
    if this.SizeX <> this.SizeY then exit sub
   
    for i as integer = 0 to this.SizeX-1
        for j as integer = 0 to this.SizeY-1
            this.set i,j,iif(i=j,1,0)
        next
    next
   
end sub

function Matrix.Transpose() as Matrix
   
    dim as matrix tmp = matrix(this.SizeY,this.SizeX)
   
    For j as integer = 0 to this.SizeY-1
        for i as integer = 0 to this.SizeX-1
           
            tmp.dat[i+j*this.SizeY] = this.dat[j+i*this.SizeX]

        next
    next
   
    return tmp
   
end function

Function Matrix.Value(i as integer, j as integer) as single
   
    return this.Dat[i+j*this.SizeX]

end function

Sub Matrix.Set(i as integer, j as integer,value_m as single)
   
    this.Dat[i+j*this.SizeX] = value_m
   
end sub


matrixExample.bas

'' Matrix.bi Example file
''
'' 2008 - Hezad
''
''
''  Contact : Hezad0@Gmail.com


#include "matrix.bi



Dim as matrix A = Matrix(3,3), B = Matrix(3,3), ID = Matrix(3,3)

for i as integer = 0 to 2
    For j as integer = 0 to 2
        A.Set i,j,i*2+j
    next
next


Print "A =>"
Print A



B = A*A
Print "B = A*A =>"
Print B



B = A.Transpose
Print "B = A.Transpose =>"
Print B



ID.LoadIdentity
Print "ID.LoadIdentity =>"
Print ID



B.Empty
Print "B.Empty =>"
Print B



B = A*ID
Print "B = A*ID (=ID*A) =>"
Print B

sleep




edit : Attached a .rar (tested the header with the engine.., a funny bug to see : try to rotate the camera with your mouse ^^)
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: hellfire on October 31, 2008
You won't need matrices of different size.
I suggest to use 4x3 matrices which is a 3x3 matrix with an additional translation-vector.
Most APIs use 4x4 matrices, but I'm pretty sure you won't need those for a while.

Matrix:

xx xy xz   xp
yx yy yz   yp
zx zy zz   zp
[rotation] [position]

Try to understand that the 3x3 part consists of three vectors.
These vectors are the axes of your coordinate system:
(xx,yx,zx) points to the right
(xy,yy,zy) points up
(xz,yz,zz) points into depth

Identity matrix (transforms x,y,z to the same x,y,z):

1 0 0   0
0 1 0   0
0 0 1   0

Notice that the bases are the cartesian axes (transformation to the vector-space you're already in).

A vector describes a position or direction relative to a coordinate system.
When transforming a vector (x,y,z) to another coordinate-system, the components of the vector are then relative to new coordinate axes:

x'= xx*x + xy*y + xz*z + xp
y'= yx*x + yy*y + yz*z + yp
z'= zx*x + zy*y + zz*z + zp


Now we want to transform using two matrices "m1" and "m2":

  first transform (x,y,z) to (x1,y1,z1) using m1 (using equation above)
  x1= m1.xx*x + m1.xy*y + m1.xz*z + m1.xp
  y1= m1.yx*x + m1.yy*y + m1.yz*z + m1.yp
  z1= m1.zx*x + m1.zy*y + m1.zz*z + m1.zp

  then transform (x1,y1,z1) to (x2,y2,z2) using m2
  x2= m2.xx*x1 + m2.xy*y + m2.xz*z + m2.xp
  y2= m2.yx*x1 + m2.yy*y + m2.yz*z + m2.yp
  z2= m2.zx*x1 + m2.zy*y + m2.zz*z + m2.zp


This is twice the work, so we're looking for new matrix "n" which does both transforms at once.
We simply plug the first equation into the second and reorder back to matrix-layout (just shown for one component):

  x2= m2.xx * (m1.xx*x + m1.xy*y + m1.xz*z + m1.xp)   (equation for x1)
    + m2.xy * (m1.yx*x + m1.yy*y + m1.yz*z + m1.yp)   (equation for y1)
    + m2.xz * (m1.zx*x + m1.zy*y + m1.zz*z + m1.zp)   (equation for z1)
    + m2.xp

=> remove braces
  x2= m2.xx*m1.xx*x + m2.xy*m1.yx*x  + m2.xz*m1.zx*x
    + m2.xx*m1.xy*y + m2.xy*m1.yy*y + m2.xz*m1.zy*y
    + m2.xx*m1.xz*z + m2.xy*m1.yz*z + m2.xz*m1.zz*z
    + m2.xx*m1.xp + m2.xy*m1.yp + m2.xz*m1.zp + m2.xp

=> refactor to x,y,z:
  x2= (m2.xx*m1.xx+m2.xy*m1.yx+m2.xz*m1.zx) * x
    + (m2.xx*m1.xy+m2.xy*m1.yy+m2.xz*m1.zy) * y
    + (m2.xx*m1.xz+m2.xy*m1.yz+m2.xz*m1.zz) * z
    + (m2.xx*m1.xp + m2.xy*m1.yp + m2.xz*m1.zp + m2.xp)

So here we've got the first row of a matrix which transform "x" directly to "x2".

The complete new matrix "n" looks like this:

   n.xx= m2.xx*m1.xx + m2.xy*m1.yx + m2.xz*m1.zx
   n.xy= m2.xx*m1.xy + m2.xy*m1.yy + m2.xz*m1.zy
   n.xz= m2.xx*m1.xz + m2.xy*m1.yz + m2.xz*m1.zz
   n.xp= m2.xx*m1.xp + m2.xy*m1.yp + m2.xz*m1.zp + m2.xp

   n.yx= m2.yx*m1.xx + m2.yy*m1.yx + m2.yz*m1.zx
   n.yy= m2.yx*m1.xy + m2.yy*m1.yy + m2.yz*m1.zy
   n.yz= m2.yx*m1.xz + m2.yy*m1.yz + m2.yz*m1.zz
   n.yp= m2.yx*m1.xp + m2.yy*m1.yp + m2.yz*m1.zp + m2.yp

   n.zx= m2.zx*m1.xx + m2.zy*m1.yx + m2.zz*m1.zx
   n.zy= m2.zx*m1.xy + m2.zz*m1.zy + m2.zy*m1.yy
   n.zz= m2.zx*m1.xz + m2.zy*m1.yz + m2.zz*m1.zz
   n.zp= m2.zx*m1.xp + m2.zy*m1.yp + m2.zz*m1.zp + m2.zp

Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Clyde on October 31, 2008
@Hezad: No worries dude, btw peeps is a way I use to say people.
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on November 02, 2008
@hellfire :

Thanks a lot mate, it'll definitely be helpful :) k++

@Clyde : thanks for the precision :D
Title: Re: WIP 3D Engine (Update : Super-Sampling, Heightmap, Fog) [v0.05]
Post by: Hezad on November 05, 2008
Finally !!!!!!!!!

UPDATE !

The engine hasn't progressed for a while because of the matrix stuff, I didn't know how to implement all this, even with the very good help I received here. But finally, the camera rotation now works :D Camera movement (correlated with its rotation) isn't implemented yet (or .. well, it's implemented but not working :P)

So I'm proud to announce the version 0.055 ! Here is the change log :

Quotev0.055 Changes List



MAJOR :

- Added Material Opacity handling.

- Changed all the Render Subs. One Render procedure for each render type
   > Render_Opt_ZBuffer
   > Render_Opt_MipMap
   > Render_Flat_NoTex
   > Render_Flat_NoTex_Fog
   > Render_Flat_Tex
   > Render_Flat_Tex_Fog
   > Render_Smooth_NoTex
   > Render_Smooth_NoTex_Fog
   > Render_Smooth_Tex
   > Render_Smooth_Tex_Fog

- Camera now uses a matrix for the engine to rotate Viewport !
        > New header : matrix.bi (handles matrices)


Minor :

- Added Water Texture.


Known bugs :

- Camera movement to code

- Transparent Objects must be the last projected objects.

Attached : *.rar archive :)



edit : camera movement is now totally fixed :D I didn't uploaded the fix, but you'll see that with the 0.06 :D
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 05, 2008
Looks very nice but still needs some serious optimizing ;)
(the zbuffer-view already got pretty fast, though!)

Your exponential fog is rather expensive:

blendfunc = exp(-(tmp_Z*_MAX_Z) * fog_Density)
Cr = blendfunc * Cr + (1-blendfunc) * Fog_Color.r
Cg = blendfunc * Cg + (1-blendfunc) * Fog_Color.g
Cb = blendfunc * Cb + (1-blendfunc) * Fog_Color.b

Generally I would recommend not to call any trigonometric functions on a per pixel basis ;)
You can do a second pass which looks up the fog-density from the significant bits of zbuffer using a small table.
This way your polyfiller's innerloop gets tighter and the blending is only done once per pixel (due to overdraw you'll usually plot every pixel more than once).
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Shockwave on November 05, 2008
I think that it looks great Hezad, it really does!

As Hellfire says, it really needs optimising though, please see the attachment.

This attachment was written by Stonemonkey, he developed a similar engine to yours, this example doesn't have fog etc, but take a look, this is the sort of speed you can achieve with optimisation.
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 05, 2008
@hellfire : I get the point, thanks :) Couldn't I create an Exp() look up table ? I mean precalculate a huge Exp() table and seeking the good value inside ?


@Shockwave : Thanks for the demo ...     those ~55 FPS make me dream ^^


@Both :
Thanks for the feedback :) Yep, optimizing is bloody necessary now if I want to continue to add features :S
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Shockwave on November 05, 2008
Your new features will not look thier best without those optimisations Hezad, but take heart.

Stonemonkeys demo there has quite a lot of triangles, gourad shading, perspective correct mapping, clipping and mip mapping and the fps is limited too.. It can go even faster than that so there's no reason why you should not be able to at least treble your fps in time :)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 05, 2008
There seems to be some lack of precision.
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 05, 2008
@Shockwave :
thanks :) I hope I'll succeed in such an objective ^^

@hellfire :
ouch ! Never noticed it o_O Could it come from not precise enough data types ? (eg : single instead of double)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 05, 2008
QuoteCould it come from not precise enough data types ? (eg : single instead of double)
No. One reason is missing subpixel-/-texel-precision (I wrote something about that somewhat earlier).
The problem is that the coordinates you interpolate along the slopes of your triangle are valid for a floating-point position (cyan dots) on the edge, but the actual (integer) pixel-position is significantly different:
(http://www.abload.de/img/rasterizem710.gif)
(image assumes all coordinates are rounded up)

Concerning optimization I restructured your source a bit.
It's still far from optimal but maybe you can get some ideas out of it:
#Macro SmoothLine_Tex_Fog(x1,x2,y, _
                    U_z1,V_z1,_
                    U_z2,V_z2,_
                    Texture, _
                    r1,r2,g1,g2,b1,b2,_
                    _z1,_z2,_
                    CurOpacity)
   
    dim as integer dR,dG,dB
    dim as integer r,g,b
    dim as single sr1,sr2,sg1,sg2,sb1,sb2
    dim as single dU_z,dV_z,U_z,V_z
    dim as single Uu_z1,Vv_z1,Uu_z2,Vv_z2
    dim as integer itx1,itx2
    dim as single xDiv,Cur_Z,_zz1,_zz2,d_Z
    dim as integer it, LODVal, MipU, MipV
    dim as integer CurTex,Cr,Cg,Cb
    dim as single tmp_Z

    ' this isn't required when all polygons have equal orientation!
    if x1<x2 then
        _zz1 = _z1
        _zz2 = _z2
       
        itx1 = x1
        itx2 = x2
       
        Uu_z1 = u_z1
        Uu_z2 = u_z2
        Vv_z1 = v_z1
        Vv_z2 = v_z2
       
        sr1 = r1
        sr2 = r2
        sg1 = g1
        sg2 = g2
        sb1 = b1
        sb2 = b2
    else
        _zz1 = _z2
        _zz2 = _z1
       
        itx1 = x2
        itx2 = x1
       
        Uu_z1 = u_z2
        Uu_z2 = u_z1
        Vv_z1 = v_z2
        Vv_z2 = v_z1
       
        sr1 = r2
        sr2 = r1
        sg1 = g2
        sg2 = g1
        sb1 = b2
        sb2 = b1
    end if
   
    ' deltas can be computed once per triangle!

    xDiv = 1/(itx2 - itx1)

    ' delta rgb (integer)
    dR = xDiv * (sr2 - sr1) * 256.0
    dG = xDiv * (sg2 - sg1) * 256.0
    dB = xDiv * (sb2 - sb1) * 256.0

    ' delta uvz (float)
    d_Z = xDiv * (_zz2 - _zz1)
    dU_z = xDiv * (Uu_z2 - Uu_z1)
    dV_z = xDiv * (Vv_z2 - Vv_z1)
   
    Cur_Z = _zz1

    U_z = Uu_z1
    V_z = Vv_z1
   
    r = sr1 * 256.0
    g = sg1 * 256.0
    b = sb1 * 256.0
           
    it = y*xRes + itx1
       
    dim length as integer
    length= itx2-itx1
   
    dim u1 as integer
    dim v1 as integer
    dim u2 as integer
    dim v2 as integer
    dim du as integer
    dim dv as integer

    dim shiftu as integer
    dim shiftv as integer

    ' get first set of perspective correct u,v
    tmp_Z = 65536.0/Cur_Z
    u1 = U_z*tmp_Z*Texture.SizeX
    v1 = V_z*tmp_Z*Texture.SizeY
   
    while length>0
      ' draw 16 pixels (or rest of scanline)
      dim size as integer
      size= 16
      if size > length then size= length
      length-=size

      ' step 16 pixels further
      U_z += dU_z*16
      V_z += dV_z*16
     
      ' get 2nd set of perspective u,v
      tmp_Z = 65536.0/(Cur_Z+d_Z*16)
      u2 = U_z*tmp_Z*Texture.SizeX
      v2 = V_z*tmp_Z*Texture.SizeY
     
      ' linear deltas
      du= (u2-u1) shr 4
      dv= (v2-v1) shr 4

      ' mip-level from deltas
      mipu = log2( abs(du shr 16) )
      mipv = log2( abs(dv shr 16) )
      LODVal = iif(mipU>mipV,MipU,mipV)
      If LODVal>Texture.NbLOD then LODVal = Texture.NbLod

      ' store this in Texture
      shiftu= log2(Texture.SizeX)
      shiftv= log2(Texture.SizeY)

      shiftu-= LODVal
      shiftv-= LODVal
     
      dim masku as integer
      dim maskv as integer
           
      masku= (1 shl shiftu)-1
      maskv= (1 shl shiftv)-1

      for i as integer = 1 to size
       
        If Cur_Z > ZBuffer(it) then
               
            ' store 1/z in zbuffer (zbuffer is now single and cleared to 0!)
            ZBuffer(it) = Cur_Z
           
            dim u as integer
            dim v as integer

            u= (u1 shr (16+LODVal)) and masku
            v= (v1 shr (16+LODVal)) and maskv
           
'            CurTex = Texture.GFX[ (v shl 8) or u ]
            CurTex = Texture.MipMap(LODVal)[ (v shl shiftu) or u ]
           
            Cb = (CurTex and 255)
            Cg = ((CurTex shr 8) and 255)
            Cr = ((CurTex shr 16) and 255)
               
            Cb= Cb*b shr 16
            Cg= Cg*g shr 16
            Cr= Cr*r shr 16
           
            ScrPtr[it] = rgb(Cr,Cg,Cb)
           
        end if
       
        it+=1
           
        r += dR
        g += dG
        b += dB
        u1+= du
        v1+= dv
        Cur_Z+=d_Z
           
      Next
     
      ' reuse 2nd set of u,v
      u1= u2
      v1= v2
     
    Wend
   
#EndMacro

(Skipped the fog- & transparency-parts for readability)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 06, 2008
thanks, Thanks, THANKS ;D I'm reading it, and it's really interesting :)

edit : I just tried it in my code ... DUDE, YOU SHOULD BE CANONIZED BY THE CODERS CHURCH  :goodpost: I get between 15 and 25 FPS (35 if just ~500 triangles are in view) instead of the 9 FPS I got before :P yehookey, the v0.06 will have a big improvement in speed, thanks to you mate.

I have a question though,
In a comment, you say :

Quote' deltas can be computed once per triangle!

But I don't understand how it could be possible :S Since I use 1/(x2-x1) to calculate the deltas, don't I need to recalc it on each scanline ((x2-x1) changes on every line) ?

Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 06, 2008
Quote
Quotedeltas can be computed once per triangle!
But I don't understand how it could be possible :S
Well, the reason why this is possible is a bit difficult to explain without rolling out a lot of math.
It's probably easy to understand by showing a picture:
(http://www.abload.de/img/perspective-uvxnzv.jpg)
On the left you see a textured cube with some perspective.
The texture-coordinates are interpolated as u/z, v/z, 1/z and reconstructed to u'= (u/z)/(1/z) v'= (v/z)/(1/z) (just the same as you do now).
In the middle you can see the perspective-correct texture-coordinates u',v' shown as red and green (a bit differently scaled so you can see a gradient).
On the right you can see the interpolated u/z and v/z (also in red and green).
What you can see here is that the gradients are parallel(!)
That's the reason why you can interpolate them linearly (instead of the original u,v).
This also means that the deltau,deltav between two pixels doesn't change from one scanline to another but is *always* the same.
To get the highest precision, you can simply calculate the deltas at the longest scanline.

QuoteI get between 15 and 25 FPS (35 if just ~500 triangles are in view) instead of the 9 FPS I got before
A big portion of the speed-improvement was achieved by removing float-to-integer-conversions.
Conversion is expensive because cpu & fpu don't have a direct connection but need to write/read values from memory.
In your source there are many more situations that can benefit significantly from such optimization.
My personal favourite is this:
For i as integer = 0 to xRes-1 step 2
    For j as integer = 0 to yRes-1 step 2
        RenderPtr[i*.5+xRes_2*j*.5] = ...


The key to proper optimization is to find those functions where your cpu spends most of the time.
Luckily there's a counter in your cpu which is increased with each processor-cycle.
You can simply query it before and after a function-call and the difference is the number of cycles spend inside the function.
To query the counter you can use this:

function getCpuTick() as double
    dim as double ticks
    asm
        lea edi,[ticks]
rdtsc
mov [edi],eax
mov [edi+4],edx
fild qword ptr [edi]
fstp qword ptr [edi]
    end asm
    return ticks
end function

Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 06, 2008
Thanks for all those tips, it'll definitely help me to optimize the engine :)

I implemented in every render procedure the restructured code you shared two or three posts ago and it work pretty well :) As I said, the speed improvement is totally noticeable, thanks again for that. But there's a little problem. It seems like the texture tiling doesn't work anymore ! Well in fact it works perfectly on the terrain, but the sphere I use for the sky is really weirdly tiled. I attach a screenshot to show what I mean. And I post here the render procedure used for the skysphere :

#Macro FlatLine_Tex(x1,x2,y, _
                    U_z1,V_z1,_
                    U_z2,V_z2,_
                    Texture, _
                    r_,g_,b_,_
                    _z1,_z2,_
                    CurOpacity)
   
    dim as integer r,g,b
    dim as single dU_z,dV_z,U_z,V_z
    dim as single Uu_z1,Vv_z1,Uu_z2,Vv_z2
    dim as integer itx1,itx2
    dim as single xDiv,Cur_Z,_zz1,_zz2,d_Z
    dim as integer it, LODVal, MipU, MipV
    dim as integer CurTex,Cr,Cg,Cb
    dim as single tmp_Z

    ' this isn't required when all polygons have equal orientation!
    if x1<x2 then
        _zz1 = _z1
        _zz2 = _z2
       
        itx1 = x1
        itx2 = x2
       
        Uu_z1 = u_z1
        Uu_z2 = u_z2
        Vv_z1 = v_z1
        Vv_z2 = v_z2
       
    else
        _zz1 = _z2
        _zz2 = _z1
       
        itx1 = x2
        itx2 = x1
       
        Uu_z1 = u_z2
        Uu_z2 = u_z1
        Vv_z1 = v_z2
        Vv_z2 = v_z1
       
    end if
   
    ' deltas can be computed once per triangle!

    xDiv = 1/(itx2 - itx1)

    ' delta uvz (float)
    d_Z = xDiv * (_zz2 - _zz1)
    dU_z = xDiv * (Uu_z2 - Uu_z1)
    dV_z = xDiv * (Vv_z2 - Vv_z1)
   
    Cur_Z = _zz1

    U_z = Uu_z1
    V_z = Vv_z1
   
    r = r_ * 256.0
    g = g_ * 256.0
    b = b_ * 256.0
           
    it = y*xRes + itx1
       
    dim length as integer
    length= itx2-itx1
   
    dim u1 as integer
    dim v1 as integer
    dim u2 as integer
    dim v2 as integer
    dim du as integer
    dim dv as integer

    dim shiftu as integer
    dim shiftv as integer

    ' get first set of perspective correct u,v
    tmp_Z = 65536.0/Cur_Z
    u1 = U_z*tmp_Z*Texture.SizeX
    v1 = V_z*tmp_Z*Texture.SizeY
   
    while length>0
      ' draw 16 pixels (or rest of scanline)
      dim size as integer
      size= 16
      if size > length then size= length
      length-=size

      ' step 16 pixels further
      U_z += dU_z*16
      V_z += dV_z*16
     
      ' get 2nd set of perspective u,v
      tmp_Z = 65536.0/(Cur_Z+d_Z*16)
      u2 = U_z*tmp_Z*Texture.SizeX
      v2 = V_z*tmp_Z*Texture.SizeY
     
      ' linear deltas
      du= (u2-u1) shr 4
      dv= (v2-v1) shr 4

      ' mip-level from deltas
      mipu = eng_log2( abs(du shr 16) )
      mipv = eng_log2( abs(dv shr 16) )
      LODVal = iif(mipU>mipV,MipU,mipV)
      If LODVal>Texture.NbLOD then LODVal = Texture.NbLod

      ' store this in Texture
      shiftu= eng_log2(Texture.SizeX)
      shiftv= eng_log2(Texture.SizeY)

      shiftu-= LODVal
      shiftv-= LODVal
     
      dim masku as integer
      dim maskv as integer
           
      masku= (1 shl shiftu)-1
      maskv= (1 shl shiftv)-1

      for i as integer = 1 to size
       
        If Cur_Z > ZBuffer(it) then
               
            ' store 1/z in zbuffer (zbuffer is now single and cleared to 0!)
            ZBuffer(it) = Cur_Z
           
            dim u as integer
            dim v as integer

            u= (u1 shr (16+LODVal)) and masku
            v= (v1 shr (16+LODVal)) and maskv
           
'            CurTex = Texture.GFX[ (v shl 8) or u ]
            CurTex = Texture.MipMap(LODVal)[ (v shl shiftu) or u ]
           
            Cb = (CurTex and 255)
            Cg = ((CurTex shr 8) and 255)
            Cr = ((CurTex shr 16) and 255)
               
            Cb = Cb*b shr 16
            Cg = Cg*g shr 16
            Cr = Cr*r shr 16
           
            If CurOpacity<>1 then
                dim as integer br,bg,bb,ptVal
               
                ptVal = ScrPtr[it]
               
                br = (ptVal shr 16) and 255
                bg = (ptVal shr 8) and 255
                bb = ptVal and 255

                Cr = CurOpacity * (Cr - Br) + Br
                Cg = CurOpacity * (Cg - Bg) + Bg
                Cb = CurOpacity * (Cb - Bb) + Bb
           
            end if
           
            if Use_Fog then
                Dim as single BlendFunc
               
                select case fog_method
                case LINEAR
                   
                    blendfunc = 1-_MAX_Z/ZBuffer(it)
                       
                    Cr = BlendFunc * (Cr - Fog_Color.r) + Fog_Color.r
                    Cg = BlendFunc * (Cg - Fog_Color.g) + Fog_Color.g
                    Cb = BlendFunc * (Cb - Fog_Color.b) + Fog_Color.b
                   
                case EXPONENTIAL
                   
                    blendfunc = exp(-(_MAX_Z/ZBuffer(it)) * fog_Density)
                       
                    Cr = BlendFunc * (Cr - Fog_Color.r) + Fog_Color.r
                    Cg = BlendFunc * (Cg - Fog_Color.g) + Fog_Color.g
                    Cb = BlendFunc * (Cb - Fog_Color.b) + Fog_Color.b
                       
                case HETEROGENOUS
                    '' placeholder
                end select
            end if
           
            ScrPtr[it] = rgb(Cr,Cg,Cb)
           
        end if
       
        it+=1

        u1+= du
        v1+= dv
        Cur_Z+=d_Z
           
      Next
     
      ' reuse 2nd set of u,v
      u1= u2
      v1= v2
     
    Wend
   
#EndMacro



( its the macro used for Flat shading, but I tried with gouraud shading and there's the same problem :S )

As you noticed, I didn't change the deltas calculations to be done once per triangle yet, I'd like to understand what's going on with the tiling before :)

For information, I tried to change the tiling coefficient (I tried higher and lower) and it seems like the selected mipmap level is wrong with big triangles (like the ones on the skysphere), don't know if it can help.

thanks again !
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 07, 2008
There's some ugly inconsistency in your mipmap handling:
TextureT.MipMap(x) starts at x=1 (which contains the first mipmap).
x can be 0 though (the original texture is to be used), which causes an out-of-bounds array-access.
Accidentally the correct pointer "GFX" is stored at that adress ;D
I strongly recommend to handle that a bit cleaner.
This doesn't seem to cause your problem, though.
Where do you scale your texture-coordinates according to MaterialT.TileX/.TileY ?
And what exactly does eng_log2() do and how does it differ from Log2() ?
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 07, 2008
Hey :)

Quotex can be 0 though (the original texture is to be used), which causes an out-of-bounds array-access.
Accidentally the correct pointer "GFX" is stored at that adress Grin
I strongly recommend to handle that a bit cleaner.

Yep, i'll change that :)

QuoteWhere do you scale your texture-coordinates according to MaterialT.TileX/.TileY ?

well in fact each UV coords of a triangle is multiplicated by TileX/Y variable before rendering the textures. That's why I used U = U-int(U) before (to get the U val between 0 and 1). I tried to use it with your code but I was a bit lost between the shifts, UV calcs, etc.. :P

QuoteAnd what exactly does eng_log2() do and how does it differ from Log2() ?

oh sorry I forgot the tell about it :P in fact eng_log2() is EXACTLY the same as the previous Log2() ^^ just the name changed (that was because I tried the crt/maths.bi version of exp_() to see if it was faster but it was not. I changed the name of the sub because there was a sub in maths.bi also called Log2() )
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 07, 2008
Sorry Hazard, I forgot to mention that texture-addressing assumes the texture-size to by a power of 2 (your sky-texture is 300x300 pixels though).
I believe this is a reasonable limitation and matches well with the concept of mipmapping.
Another thing I noticed is that, when calculating the scanline-deltas, you use the integer-coordinates of x1,x2:

    dim as integer itx1,itx2
    ...
    itx1 = x1
    itx2 = x2
    ...
    xDiv = 1/(itx2 - itx1)
    d_Z = xDiv * (_zz2 - _zz1)
    ...

That makes you loose a lot of precision.
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 07, 2008
I changed the size of the texture to be 256*256 and it works PERFECTLY :D thanks :) I don't even know how I could forget it (the power of 2 thing) >_<

Quote[...] That makes you loose a lot of precision.
hum.. Once again I don't understand lol since the x delta is based on the screen pixels delta, shouldn't it be integers coords anyway ?
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 07, 2008
Quotesince the x delta is based on the screen pixels delta, shouldn't it be integers coords anyway ?

Have a look at the image (triangle) above again...
You interpolated u,v,z along the left and right side of your triangle.
These coordinates are valid on the edge of the triangle (precisely at the x-coordinate which was interpolated, too).
The pixel starts somewhat later (or earlier, depends on rounding - later you should also take into acount the space "skipped" from the edge to the first pixel of the scanline).
So when dividing by the number of pixels you're somewhat away from the actual space between the two sets of coordinates.

In other words:
If you have 7.9 and want to know how much is 1.0 you wouldn't divide it by 7 ;)

Of course the same applies to the interpolation along the edges (y-direction).


Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 07, 2008
QuoteHave a look at the image (triangle) above again...

erm .. yeah sorry I spoke before thinking >< I modified the render subs and macros and it looks way better :) Now I notice a small gap when I look for clipped triangles from a really near point of view. But it must be a precision stuff again, I'll check that :)

I continue to optimize stuff with the advises you shared :)


oh by the way, my pseudo's not "hazard"  :2funny:

anyway, thanks again :)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 07, 2008
Quoteoh by the way, my pseudo's not "hazard"
Oops, sorry dude.
I also just realized that I have three folders on my pc, each containing a different version of your engine.
Their names are "hazad", "hezard" and "hazerd"  ;D
Feel honoured, they're usually called "crap" or "shit" ;)
But I promise to pay some more attention from now on :)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 07, 2008
Don't worry, I don't feel outraged ^^ And I am honored to have my own folders on your hard-drive :D
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 10, 2008
Now that the zbuffer contains floating-point-values, the ztest became a bit suboptimal:

fld dword ptr [ebp-380]    ' load Cur_Z (floating-point)
fcomp dword ptr [eax]      ' Compare with Zbuffer(it)
push eax                   ' Save Register ax
fnstsw ax                  ' load fpu-state to ax
test ah, 0b01000001        ' test for bit containing "lower" state
pop eax                    ' restore register ax
jnz .Lt_05EA               ' jump if bit was set
  ' render pixel
.Lt_05EA:
  ' continue

(piece of assembly-output from the freebasic compiler)

As this is a general overhead even for those pixels which don't pass the ztest, I suggest you scale your interpolated z-value to reasonable size and store it into an integer zbuffer:

dim as integer Cur_Z,d_Z
...
d_Z = xDiv * (_zz2 - _zz1) * 2147483647.0
Cur_Z = _zz1 * 2147483647.0

for i as integer = 1 to size
  If Cur_Z > ZBuffer(it) then
    ZBuffer(it) = Cur_Z
    ...
  end if
  ...
  Cur_Z+=d_Z
Next

(Using 2147483647.0 [2^31-1] assumes your front-clipplane is >=1)

Alternatively, because the binary bitset of strictly increasing positive floating-point values is also strictly increasing, you can simply "assume them as integers":
Make integer-pointers to your (single-)zbuffer and (single-)z-value and compare those.
Both ways end up equally:

mov eax, dword ptr [ebp-380] ' load Cur_Z (inetger)
cmp dword ptr [eax]          ' Compare with Zbuffer(it)
jle .Lt_05EA                 ' jump if smaller or equal
' render
.Lt_05EA:
' skip


If your sub (including macros) is called often and has a lot of local variables, declare them static.
Otherwise the compiler will initialize all local variables to zero when it is called, which looks like this:
mov dword ptr [ebp-4], 0
mov dword ptr [ebp-8], 0
mov dword ptr [ebp-12], 0
mov dword ptr [ebp-16], 0
mov dword ptr [ebp-20], 0
mov dword ptr [ebp-24], 0
mov dword ptr [ebp-28], 0
mov dword ptr [ebp-32], 0
mov dword ptr [ebp-36], 0
mov dword ptr [ebp-40], 0
mov dword ptr [ebp-44], 0
mov dword ptr [ebp-48], 0
mov dword ptr [ebp-52], 0
mov dword ptr [ebp-56], 0
mov dword ptr [ebp-60], 0
mov dword ptr [ebp-64], 0
mov dword ptr [ebp-68], 0
mov dword ptr [ebp-72], 0
mov dword ptr [ebp-76], 0
mov dword ptr [ebp-80], 0
mov dword ptr [ebp-84], 0
mov dword ptr [ebp-88], 0
mov dword ptr [ebp-92], 0
mov dword ptr [ebp-96], 0
mov dword ptr [ebp-100], 0
mov dword ptr [ebp-104], 0
mov dword ptr [ebp-108], 0
mov dword ptr [ebp-112], 0
mov dword ptr [ebp-116], 0
mov dword ptr [ebp-120], 0
mov dword ptr [ebp-124], 0
mov dword ptr [ebp-128], 0
mov dword ptr [ebp-132], 0
mov dword ptr [ebp-136], 0
mov dword ptr [ebp-140], 0
mov dword ptr [ebp-144], 0
mov dword ptr [ebp-148], 0
mov dword ptr [ebp-152], 0
mov dword ptr [ebp-156], 0
mov dword ptr [ebp-160], 0
mov dword ptr [ebp-164], 0
mov dword ptr [ebp-168], 0
mov dword ptr [ebp-172], 0
mov dword ptr [ebp-176], 0
mov dword ptr [ebp-180], 0
mov dword ptr [ebp-184], 0
mov dword ptr [ebp-188], 0
mov dword ptr [ebp-192], 0
mov dword ptr [ebp-196], 0


In TextureT.GenerateMipMap you should take into account that texture do not need to be quadratic.
Especially for sky-domes it's common to have 1024x256 or something like that.
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 10, 2008
Cool :D thanks a lot for this tip, I'll code it immediately :)

edit : erm .. Since d_Z is used for the U,V coordinates and LOD calculation, and since theses are already subjects to integers conversions (the reorganization you shared some posts ago), if I modify d_Z, all the nexts calcs involving it become wrong :S Must I reconvert d_Z before ?

A little preview of a new feature of the v0.06 before I release it : the klein bottle generation :D

(You can see a little light problem on the screenshot, it's because of the normals, since the Klein has only one side, some normals are not okay, I don't really know how to fix that for now but it's not a problem, the Klein bottle generation is more a gadget than anything else :D )
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 10, 2008
Nice pipe ;D

d_Z isn't required for LOD calculation if you lay out your code as I suggested before.
If it's a problem to scale the interpolated z, you can also avoid the float-compare as described above:
dim as integer ptr izbuf= zbuffer
dim as integer ptr izval = @Cur_Z

...
for i as integer = 1 to size
  if *izval > izbuf(it) then
    zbuffer(it)= Cur_Z
    ...
  end if

  Cur_Z+=d_Z
  ...
end

(untested)

Another thing you should look after is the fact that your triangle-structure is 696 Bytes big!
It doesn't make sense to store a copy(!) of the material for each triangle.
I suggest keeping an array of (different) materials, each with a list of triangles.
Also try to reduce the vertex-size to a minimum. Remember: A "vertex" is the data necessary for rasterization and not a trashbin of coordinates ;)
Personally I would create different types of materials, each doing a specified job.

Notice that the compare in GenerateTore and GenerateSphere doesn't work due to rounding errors:

dPhi = PIx2/nbFaces
for theta as single = 0 to PIx2 step dphi
  ...
  if theta<>PIx2 then  ' this is always the case!

- seems like the sphere has an additional row of triangles (tested with this (http://www.abload.de/image.php?img=skysphere3tst.jpg) texture)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 10, 2008
Heya :) Thanks again for all this ! I already implemented the Material list and I am wondering about the z_buffer change :

I tried and I'm not sure to notice a change in speed, and it brings me to a question : Since the pointer points to an integer, when I do


*zbuff = zbuffer(it)*2147483647.0
*zval = Cur_Z*2147483647.0
       
If *zbuff > *zval then
[...]
,

There's still a single>integer conversion, no ? Maybe I shouldn't multiply by 2147483647.0 before, but if it's the case, I must admit there's something I didn't understand :S
Plus, I must allocate and deallocate the pointer, which makes me loose some cycles again  ???

Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 10, 2008
It's much simpler.
You are not doing any conversion and keep everything in floating point:

redim as single zBuffer(SCREEN_MEM)
dim as single Cur_Z, d_Z     ' the interpolated z-value and delta


Now you get pointers to both:

dim as integer ptr izbuf= @zbuffer(0)
dim as integer ptr izval= @Cur_Z


When addressing a value with the integer pointer, this is handled by the cpu.
All you have to do is replace the compare:
for i as integer = 1 to size
'  if Cur_Z > ZBuffer(it) then  ' old: the fpu handles the fairly slow single-compare
  if *izval > izbuf[it] then    ' new: the compiler thinks these values are integers and lets the cpu do the compare
    ...
  endif
  it+=1
  Cur_Z+=d_Z
  ...           
wend

This works for positive singles only.
Made me go from 21 to 23 fps (initial view, 1002 triangles).

Alternatively you multiply all "1/z" and the corresponding deltas with a fairly big number and make them (and your zbuffer) integer.
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 11, 2008
ah ! Okay I get It now :) It works perfectly :D (excepted I have some "Suspicious pointer assignement" warnings)

Well I'm on all those optimizations then :)

edit : the v0.06 will also have a false phong shader :D (without a big FPS loss)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: benny! on November 11, 2008
Unbelievalble ... project is very cool.

Just out of curiosity .. current version is v0.06.
What is the roadmap to V1.0 ???
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 11, 2008
heya benny :) thanks mate !

Well, my objective for the v1.0 is to have a working, optimized, complete engine, easily usable and modifiable by anyone (with two modes : RT for realtime rendering (games ?) and CG for Computer graphics rendering (supersampling, maybe true phong, why not some reflections, refractions, env. map, Ambiant Occlusion, ...)) and all this, without any bug or flaw .. Of course it can take a veeery long time for those versions to be released ^^ Useless to say there's a huge probability we'll never see those versions :xmas:

However, The main roadmap for the next versions consists in Optimization, OBJ Loader and texture filtering (yeah ...  still have to code it lol)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Shockwave on November 11, 2008
The image of the fake phong looks fabulous, nice one!

It sounds like you still have a lot of work planned for it too... I bet you never planned on doing all this when you started :)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 11, 2008
thanks shocky :D

QuoteI bet you never planned on doing all this when you started Smiley

uhuh totally ^^ When I started, I guess I just wanted to code some texture mapped stuff, maybe little intro with it and nothing more ..  ;D
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hotshot on November 11, 2008
Very nice and I love those Checkboard  8) 8)

Will there be any shadow reflections to the checkboard? i.e. Ball shadow that relections to the checkboard :) :)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 11, 2008
thanks :)

Shadows/Reflections/Refraction .. I'd like to implement them :) But I don't think it'll be real-time .. We'll see :)

A little screen of an improvement of the fake phong shading (highlights follow your view)

Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 11, 2008
Because the "highlight" is a reflection, it doesn't interact with the texture-color (add instead of multiply).
For all the round stuff, environment mapping probably works better and gives some amount of "reflections" for free (compare here (http://hellfire.untergrund.net/stuff/torus.zip)).

Shadows and reflections were very well managable in realtime over a decade ago, just have a look at this (http://www.pouet.net/prod.php?which=312) or that (http://www.pouet.net/prod.php?which=1285) - refraction is a little bit trickier, though.
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 12, 2008
QuoteBecause the "highlight" is a reflection, it doesn't interact with the texture-color (add instead of multiply).

Yeah, but I didn't find how to change it for now (since highlight is calculated per vertex and directly fed in the (Blinn I guess) lighting equation )

QuoteFor all the round stuff, environment mapping probably works better and gives some amount of "reflections" for free (compare here).

That .. is .. bloody .. awesome o___O Yeah, I'd like to add some environment mapping, but the bump mapping does a huge work too in "awesomizing" this torus :D


QuoteShadows and reflections were very well managable in realtime over a decade ago, just have a look at this or that - refraction is a little bit trickier, though.

Both didn't run on my PC :S




The v0.06 is almost ready :D Now I'm just wondering if I must release it and add next optimizations/changes in the v0.07 or wait for those changes to be done ..
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Shockwave on November 12, 2008
Release it! :)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 13, 2008
uhuh I'm glad to see your enthusiasm ^^ Okay, I'll release it this evening, when I'll be back home  :)
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: hellfire on November 13, 2008
QuoteI didn't find how to change it for now (since highlight is calculated per vertex and directly fed in the lighting equation)
Usually diffuse and specular components are interpolated individually across the triangle.
Since the specular highlight (with large exponent) covers only a few triangles, you can either draw those in a separate pass (with additive transparency) or call a different polyfiller if one of the vertices has specular>EPS.

QuoteThat .. is .. bloody .. awesome o___O
thanks :)
Notice the two profiling counters (measured in million-cpu-cycles)
The first shows the time required for vertex-transformation & clipping, the second for filling the polygons.
This way it's easy to find good places to start optimization.

QuoteBoth didn't run on my PC :S
You can try dosbox (http://www.dosbox.com).
Title: Re: WIP 3D Engine (Update : Camera rotation, Opacity) [v0.055]
Post by: Hezad on November 13, 2008
Heya :)

QuoteUsually diffuse and specular components are interpolated individually across the triangle.
Since the specular highlight (with large exponent) covers only a few triangles, you can either draw those in a separate pass (with additive transparency) or call a different polyfiller if one of the vertices has specular>EPS.
oh yeah okay, I'll change that in the next release, thanks :)

QuoteYou can try dosbox.
ah yep, didn't think about it ^^




Well, as promised, here is the v0.06 :D Lots of stuff changed : new example files, faster, a new fake phong shader, ...


- Download Rar(Src + bin + tex + mipmaps) (http://narky00.free.fr/NewSite/MyFiles/Engine_v0.06.rar) -

Sorry, too big to post it here :S (Next time, I'll suppress the mipmaps to gain some space, the engine generates them itself)

Quotev0.06 Changes List



MAJOR :

- Camera is totally handled by matrices (movement + rotation).

- Huge Render Optimization (Won almost 10 FPS)

- NEW SHADER :D => FAKE_PHONG_SHADER : lighting is more realistic



Minor :

- replaced Materials copy by a List of Materials

- Different optimizations (GenerateSphere(), Handle_Super_Sampling, ZBuffer, ..)

- Added Generate_Klein_Bottle Sub

- New Material property : Mat_No_Culling : Will render this material without backface culling (useful to render open shapes/surfaces)

- New Examples



Known bugs :

- Objects with Opacity <> 1 must be projected at last.

- There's a problem with the Klein Bottle normals

- PreCalc_Object_Normals() sub anormaly slow

Attached *.rar :)
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: hellfire on November 13, 2008
Really nice!
The camera-handling works really well now and even the supersampled versions have "interactive framerates" :)
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: Shockwave on November 13, 2008
Definately a huge improvement in framerate Hezad, this is quite a leap ahead from the last version you made :)

My favourite part of your engine is the way that you have implimented the fog. It's excellent and looks exactly the same as the standard opengl implimentations of it.

I know that I said abou the fog looking great before, but it really does look good with all the nice rendermodes now.

Fake phong is fast as well. 
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: Hezad on November 13, 2008
thanks a lot mates :)
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: hellfire on November 14, 2008
I noticed that all triangles have a flag "IsInverted" which is used to flip orientation and causes special-case-handling at many places.
If you want to flip a triangle which is made up by the vertices v1,v2,v3, you can simply change the order of the vertices, for example v1,v3,v2.

In Culling_Visible you don't need to normalize viewpos and viewposnormal.
You can also simplify the computation for ViewPosNormal:

ViewPosNormal.x = Tri.Vertex(1).Viewpos.y*Tri.Vertex(2).Viewpos.z - Tri.Vertex(1).Viewpos.z*Tri.Vertex(2).Viewpos.y
ViewPosNormal.y = Tri.Vertex(1).Viewpos.z*Tri.Vertex(2).Viewpos.x - Tri.Vertex(1).Viewpos.x*Tri.Vertex(2).Viewpos.z
ViewPosNormal.z = Tri.Vertex(1).Viewpos.x*Tri.Vertex(2).Viewpos.y - Tri.Vertex(1).Viewpos.y*Tri.Vertex(2).Viewpos.x

A better way is to test culling in object-space, but I guess that's a bit too far out at the moment.

Objects with higher tesselation won't benefit much from perspective interpolation.
For rather small triangles (and those with small delta-z) you can use linear interpolation.

Try to separate your vertex-data from the triangles. At the moment you're still transforming 6 times as many vertices as would be required.
Also try to do as few transformations on your vertices as absolutely necessary.
Actually there's no need to touch vertices in Rotate_Object.
Just build a rotation-matrix, multiply it with the camera-matrix and do the complete transform (with a single matrix) in RotateViewPos (already mentioned here (http://dbfinteractive.com/forum/index.php?topic=3445.msg49371#msg49371)).
When calculating lighting (where you require .pos3d) you can transform the lights into camera-space (or object-space).

You can generally speed up vector-normalization (extensively used in the lighting-calculation) with the method described here (http://dbfinteractive.com/forum/index.php?topic=3669.0).
Since the specular exponent is constant, you can find an lower bound for (N*H) so that (N*H)^e<EPS.

You're now storing an id of the material in your triangle-structure. It's more efficient to do it the other way round:
In your material-structure store a list of pointers to all meshes which got this material applied.
This way you save the (rather complex) test which material is currently active for every triangle.

You might consider upgrading to fbc 0.2 and use command-line parameter "-fpu sse" to gain ~5fps.

The more I look at freebasic's asm-output, the more I think this will become a good opportunity for you to learn assembler ;)
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: Hezad on November 14, 2008
well, thanks (again) for those advises :) I'll think about each of them.

Concerning learning Assembler .. I'd like to, but I won't have time lol It will also be more and more difficult for me to upgrade this engine (I found a job lol, and I have lots of projects aside).

But of course, I stay in the course :p I'll continue to work on it but releases will be even more spread in time :S


@hellfire : a question : If I turn on the fpu option when compiling the examples, will they run on most/every machines ?


@shockie : by the way, thanks for the fog :) I red a really useful (and bloody interresting) paper on the subject, you can find it here :
http://www.cescg.org/CESCG-2004/web/Zdrojewska-Dorota/
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: hellfire on November 14, 2008
QuoteIf I turn on the fpu option when compiling the examples, will they run on most/every machines ?
It won't run on anything older than Pentium3 (1999) or AthlonXP (2001).
A benefit of using SSE (http://en.wikipedia.org/wiki/Sse) for floating-point operations is that MMX (http://en.wikipedia.org/wiki/MMX_(instruction_set)) (which shares its registers with the fpu) can be used for pixel-processing.
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: DruggedBunny on November 15, 2008
I don't have anything useful to offer, but this is a fascinating thread, and you're doing a fantastic job with the advice of these experts, Hezad! It's hard to see many practical reasons for software rendering these days, but I still find it fascinating, and I have tons of respect for people who implement it. I also love the visual idiosyncracies that software rendering can offer over standard DirectX/OpenGL rendering. All games should look like Zeewolf! http://amiga.probers.info/images/stories/predseda/zeewolf205.gif (http://amiga.probers.info/images/stories/predseda/zeewolf205.gif)

I do think (well, I hope) that Intel's Larrabee (and the competition's resulting efforts) will offer a genuine avenue for such in-depth software rendering knowledge in the future:

http://arstechnica.com/articles/paedia/gpu-sweeney-interview.ars (http://arstechnica.com/articles/paedia/gpu-sweeney-interview.ars)
http://www.anandtech.com/cpuchipsets/intel/showdoc.aspx?i=3367&p=1 (http://www.anandtech.com/cpuchipsets/intel/showdoc.aspx?i=3367&p=1)

You will be one of the very few people in the world who are able to take advantage of such arcane knowledge!


Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: Hezad on November 15, 2008
@hellfire : oh yeah okay, thanks :) I'll make a fpu and a "non-fpu" compilations of examples for the next release :D


@DruggedBunny : Heya :) thanks a lot for your kind words. These papers are very interesting :D
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: Hezad on November 19, 2008
A little screen from the v0.07 in progress :D The fake phong shader has been improved !
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: Shockwave on November 20, 2008
Very shiny! the shapes look like they are more solid now.
Title: Re: WIP 3D Engine (Update : Fake phong shader, big optimization, ...) [v0.06 !]
Post by: Hezad on November 20, 2008
Quotethe shapes look like they are more solid now.

Yep :D The way I "add" (specular) and (diffuse + ambiant + texture data) is not perfect though, I must work on it, but it's a first step :D
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: hellfire on November 21, 2008
That looks great, Hezad! Now all that's missing are shadows ;D
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Shockwave on November 21, 2008
Quote from: hellfire on November 21, 2008
That looks great, Hezad! Now all that's missing are shadows ;D


I suspect that Hezad will try them soon... He's run out of other things to add to the engine now I think.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Hezad on November 22, 2008
QuoteThat looks great, Hezad! Now all that's missing are shadows Grin

QuoteThat looks great, Hezad! Now all that's missing are shadows Grin


I suspect that Hezad will try them soon... He's run out of other things to add to the engine now I think.


uhuhuh thanks ;D In fact I still have optimizations to code :S but yeah, shadows are already planned ^^ Just don't know when yet.
However, there're still a lot of cool 3D "concepts" I'd like to add :p

(not in any order)

  • Env. Mapping
  • real phong
  • Shadows (of course :p)
  • dynamic (in an utopic world :P static otherwise) ambient occlusion
  • reflexion/refraction (so raytracing, and scene handling)
  • other stuff I'm not thinking about right now ...

I already red several articles/tuts/... about those different steps and I get them roughly but I just don't really know what to code next. Of course some stuff are way more difficult to handle than others ...

I'll start with a question about shadows if you don't mind ^^

Can I really envisage to add dynamic shadows in real-time in a software 3d engine without loosing a huge amount of FPS ? Obviously without using 'cpu-costly' shaders (using Gouraud shader max no phong or fake phong) :)
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Shockwave on November 22, 2008
Do you want objects to be able to cast shadows upon other objects and themselves or are you looking to simply project a shadow onto a plane?

Probably the method you want to impliment is shadow volumes.
For each light source you have, you need to project a ray from the light source, passing through every vertex in the object you are testing to some point outside your scene (infinity). You can then test the vertices in your scene to see if they lie inside the shadow volume, if they do then they are in shadow and you can colour accordingly.

This would work quite nicely for you without too much cost.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Hezad on November 22, 2008
Yeah, I'd like the objects to cast shadows on other objects and themselves :)

I red some stuff about shadow volumes too.

QuoteThis would work quite nicely for you without too much cost.

I'll give it a try then :D
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Stonemonkey on November 24, 2008
Hi hezad, nice work you've done on this. Stencil shadows (volumes) are definitely do-able in software although you'd probably be best to try to keep the number of large shadow casting objects to a minimum and in some cases you can simplify the shadow casting geometry but it can lead to some problems particularly with self shadowing objects.

A couple of things I would recommend looking up are sillouette edges and z-fail, using z-pass doesn't work well if the camera is inside a shadow volume.

Since the technique needs at least a depth pass of the scene initially I did my texturing and shading interpolation in that pass also but instead of doing the shading calcs I stored the colour as 24 bit and the shading as 8 bit (I came up with a bit of a dodgy method of storing the diffuse and specular in 8 bits).
Then filled the shadow volumes, front facing triangles increment the value in the stencil buffer if the z test fails and backfacing triangles decrement the value if z test fails.
Then in a single pass over the screenbuffer did my shading calcs for each pixel using the values in the stencil buffer to determine whether the pixel was in light or shadow.
I could never get this to work with anything transparent.

One way to add simple planar reflections for example to the floor is to make the floor transparent and place an inverted copy of your scene underneath.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: hellfire on November 24, 2008
Shadow-volumes are a serious fillrate-killer and since you need a z-pass anyway, you're sometimes better off with zbuffer-shadows. Both techniques have their pros & cons, though.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Hezad on November 24, 2008
thanks for your advices, I'm still checking a lot of ressources on the net about it to understand the vocabulary and the different algorithms :)
In the same time i'm pretty interested by Occlusion culling :P So i'm thinking about a way to use both shadows and occlusion culling in the same time, that'd be bloody awesome :D
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Stonemonkey on November 25, 2008
Even though shadow volumes suffer from hard edges I still prefer that over the aliasing of other methods and as I said it's possible to do it in a single pass of the scene so the impact on using shadows only depends on how many objects are actually casting shadows and what size they are, too many or too large and yep, fillrate becomes a problem. Still, it's all down to personal preference and some methods might be preferable depending on the application.

Here's a small example done in freebasic I've dug out, sorry I can't find much else atm.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Hezad on November 25, 2008
heya thanks for the exe !! it's pretty attractive ^^

Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Shockwave on November 25, 2008
Oooh, I hadn't seen that example before Stonemonkey.
It runs at a very good framerate.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: hellfire on November 25, 2008
Quoteshadow volumes suffer from hard edges
That's not so much of problem when several light-sources are blending together (have a look here (http://hellfire.untergrund.net/stuff/shader2test.rar), it's accelerated though).
Distance attenuation makes the hard edges less obvious and near to a light-source shadow-edges are pretty hard anyway.
But a proper lighting-rig that doesn't look totally synthetic requires a considerable amount of light-sources; not all lights require shadow-casting though, but one is quickly multiplying fillrate to unreasonable amounts (especially when filling in software), not to mention enabling multisampling to make the hard edges a bit softer.
I haven't yet managed to integrate shadow-volumes with envmap-based lighting authentically.
Shadow-maps on the contrary have one major benefit: You can look up a pixel's surrounding in the map and decide the amount of shadowing without much cost.
Still both methods are useful to have.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Stonemonkey on November 27, 2008
Ah very nice hellfire, I'd done some stuff with multiple lightsources and volume shadows in hardware too but it might be a bit too much work to get out of software rendering.

I'd also looked into shadow maps but never attempted it as it still looked to me like a lot of additional work, particularly for dynamic shadows.

Quote
Shadow-maps on the contrary have one major benefit: You can look up a pixel's surrounding in the map and decide the amount of shadowing without much cost.
even with software? I've been trying bi linear filtering which costs a little over nearest pixel but to add more interpolation as well as multiple lookups from the z buffer and then calcs to work out the shadow value could add a fair bit.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: hellfire on November 27, 2008
Quoteit might be a bit too much work to get out of software rendering
For the beginning you can keep the software-engine as it is and just call "someone else's" polyfiller.
On the long run it's inevitable to rearrange most of the data-structure, though.
It can be quite deflating to see how much faster that damn hardware is in comparism to a seemingly well-optimized software-polyfiller...

Quoteshadow maps [...] looked to me like a lot of additional work
Since you need to draw a z-pass from the light's point of view, it requires some sensible concept to use a framebuffer's zbuffer as a texture without making the renderloop a complete mess.
Unluckily inheritance isn't implemented in FreeBasic yet, so I can't really suggest an aesthetic solution here.

Quote
Quotelook up a pixel's surrounding...
to add more interpolation as well as multiple lookups from the z buffer and then calcs to work out the shadow value could add a fair bit.
For hard-edged shadows you'd test if the current pixel's distance from the light is greater than the distance stored in the shadowmap, so it's a simple compare (to avoid the compare it's even just the sign-bit of the difference). Usually the z-values can be stored with enough precision in 16bits, so a 2x2 area only requires reading of two dwords. There's no interpolation required here, you just count how many samples are lit. Creating a few levels of mipmaps from the shadowmap can increase the sampling area without much cost.
To avoid out-of-range tests when accessing the shadowmap in the innerloop, I clipped all polygons at the borders of the shadowmap.
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: Stonemonkey on November 28, 2008
Quote
There's no interpolation required here

This is maybe where I'm getting confused, how do you know which pixels to test from the shadow map?
Title: Re: WIP 3D Engine (Update : v0.07 screenie preview) [v0.06]
Post by: hellfire on November 28, 2008
Sorry for the confusion; of course you need to interpolate coordinates for the (projective or cubic) shadowmap, you just won't need color-interpolation in the amount bilinear-filtering requires.