REM  Donut (sic)


     REM
     REM  Disable Escape key
     REM

     
*ESC OFF


     REM
     REM  Set up a simple error handler
     REM

     
ON ERROR OSCLI "REFRESH ON" : MODE 8 : REPORT : PRINT " at line ";ERL : END


     
REM
     REM  Select 64-bit floating point mode (double-precision maths)
     REM

     
*FLOAT 64


     REM
     REM  Prevent the program window from being resized by the user
     REM

     
SYS "GetWindowLong", @hwnd%, -16 TO S%
     SYS "SetWindowLong", @hwnd%, -16, S% AND NOT &50000
     SYS "SetWindowPos", @hwnd%, 0, 0, 0, 0, 0, 32+7


     REM
     REM  Select a 640x512 display mode and switch off the flashing cursor
     REM

     
MODE 8
     OFF


     
REM
     REM  Install and initialise GFXLIB and required external modules
     REM

     
INSTALL @lib$ + "GFXLIB2.BBC"
     PROCInitGFXLIB( dispVars{}, 0 )

     INSTALL @lib$ + "GFXLIB_modules\PlotSwapRGB.BBC" : PROCInitModule
     INSTALL @lib$ + "GFXLIB_modules\PlotTint.BBC"    : PROCInitModule


     REM
     REM  Install and initialise SORTLIB (which will be used to depth-sort the
     REM  'vector balls' according to their Z coordinate)
     REM

     
INSTALL @lib$+"SORTLIB"

     sort% = FN_sortinit(1,0)



     REM
     REM  Load-in the ball sprite (32x32 pixels, 24bpp) and convert to 32bpp
     REM

     
PROCLoadBMP( @dir$ + "sphere003_32x32x24.BMP", ballSprAddr%, FALSE )



     maxNumBalls%  = 1000  : REM  Max. allowed number of balls

     REM  Arrays to hold the ball's position in 3D space
     
DIM x%(maxNumBalls% -1), y%(maxNumBalls% -1), z%(maxNumBalls% -1)


     REM  Arrays to hold the ball's position in 3D space *after* it's been rotated
     
DIM x2%(maxNumBalls% -1), y2%(maxNumBalls% -1), z2%(maxNumBalls% -1)


     REM
     REM  Initialise number of balls to zero
     REM

     
N% = 0


     REM
     REM  Define our 3D donut object
     REM

     
