Code archives/Graphics/3D Engine

This code has been declared by its author to be Public Domain code.

Download source code

3D Engine by jfk EO-111102002
Simple 3D engine with linear texturemapping (for Blitz 2D)
; Demo: Framework for a simplyfied 3D Engine featuring linear Texturemapping.
; Minor bugs. Supports Quads only. Someone should add Perspective Correction.
; Z-order by Quads and not by Pixel. Goodie: Renderer is using only Integers!
; No "hidden Quads removal" Function implemented yet - depends heavily on usage...
; Send obscene amounts of cash to howaboutpeace@utopia.gov j/k. Enjoy!
; Credits go to Toshi - thx for his Qbasic Texture Mapper (I removed all Floats) and
; to ParanoidPete and to some other PPL who are writing 3D-tutorials all the time.

; PS: Funny: I got "fps: INFINITY" in Fullscreen Mode :) ... Move the Mouse to Zoom in.

 
Graphics 320,240,16,2
SetBuffer BackBuffer()

; -- init texturemapper

texturefile$="texture256.jpg"
If FileType(texturefile$)=1 Then ; if it exists then load it...
 txt=LoadImage(texturefile$); use any 256*256 pixel texture
Else
 txt=CreateImage(256,256)   ; else create one on the fly
 SetBuffer ImageBuffer(txt)
 For i=0 To 255
  Color i,255-i,0
  Line 255-i,0,255,i
  Color 255,255-i,255-i
  Line 0,255-i,i,255
 Next
 SetBuffer BackBuffer()
EndIf
Global imgtxt=ImageBuffer(txt)

Dim Lefttable%(480, 2), Righttable%(480, 2)  ;Scan converter tables (make shure to reserve enough)
Dim Polypoints%(3, 1) ; Array for polygon co-ords, 4 pairs(x,y) co -ords
Global Miny%, Maxy%
Global Pwidth%, Pheight%
 
 
Pwidth = 255 Shl 16   ;original picture width in pixels -1 shl 16
Pheight = 255 Shl 16 ;original picture height in pixels -1 shl 16

; eo init texturemapper

