REM Donut
REM Version 1.2 // 16-Mar-2012
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
PROCfixWindowSize
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
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
ballBm% = FNLoadImg( @dir$ + "sphere003_32x32x24.BMP", 0 )
REM
REM Define our donut ring variables
REM
ballsPerRing% = 12
ringRadius% = 50
ringDist% = 180
numRings% = 30
numBalls% = numRings% * ballsPerRing%
REM
REM Arrays to hold the ball's position in 3D space,
REM and declare the rotation matrix arrays
REM
DIM p(2, numBalls%-1), q(2, numBalls%-1)
DIM m1(2,2), m2(2,2), m3(2,2)
REM
REM Define our 3D donut object
REM
N% = 0
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
PROCrotatePoint( x, y, z, 0, T%*(2*PI/numRings%), 0, x`, y`, z` )
p(0, N%) = x`
p(1, N%) = y`
p(2, N%) = z`
N% += 1
NEXT A%
NEXT T%
REM
REM Define the initial rotation angles, and the 'colour-change' variables
REM
R% = RND(-TIME)
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-change vars
f = 2.0 * PI*RND(1) : REM /
REM
REM Load some frequently accessed values into static vars
REM for faster speed of access
REM
B% = ballBm%
D% = dispVars{}
P% = GFXLIB_PlotTint%
REM
REM Disable automatic program window refresh
REM
*REFRESH OFF
REPEAT
REM
REM Clear the viewport (fill it with slowly changing colour)
REM
red = 127 + 128*SINd
green = 127 + 128*COSe
blue = 127 + 128*SINf*COSe
bgCol% = FNrgb( red, green, blue )
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
m1() = 1, 0, 0, 0, cosa, -sina, 0, sina, cosa
m2() = cosb, 0, sinb, 0, 1, 0, -sinb, 0, cosb
m3() = cosc, -sinc, 0, sinc, cosc, 0, 0, 0, 1
q() = m1().p()
q() = m2().q()
q() = m3().q()
REM
REM Sort the rotated ball positions according to their Z-coordinate
REM
C% = DIM(q(),DIM(q()))+1
CALL sort%, q(2,0), q(0,0), q(1,0)
REM ===========================
REM Draw the depth-sorted balls
REM ===========================
REM
REM Reminder: P% = GFXLIB_PlotTint%
REM D% = dispVars{}
REM B% = ballSprAddr%
REM
FOR I%=0 TO numBalls%-1
REM Calc. perspective factor
z = 680 / (600 + q(2,I%))
REM Calc. the depth-dependent colour tinting factor
T% = 255 * (160 + q(2,I%))/320
REM Calc. ball's screen coordinates
X% = 304 + q(0,I%)*z
Y% = 240 + q(1,I%)*z
REM Plot the ball sprite
REM
REM P% = GFXLIB_PlotTint%, D% = dispVars{}, B% = ballBm%
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 PROCrotatePoint( 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
DEF PROCfixWindowSize
LOCAL GWL_STYLE, WS_THICKFRAME, WS_MAXIMIZEBOX, ws%
GWL_STYLE = -16
WS_THICKFRAME = &40000
WS_MAXIMIZEBOX = &10000
SYS "GetWindowLong", @hwnd%, GWL_STYLE TO ws%
SYS "SetWindowLong", @hwnd%, GWL_STYLE, ws% AND NOT (WS_THICKFRAME+WS_MAXIMIZEBOX)
ENDPROC