ballsPerRing% = 12
     ringRadius% = 50
     ringDist% = 180
     numRings% = 30

     FOR T% = 0 TO numRings%-1
       FOR A% = 0 TO ballsPerRing%-1

         x# = ringDist% + ringRadius% * SIN( A% * 2*PI/ballsPerRing% )
         y# = ringRadius% * COS( A% * 2*PI/ballsPerRing% )
         z# = 0

         PROCrotate( x#, y#, z#, 0, T%*(2*PI/numRings%), 0, x`#, y`#, z`# )

         x%( N% ) = x`#
         y%( N% ) = y`#
         z%( N% ) = z`#

         N% += 1

       NEXT A%
     NEXT T%


     a# = 2.0 * PI*RND(1) : REM  \
     
b# = 2.0 * PI*RND(1) : REM   >---  rotation angles
     
c# = 2.0 * PI*RND(1) : REM  /


     
d# = 2.0 * PI*RND(1) : REM  \
     
e# = 2.0 * PI*RND(1) : REM   >---  colour-changing angles
     
f# = 2.0 * PI*RND(1) : REM  /


     
B% = ballSprAddr%
     D% = dispVars{}
     P% = GFXLIB_PlotTint%


     *REFRESH OFF


     REPEAT


       
REM
       REM  Clear the viewport (fill it with slowly changing colour)
       REM

       
r& = 127 + 128*SINd#
       g& = 127 + 128*COSe#
       b& = 127 + 128*SINf#*COSe#
       bgCol% = FNrgb( r&, g&, b& )

       SYS GFXLIB_Clr%, dispVars{}, bgCol%


       REM
       REM  Rotate the 3D positions of the balls
       REM

       
cosa# = COSa#
       cosb# = COSb#
       cosc# = COSc#
       sina# = SINa#
       sinb# = SINb#
       sinc# = SINc#

       FOR I% = 0 TO N%-1

         x# = x%(I%)
         y# = y%(I%)
         z# = z%(I%)

         REM  X rotation
         
x1# = x#
         y1# = y#*cosa# - z#*sina#
         z1# = y#*sina# + z#*cosa#

         REM  Y rotation
         
x2# = z1#*sinb# + x1#*cosb#
         y2# = y1#
         z2# = z1#*cosb# - x1#*sinb#

         REM  Z rotation
         
x2%(I%) = x2#*cosc# - y2#*sinc#
         y2%(I%) = x2#*sinc# + y2#*cosc#
         z2%(I%) = z2#

       NEXT


       
REM
       REM  Sort the rotated ball positions according to their Z-coordinate
       REM

       
C% = N%
       CALL sort%, z2%(0), x2%(0), y2%(0)


       REM  ===============================
       REM  Draw the now depth-sorted balls
       REM  ===============================

       REM
       REM  Reminder:  P% = GFXLIB_PlotTint%
       REM             D% = dispVars{}
       REM             B% = ballSprAddr%
       REM

       
FOR I%=0 TO N%-1

         REM  Calc. perspective factor
         REM  Note: this could have been pre-calculated :)
         
p# = 680/(600 + z2%(I%))

         REM  Calc. the depth-dependent colour tinting factor
         REM  Note:  this also could have been pre-calculated :)
         
T% = 255 * ((160 + z2%(I%))/320)

         REM  Calc. final viewport coordinates
         
X% = 304 + x2%(I%)*p#
         Y% = 240 + y2%(I%)*p#

         REM  Plot the ball sprite
         REM  Remember that P% = GFXLIB_PlotTint%,  D% = dispVars{},  B% = ballSprAddr%
         
SYS P%, D%, B%, 32, 32, X%, Y%, bgCol%, T%

       NEXT


       
REM
       REM  Bump and check the rotation angles and the background colour-change vars
       REM

       
a# += 0.0292710182113
       b# += 0.0263168891711
       c# += 0.0221941538383

       d# += 0.0021771091552
       e# += 0.0050891118723
       f# += 0.0039187106721

       IF a# > 2*PI THEN a# -= 2*PI
       IF
b# > 2*PI THEN b# -= 2*PI
       IF
c# > 2*PI THEN c# -= 2*PI

       IF
d# > 2*PI THEN d# -= 2*PI
       IF
e# > 2*PI THEN e# -= 2*PI
       IF
f# > 2*PI THEN f# -= 2*PI


       
REM
       REM  Update the screen (program window)
       REM

       
PROCdisplay

     UNTIL FALSE




     
DEF PROCrotate( x%, y%, z%, a#, b#, c#, RETURN x`#, RETURN y`#, RETURN z`# )
     LOCAL x1#, y1#, z1#, x2#, y2#, z2#, x3#, y3#, z3#
     LOCAL cosa#, cosb#, cosc#, sina#, sinb#, sinc#

     cosa# = COSa#
     cosb# = COSb#
     cosc# = COSc#
     sina# = SINa#
     sinb# = SINb#
     sinc# = SINc#

     REM X rotation
     
y1# = y%*cosa# - z%*sina#
     z1# = y%*sina# + z%*cosa#
     x1# = x%

     REM Y rotation
     
z2# = z1#*cosb# - x1#*sinb#
     x2# = z1#*sinb# + x1#*cosb#
     y2# = y1#

     REM Z rotation
     
x3# = x2#*cosc# - y2#*sinc#
     y3# = x2#*sinc# + y2#*cosc#
     z3# = z2#

     x`# = x3#
     y`# = y3#
     z`# = z3#
     ENDPROC