Dim zbuffer(10000) ; maximum of 10k Quads (don't worry, would be too slow anyway :) )
 
; Read in a mesh
Restore building
Read anz
Dim xwww(anz),ywww(anz),zwww(anz)
Dim xw(anz),yw(anz),zw(anz)
 
For i=0 To anz
 Read xwww(i)
 Read ywww(i)
 Read zwww(i)
Next

a=0

alpha=1
beta=1
gamma=1

zoom=-500 ; better alter "mausy#" to zoom in (see below)


; MMMMMMMMMMMMMMMMMMMMMMMMMmmmmmain

While KeyDown(1)=0

 Miny% = 32767  ;set initial smallest y co-ord of polygon After rotation
 Maxy% = 0      ;set initial largest y co-ord of polygon After rotation

 ; calc frames per second
 tt=MilliSecs()
 fps#=1000.0/(tt-ttold)
 fpsadd#=fpsadd#+fps#
 fpscount=fpscount+1
 If fpscount>19 Then
  fpsreal#=fpsadd#/20
  fpsadd#=0
  fpscount=0
 EndIf
 ttold=tt
 ; eo calc fps

 ; Cls
 Color 0,0,0
 Rect 0,0,320,240,1

 a=a+1.0 ; automatic rotation...
 If a>359.9 Then a=0
 alpha=a
 beta=a
 gamma=a+a Mod 360

 mausy#=0.1+(MouseY()/50.0)
 ; the renderer is working with rotated Copies of the original Points to prevent degeneration by Floating Inaccuracy
 For i=0 To anz
  ; rotate pitch, roll and yaw
  xl1#=zwww(i)*Sin(gamma)+xwww(i)*Cos(gamma)
  yl1#=ywww(i)
  zl1#=zwww(i)*Cos(gamma)-xwww(i)*Sin(gamma)

  xl2#=xl1
  yl2#=yl1*Cos(beta)-zl1*Sin(beta)
  zl2#=yl1*Sin(beta)+zl1*Cos(beta)
 
  xl3#=(yl2*Sin(alpha)+xl2*Cos(alpha))
  yl3#=(yl2*Cos(alpha)-xl2*Sin(alpha))
  zl3#=(zl2)


  ; Projecting 3D to 2D
  If yloc# - Zoom <> 0 Then yloc = Int(yl3 ) * 200 / (zl3 - Zoom)
  If xloc# - Zoom <> 0 Then xloc = Int(xl3 ) * 200 / (zl3 - Zoom)

  xw(i)=((mausy#)*xloc) +160
  yw(i)=((mausy#)*yloc) +120
  zw(i)=(zl3+256) ; remember this for Z-sorting
 Next

 ; z-sorting...
 For i=0 To 10000
  zbuffer(i)=-1 ; wipe out old zbuffer info (could be optimized)
 Next
 For i=0 To anz-3 Step 4
  If zw(i)>=0 ; clip Quads behind Camera
   zwmax=zw(i)
   If zwmax<zw(i+1) Then zwmax=zw(i+1)
   If zwmax<zw(i+2) Then zwmax=zw(i+2)
   If zwmax<zw(i+3) Then zwmax=zw(i+3)
   While zbuffer(zwmax)<>-1 And zwmax<10000 ; find next free slot
    zwmax=zwmax+1
   Wend
   zbuffer(zwmax)=i
  EndIf
 Next

 LockBuffer ImageBuffer(txt)
 LockBuffer BackBuffer()

 For i2=10000 To 0 Step -1 ; reading quads in z-order from far to near
  i=zbuffer(i2)

  If i>-1 And i< anz-2 ; if it isn't -1 then it's a Quad Point 1 ID
   ; Mapping...
   GetPolygonPoints(i)
   FindSmallLargeY()
   ; Send polygon points To the scan converter
   X1% = Polypoints%(0, 0)
   Y1% = Polypoints%(0, 1)
   X2% = Polypoints%(1, 0)
   Y2% = Polypoints%(1, 1)
   ScanConvert(X1%, Y1%, X2%, Y2%, 1)     ;scan top of picture
   X1% = Polypoints%(1, 0)
   Y1% = Polypoints%(1, 1)
   X2% = Polypoints%(2, 0)
   Y2% = Polypoints%(2, 1)
   ScanConvert(X1%, Y1%, X2%, Y2%, 2)   ;scan Right of picture
   X1% = Polypoints%(2, 0)
   Y1% = Polypoints%(2, 1)
   X2% = Polypoints%(3, 0)
   Y2% = Polypoints%(3, 1)
   ScanConvert(X1%, Y1%, X2%, Y2%, 3)  ;scan bottom of picture
   X1% = Polypoints%(3, 0)
   Y1% = Polypoints%(3, 1)
   X2% = Polypoints%(0, 0)
   Y2% = Polypoints%(0, 1)
   ScanConvert(X1%, Y1%, X2%, Y2%, 4)    ;scan Left of picture

   TextureMap()
   ; eo mapping
  EndIf
 Next

 UnlockBuffer BackBuffer()
 UnlockBuffer ImageBuffer(txt)

 Color 255,255,255
 Text 0,0,"fps "+fpsreal#

 Flip 0
Wend

End





 
; --- texture mapping functions

Function GetPolygonPoints(ilocal%) ; initially read in a rectangle
 For Count% = 0 To 3
  Polypoints%(Count%, 0) = xw(ilocal%+Count%)
  Polypoints%(Count%, 1) = yw(ilocal%+Count%)
 Next
End Function

Function FindSmallLargeY()
 For Count% = 0 To 3
  Ycoord% = Polypoints%(Count%, 1)

  If Ycoord% < Miny% Then       ; is this the New lowest y co-ord?
   Miny% = Ycoord%             ; Yes...
  End If

  If Ycoord% > Maxy% Then       ; is this the New highest y co-ord?
   Maxy% = Ycoord%             ; Yes...
  End If
 Next
End Function

Function ScanConvert (X1%, Y1%, X2%, Y2%, Pside)

 ; This procedure takes as defined by X1%,Y1%,x2,Y2%.
 ; It also takes a var telling it which side of the picture we are
 ; mapping.  The var are 1,2,3,4 for top,Right,bottom,Left. This routine decides
 ; which ;side; of the polygon the Line is on, And Then calls the
 ; appropriate routine.

 If Y2% < Y1% Then
  temp%=X1% : X1%=X2% : X2%=temp%
  temp%=Y1% : Y1%=Y2% : Y2%=temp%
  Lineheight% = (Y2% - Y1%)
  ScanLeftSide(X1%, X2%, Y1%, Lineheight%, Pside)
 Else
  Lineheight% = (Y2% - Y1%)
  ScanRightSide(X1%, X2%, Y1%, Lineheight%, Pside)
 End If
End Function

Function ScanLeftSide (X1%, X2%, Ytop%, Lineheight%, Pside)

 ; This procedure calculates the x points for the Left side of the
 ; polygon. It also calculates the x,y co-ords of the picture For the Left
 ; side of the polygon.

 Lineheight% = Lineheight% + 1       ; prevent divide by zero
 Xadd = (X2% - X1%) Shl 16
 Xadd = Xadd / Lineheight%

 
 If Pside = 1 Then
  Px = Pwidth% - 1
  Py = 0
  Pxadd = -Pwidth%  / Lineheight%
  Pyadd = 0
 End If
 If Pside = 2 Then
  Px = Pwidth%
  Py = Pheight%
  Pxadd = 0
  Pyadd = -Pheight%  / Lineheight%
 End If
 If Pside = 3 Then
  Px = 0
  Py = Pheight%
  Pxadd = Pwidth%  / Lineheight%
  Pyadd = 0
 End If
 If Pside = 4 Then
  Px = 0
  Py = 0
  Pxadd = 0
  Pyadd = Pheight%  / Lineheight%
 End If

 x = X1% Shl 16
 For y% = 0 To Lineheight%
  Ytopy%=Ytop%+y%
  If Ytopy%<0 Then Ytopy%=0
  Lefttable(Ytopy%, 0) = x Sar 16    ;polygon x
  Lefttable(Ytopy%, 1) = Px          ;picture x
  Lefttable(Ytopy%, 2) = Py          ;picture y
  x = x + Xadd                       ;Next polygon x
  Px = Px + Pxadd                    ;Next picture x
  Py = Py + Pyadd                    ;Next picture y
 Next
End Function

Function ScanRightSide (X1%, X2%, Ytop%, Lineheight%, Pside)

 ; This procedure calculates the x points For the Right side of the ;
 ; polygon. It also calculates the x,y co-ords of the picture For the
 ; Right side of the polygon.

 Lineheight% = Lineheight% + 1    ; No divide by zero
 Xadd = (X2% - X1%) Shl 16
 Xadd = Xadd / Lineheight%


 If Pside = 1 Then
  Px = 0
  Py = 0
  Pxadd = Pwidth% / Lineheight%
  Pyadd = 0
 End If
 If Pside = 2 Then
  Px = Pwidth%
  Py = 0
  Pxadd = 0
  Pyadd = Pheight% / Lineheight%
 End If
 If Pside = 3 Then
  Px = Pwidth%
  Py = Pheight%
  Pxadd = -Pwidth% / Lineheight%
  Pyadd = 0
 End If
 If Pside = 4 Then
  Px = 0
  Py = Pheight%
  Pxadd = 0
  Pyadd = -Pheight% / Lineheight%
 End If

 x = X1% Shl 16
 For y% = 0 To Lineheight%
  Ytopy%=Ytop%+y%
  If Ytopy%<0 Then Ytopy%=0
  Righttable(Ytopy%, 0) = x Sar 16   ;polygon x
  Righttable(Ytopy%, 1) = Px         ;picture x
  Righttable(Ytopy%, 2) = Py         ;picture y
  x = x + Xadd                       ;Next polygon x
  Px = Px + Pxadd                    ;Next picture x
  Py = Py + Pyadd                    ;Next picture y
 Next 
End Function





Function TextureMap()
; This is the actual mapping routine. Only linerar mapping, no perspective correction :/
; It takes the co-ords that have been calculated by the scan converter
; And 'traces' across the original picture in between them looking at
; the pixel Color And Then plotting a pixel in that Color in the
; current position within the polygon.

; If you were able to optimze this in speed or accuracy then please let us know.

 For y% = Miny% To Maxy%
  If y>0 And y<=239 ; clipping y
   Polyx1% = Lefttable((y%), 0)    ;get Left polygon x
   Px1 = Lefttable(y%, 1)          ;get Left picture x
   Py1 = Lefttable(y%, 2)          ;get Left picture y  

   Polyx2% = Righttable((y%), 0)   ;get Right polygon x
   Px2 = Righttable(y%, 1)         ;get Right picture x
   Py2 = Righttable(y%, 2)         ;get Right picture y
   Linewidth% = Polyx2% - Polyx1%  ;what is the width of this polygon Line
   Linewidth%=Linewidth% Or 1      ;prevent divide by zero
   Pxadd = ((Px2 - Px1)) / Linewidth% ;squash picture xdist into poly xdist
   Pyadd = ((Py2 - Py1)) / Linewidth% ;squash picture ydist into poly ydist

   For x% = Polyx1% To Polyx2%
     If x>0 And x<=319 ; clipping x
      Col%=ReadPixelFast((Px1 Shr 16),(Py1 Shr 16),imgtxt)
      WritePixelFast x%,y%,Col%
     EndIf
     Px1 = Px1 + Pxadd        ; move x picture co-ord
     Py1 = Py1 + Pyadd        ; move y picture co-ord
   Next
  EndIf
 Next
End Function

; eo  texture mapping functions


.building
; a simple cube mesh: 24 Points (unshared Vertices) and 6 Quads (must be clockwise):
; (Of course you could implement a Loading Function as well)
Data 23  ; number of pts -1
;    x   y   z

Data -100,-100,-100
Data -100,100,-100
Data -100,100,100
Data -100,-100,100

Data -100,-100,-100
Data  -100,-100,100
Data  100,-100, 100
Data 100,-100, -100

Data -100,100,-100
Data  100,100,-100
Data  100,100, 100
Data -100,100, 100

Data 100,-100,-100
Data 100,-100,100
Data 100,100,100
Data 100,100,-100
 
Data -100,100,100
Data 100,100,100
Data 100,-100,100
Data -100,-100,100

Data -100,100,-100
Data -100,-100,-100
Data 100,-100,-100
Data 100,100,-100

Comments

jfk EO-111102016
Additional note: was able to quadruple the framerate by grabing the texture pixels from an 2D array, instead of the imagebuffer (don't know why I didn't do that in the first place).

Also, draw instead z order from near to far and count drawn pixels(and draw+count only untouched ones), then skip rendering if amount reaches n of screen-pixels. Also, render to intermediate array, so no readpixel is required (for checking if pixel untouched).


Flanker2016
Nice work here ! Very interesting.


jfk EO-111102016
Thanks, as you have seen, the one in the blitz3d discussion is the successor. So the incorrect linear texturemapping here is rather of historical interest.


Code Archives Forum