ON ERROR OSCLI "refresh on" : IF ERR=17 CHAIN @lib$+"../examples/tools/touchide" ELSE MODE 3 : PRINT REPORT$ : END REM Torus 2 REM Version 1.2 // 16-Mar-2012 REM Original BB4W/GFXLIB program by David Williams REM BBCSDL/GFX2DLIB adaptation by Richard Russell. REM Prevent the program window from being resized by the user IF @platform% >= &2050000 SYS "SDL_SetWindowResizable", @hwnd%, FALSE REM Select a 640x512 display mode and switch off the flashing cursor MODE 8 OFF REM Initialise 2D graphics PROC_init2D REM Install and initialise SORTLIB (which will be used to depth-sort the REM 'vector balls' according to their Z coordinate) INSTALL @lib$ + "sortlib" sort%% = FN_sortinit(1, 0) REM Load-in the ball sprite (20x20 pixels) ballSprite = FN_loadBMP( @dir$ + "ball3_20x20.bmp", 0 ) REM Define torus vars ballsPerRing% = 12 ringRadius% = 20 ringDist% = 56 numRings% = 32 numBalls% = numRings% * ballsPerRing% REM Array to hold the balls' positions in '3D space' DIM p(2, numBalls% - 1) REM Array to hold the balls' positions *after* they've been rotated DIM q(2, numBalls% - 1) REM Arrays to hold each ball's ``normal vector`` DIM n(2, numBalls% - 1) REM Arrays to hold each ball's rotated normal vector DIM o(2, numBalls% - 1) REM Rotation matrices DIM a(2,2), b(2,2), c(2,2), r(2,2) REM Define our 'light source' direction vector DIM light(2) light() = 20, 5, -10 light() /= MOD(light()) REM Set up a horizontally-scrolling starfield (four pixels per star) numStars% = 100 DIM sx( numStars%-1 ), sy( numStars%-1 ), dx( numStars%-1 ) DIM pt{( numStars%*4-1 ) x%, y%} FOR I% = 0 TO numStars%-1 sx( I% ) = 640.0 * RND(1) sy( I% ) = 48 + (512.0 - 2*48) * RND(1) dx( I% ) = 0.5 + 3.5*I%/numStars% - 0.5*RND(1) NEXT I% REM Define our 3D torus object 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.0 nx = 1.0 * SIN( A% * 2*PI/ballsPerRing% ) ny = 1.0 * COS( A% * 2*PI/ballsPerRing% ) nz = 0.0 PROCrotate( x, y, z, 0, T%*(2*PI/numRings%), 0, x`, y`, z` ) PROCrotate( nx, ny, nz, 0, T%*(2*PI/numRings%), 0, nx`, ny`, nz` ) p( 0, N% ) = x` p( 1, N% ) = y` p( 2, N% ) = z` n( 0, N% ) = nx` n( 1, N% ) = ny` n( 2, N% ) = nz` 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 / REM Disable automatic program window refresh *REFRESH OFF REPEAT REM Clear the viewport PROC_clr2D(0, 0, 0) REM Update star positions FOR I% = 0 TO numStars%-1 IF sx(I%) > 640 sx(I%) -= 640 pt{( I%*4 )}.x% = sx(I%) + 0.5 pt{( I%*4 )}.y% = sy(I%) + 0.5 pt{(I%*4+1)}.x% = sx(I%) + 1.5 pt{(I%*4+1)}.y% = sy(I%) + 0.5 pt{(I%*4+2)}.x% = sx(I%) + 0.5 pt{(I%*4+2)}.y% = sy(I%) + 1.5 pt{(I%*4+3)}.x% = sx(I%) + 1.5 pt{(I%*4+3)}.y% = sy(I%) + 1.5 NEXT sx() += dx() REM Draw stars (four pixels per star) PROC_pixels2D(pt{(numStars%*0)}, numStars%, &40, &40, &40, &FF) PROC_pixels2D(pt{(numStars%*1)}, numStars%, &80, &80, &80, &FF) PROC_pixels2D(pt{(numStars%*2)}, numStars%, &C0, &C0, &C0, &FF) PROC_pixels2D(pt{(numStars%*3)}, numStars%, &FF, &FF, &FF, &FF) REM Draw upper and lower blue borders FOR Y% = 0 TO 47 C% = 255*(1 - Y%/47) PROC_rect2D(0, Y%, 640, 1, 0, 0, C%, &FF) PROC_rect2D(0, 511-Y%, 640, 1, 0, 0, C%, &FF) NEXT REM Create the rotation matrix a() = 1, 0, 0, 0, COS(a), -SIN(a), 0, SIN(a), COS(a) b() = COS(b), 0, SIN(b), 0, 1, 0, -SIN(b), 0, COS(b) c() = COS(c), -SIN(c), 0, SIN(c), COS(c), 0, 0, 0, 1 r() = b() . a() r() = c() . r() REM Rotate the 3D positions of the balls REM (and also rotate the normal vectors) q() = r() . p() o() = r() . n() REM Sort the rotated ball positions according to their Z-coordinate C% = numBalls% CALL sort%%, q(2,0), q(1,0), q(0,0), o(2,0), o(1,0), o(0,0) REM =========================== REM Draw the depth-sorted balls REM =========================== FOR I%=0 TO numBalls%-1 REM Calc. perspective factor z = 280 / (200 + q(2,I%)) REM Calc. 2D viewport coordinates X% = 304 + q(0,I%)*z Y% = 240 + q(1,I%)*z REM Calc. angle between the ball's normal vector, REM and light source vector l_dot_n = light(0)*o(0,I%) + light(1)*o(1,I%) + light(2)*o(2,I%) IF l_dot_n < 0 l_dot_n = 0 l_dot_n = 0.5 + l_dot_n / 2 REM Plot the ball sprite ('tinting' it white in real-time!) PROC_plot2D(ballSprite, 20, 20, X%, Y%, &FF*l_dot_n, &FF*l_dot_n, &FF*l_dot_n, &FF, FALSE, FALSE, FALSE) NEXT REM Increment and check the rotation angles a += 0.0292710182113 b += 0.0263168891711 c += 0.0221941538383 IF a > 2*PI THEN a -= 2*PI IF b > 2*PI THEN b -= 2*PI IF c > 2*PI THEN c -= 2*PI REM Update the screen (program window) *REFRESH UNTIL FALSE DEF PROCrotate( x, y, z, a, b, c, RETURN x3, RETURN y3, RETURN z3 ) LOCAL x1, y1, z1, x2, y2, z2 LOCAL ca, cb, cc, sa, sb, sc ca = COSa cb = COSb cc = COSc sa = SINa sb = SINb sc = SINc REM X rotation y1 = y*ca - z*sa z1 = y*sa + z*ca x1 = x REM Y rotation z2 = z1*cb - x1*sb x2 = z1*sb + x1*cb y2 = y1 REM Z rotation x3 = x2*cc - y2*sc y3 = x2*sc + y2*cc z3 = z2 ENDPROC REM High(ish)-performance 2D graphics library DEF PROC_init2D PIXELFORMAT = &16362004 `SDL_SetRenderDrawColor` = FN_gpa("SDL_SetRenderDrawColor") `SDL_SetTextureAlphaMod` = FN_gpa("SDL_SetTextureAlphaMod") `SDL_SetTextureColorMod` = FN_gpa("SDL_SetTextureColorMod") `SDL_SetTextureBlendMode` = FN_gpa("SDL_SetTextureBlendMode") `SDL_RenderDrawPoints` = FN_gpa("SDL_RenderDrawPoints") `SDL_RenderFillRect` = FN_gpa("SDL_RenderFillRect") `SDL_RenderClear` = FN_gpa("SDL_RenderClear") `SDL_RenderCopy` = FN_gpa("SDL_RenderCopy") `SDL_RenderCopyEx` = FN_gpa("SDL_RenderCopyEx") ENDPROC DEF FN_loadBMP(path$, K%) LOCAL R%, s%%, t%% SYS "SDL_RWFromFile", path$, "rb" TO R% IF R%=0 ERROR 103, "Unable to load " + path$ SYS "SDL_LoadBMP_RW", R%, 1 TO s%% IF s%%=0 ERROR 104, "Unable to create surface from " + path$ IF K%<>TRUE SYS "SDL_SetColorKey", s%%, 1, K% SYS "SDL_ConvertSurfaceFormat", s%%, PIXELFORMAT, 0 TO t%% SYS "SDL_FreeSurface", s%% : s%% = t%% SYS "SDL_CreateTextureFromSurface", @memhdc%, s%% TO t%% IF t%%=0 ERROR 105, "Unable to create texture from " + path$ SYS "SDL_FreeSurface", s%% = t%% DEF PROC_clr2D(R%,G%,B%) SYS `SDL_SetRenderDrawColor`,@memhdc%,R%,G%,B%,&FF SYS `SDL_RenderClear`,@memhdc% ENDPROC DEF PROC_plot2D(t%%,W%,H%,X%,Y%,R%,G%,B%,A%,M%,F%,a) LOCAL rc{} : DIM rc{x%,y%,w%,h%} rc.x% = X% - W%/2 rc.y% = Y% - H%/2 rc.w% = W% rc.h% = H% IF A%<>&FF SYS `SDL_SetTextureColorMod`,t%%,A% IF R%<>&FF OR G%<>&FF OR B%<>&FF SYS `SDL_SetTextureColorMod`,t%%,R%,G%,B% IF M% SYS `SDL_SetTextureBlendMode`,t%%,M% IF a<>0 OR F% THEN IF @platform% AND &40 THEN SYS `SDL_RenderCopyEx`,@memhdc%,t%%,FALSE,rc{},FN_nz(a),FALSE,F% ELSE SYS `SDL_RenderCopyEx`,@memhdc%,t%%,FALSE,rc{},FN_dl(a),FN_dh(a),FALSE,F% ENDIF ELSE SYS `SDL_RenderCopy`,@memhdc%,t%%,FALSE,rc{} ENDIF IF M% SYS `SDL_SetTextureBlendMode`,t%%,FALSE IF R%<>&FF OR G%<>&FF OR B%<>&FF SYS `SDL_SetTextureColorMod`,t%%,&FF,&FF,&FF IF A%<>&FF SYS `SDL_SetTextureColorMod`,t%%,&FF ENDPROC DEF PROC_rect2D(X%,Y%,W%,H%,R%,G%,B%,A%) LOCAL rc{} : DIM rc{x%,y%,w%,h%} rc.x% = X% rc.y% = Y% rc.w% = W% rc.h% = H% SYS `SDL_SetRenderDrawColor`,@memhdc%,R%,G%,B%,A% SYS `SDL_RenderFillRect`,@memhdc%,rc{} ENDPROC DEF PROC_pixels2D(p%%,N%,R%,G%,B%,A%) SYS `SDL_SetRenderDrawColor`,@memhdc%,R%,G%,B%,A% SYS `SDL_RenderDrawPoints`,@memhdc%,p%%,N% ENDPROC DEF FN_gpa(p$) IF @platform% AND &40 THEN LOCAL P%, p%% DIM p%% LOCAL 8 P% = p%% + !340 - PAGE [OPT 0:equq p$:] = ]p%% ENDIF LOCAL P% DIM P% LOCAL 8 [OPT 0:nop:] CASE P%?-1 OF WHEN &90: [OPT 0:call p$:] = P% + P%!-4 WHEN &E1: [OPT 0:equd p$:] = P%!-4 ENDCASE = FALSE DEF FN_dl(a#)=!^a# DEF FN_dh(a#)=!(^a#+4) DEF FN_nz(a#) a#*=1.0:IFa#=0 ?(^a#+7)=&80 = a#