Code: Select all
Type Something
Buffer() As Something
End Type
Code: Select all
'' Metaeffect by Vesa Piittinen aka Merri <http://merri.net/>
''
'' ORIGINAL HEADER FOLLOWS...
'' ddrawtest -- shows how to use DirectDraw directly from FB
''
'' code looks hard to read because all the COM interface "hacking" needed, as FB
'' has no OO-support (yet :P)
''
'' based on C++ DX tutorials found on the Net
''
defint a-z
option explicit
option private
#include once "win\kernel32.bi"
#include once "win\user32.bi"
#include once "win\gdi32.bi"
#include once "win\ddraw.bi"
const SCR_WIDTH = 320
const SCR_HEIGHT = 240
const SCR_SIZE = (SCR_WIDTH * SCR_HEIGHT)
const SCR_BPP = 32 '' changing BPP and doRendering has to be updated too
'' default TRESHOLD is 0.00397 (effect is then less than one pixel)
const TRESHOLD As Double = 0.05 ''00397
TYPE metaball
X as double
Y as double
Rad as ushort
Size as ushort
Red as double
Green as double
Blue as double
END TYPE
declare function WinMain( byval hInstance as integer, byval hPrevInst as integer, lpszCmdLine AS string, byval lCmdShow as integer ) as integer
'' globals
dim shared hInst as integer
dim shared pDD as IDirectDraw ptr
dim shared pDDSFront as IDirectDrawSurface ptr
dim shared pDDSBack as IDirectDrawSurface ptr
dim shared ddsd AS DDSURFACEDESC
dim shared Balls(9) as metaball
dim shared BallBuffer() as double
dim shared BFR(SCR_SIZE - 1) as double
dim shared BFG(SCR_SIZE - 1) as double
dim shared BFB(SCR_SIZE - 1) as double
end WinMain( GetModuleHandle( null ), null, Command$, SW_NORMAL )
'':::::
function InitDirectDraw( byval hWnd as integer ) as integer
dim ddscaps as DDSCAPS
InitDirectDraw = FALSE
' create an interface to DDraw
if( DirectDrawCreate( NULL, @pDD, NULL ) <> DD_OK ) then
exit function
end if
' set the access mode (full screen)
if( IDirectDraw_SetCooperativeLevel( pDD, hWnd, DDSCL_EXCLUSIVE or DDSCL_FULLSCREEN ) <> DD_OK ) then
exit function
end if
' set the display mode
if( IDirectDraw_SetDisplayMode( pDD, SCR_WIDTH, SCR_HEIGHT, SCR_BPP ) <> DD_OK ) then
exit function
end if
' create the primary surface with 1 back-buffer
clear ddsd, 0, len( ddsd )
ddsd.dwSize = len( ddsd )
ddsd.dwFlags = DDSD_CAPS or DDSD_BACKBUFFERCOUNT
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE or DDSCAPS_FLIP or DDSCAPS_COMPLEX
ddsd.dwBackBufferCount = 1
if( IDirectDraw_CreateSurface( pDD, @ddsd, @pDDSFront, NULL ) <> DD_OK ) then
exit function
end if
'' get a pointer to the back buffer
clear ddscaps, 0, len( ddscaps )
ddscaps.dwCaps = DDSCAPS_BACKBUFFER
if( IDirectDrawSurface_GetAttachedSurface( pDDSFront, @ddscaps, @pDDSBack ) <> DD_OK ) then
exit function
end if
InitDirectDraw = TRUE
end function
sub Init_Ball(byval Index as byte, byval Radius as ushort, byval Red as double, byval Green as double, byval Blue as double)
Dim tmpRad as double, tmpDouble as double, tmpSize as long, tmpY As uinteger, X as uinteger, Y as uinteger, A as uinteger
'' initialize some settings
Balls(Index).Rad = Radius
Balls(Index).Red = Red
Balls(Index).Green = Green
Balls(Index).Blue = Blue
Balls(Index).X = CDbl(SCR_WIDTH \ 2)
Balls(Index).Y = CDbl(SCR_HEIGHT \ 2)
'' reserve initial memory
tmpSize = Radius * Radius
If UBound(BallBuffer, 2) < tmpSize Then ReDim Preserve BallBuffer(9, tmpSize)
'' set some temporary variables
tmpRad = CDbl(Radius)
tmpDouble = CDbl(Radius)
'' count the first line (and thus the size of the metaball)
Do Until tmpDouble < TRESHOLD
BallBuffer(Index, A) = tmpDouble - TRESHOLD
A += 1
tmpDouble = tmpRad / CDbl(A * A)
'' reserve more memory as required
If A > tmpSize Then
tmpSize += tmpSize
If UBound(BallBuffer, 2) < tmpSize Then ReDim Preserve BallBuffer(9, tmpSize)
End If
Loop
'' set size
tmpSize = A
Balls(Index).Size = A
'' the final buffer size is now known
If UBound(BallBuffer, 2) < tmpSize Then ReDim Preserve BallBuffer(9, tmpSize * tmpSize - 1)
'' draw the rest of the ball
For Y = 1 To tmpSize - 1
tmpY = Y * Y
'' set the first item
BallBuffer(Index, A) = tmpRad / CDbl(tmpY) - TRESHOLD
'' loop through whole line
For X = 1 To tmpSize - 1
'' calculate the strength
tmpDouble = tmpRad / CDbl(X * X + tmpY)
'' check if it is a good idea to quit the current row
If tmpDouble < TRESHOLD Then Exit For
'' set the strength to item
BallBuffer(Index, A + X) = tmpDouble - TRESHOLD
Next X
'' jump to the next row
A += tmpSize
Next Y
end sub
'':::::
sub doRendering
dim dst as uinteger ptr
dim X as integer, Y as integer, iX as integer, iY as integer, aX as integer, aY as integer
dim tmpX as integer, tmpY as integer, tmpYX as integer, XX as integer, YX as integer
dim A as byte, Red as uinteger, Green as uinteger, Blue as uinteger
'' lock the back buffer before start drawing on it
if( IDirectDrawSurface_Lock( pDDSBack, NULL, @ddsd, DDLOCK_WAIT, NULL ) <> DD_OK ) then
exit function
end if
'' yes, we really go the easiest way here moving the balls...
Balls(0).X += Rnd * 150 - 75
Balls(0).Y += Rnd * 150 - 75
'' check we're not out of boundaries
If Balls(0).X < 0 Then Balls(0).X = 0
If Balls(0).Y < 0 Then Balls(0).Y = 0
If Balls(0).X > SCR_WIDTH Then Balls(0).X = SCR_WIDTH
If Balls(0).Y > SCR_HEIGHT Then Balls(0).Y = SCR_HEIGHT
'' move balls
For A = 1 To 9
Balls(A).X += (Balls(A - 1).X - Balls(A).X) / 5 - (Balls(0).X - Balls(A - 1).X) * 0.02
Balls(A).Y += (Balls(A - 1).Y - Balls(A).Y) / 5 - (Balls(0).Y - Balls(A - 1).Y) * 0.02
Next A
' draw the balls
For A = 0 To 9
'' calculate the area to draw into
aX = SCR_WIDTH - 1
If aX > CInt(Balls(A).X) + Balls(A).Size - 1 Then aX = CInt(Balls(A).X) + Balls(A).Size - 1
aY = SCR_HEIGHT - 1
If aY > CInt(Balls(A).Y) + Balls(A).Size - 1 Then aY = CInt(Balls(A).Y) + Balls(A).Size - 1
iX = 0
If iX < CInt(Balls(A).X) - Balls(A).Size + 1 Then iX = CInt(Balls(A).X) - Balls(A).Size + 1
iY = 0
If iY < CInt(Balls(A).Y) - Balls(A).Size + 1 Then iY = CInt(Balls(A).Y) - Balls(A).Size + 1
'' now just draw the ball
YX = iY * SCR_WIDTH
tmpY = iY - CInt(Balls(A).Y)
tmpYX = CUInt(Abs(tmpY)) * Balls(A).Size
For Y = iY To aY
tmpX = iX - CInt(Balls(A).X)
For X = iX To aX
XX = YX + X
BFR(XX) += BallBuffer(A, CUInt(Abs(tmpX)) + tmpYX) * Balls(A).Red
BFG(XX) += BallBuffer(A, CUInt(Abs(tmpX)) + tmpYX) * Balls(A).Green
BFB(XX) += BallBuffer(A, CUInt(Abs(tmpX)) + tmpYX) * Balls(A).Blue
tmpX += 1
Next X
YX += SCR_WIDTH
tmpY += 1
If tmpY < 0 Then
tmpYX -= Balls(A).Size
Else
tmpYX += Balls(A).Size
End If
Next Y
Next A
'' get pointer to back-buffer
dst = ddsd.lpSurface
YX = 0
'' draw some static (code from ptc_test)
for Y = 0 to SCR_HEIGHT - 1
for X = 0 to SCR_WIDTH - 1
XX = YX + X
Select Case BFR(XX)
Case Is >= 1
Red = 255
Case Is <= 0
Red = 0
Case Else
Red = CUInt(BFR(XX) * 255)
End Select
BFR(XX) = BFR(XX) * 0.95
Select Case BFG(XX)
Case Is >= 1
Green = 255
Case Is <= 0
Green = 0
Case Else
Green = CUInt(BFG(XX) * 255)
End Select
BFG(XX) = BFG(XX) * 0.95
Select Case BFB(XX)
Case Is >= 1
Blue = 255
Case Is <= 0
Blue = 0
Case Else
Blue = CUInt(BFB(XX) * 255)
End Select
BFB(XX) = BFB(XX) * 0.95
*dst = (Red shl 16) or (Green shl 8) or Blue
dst = dst + len( uinteger )
next X
YX += SCR_WIDTH
dst += ddsd.lPitch - (SCR_WIDTH * len( uinteger ))
next Y
'' unlock it, no more needed
IDirectDrawSurface_Unlock( pDDSBack, NULL )
end sub
'':::::
sub CleanUp
'' free the back-buffer
if( pDDSBack <> NULL ) then
IDirectDrawSurface_Release( pDDSBack )
pDDSBack = NULL
end if
'' and the primary one
if( pDDSFront <> NULL ) then
IDirectDrawSurface_Release( pDDSFront )
pDDSFront = NULL
end if
'' and for last the ddraw interface
if( pDD <> NULL ) then
IDirectDraw_Release( pDD )
pDD = NULL
end if
end sub
'':::::
function ProcessIdle
dim hRet as integer
ProcessIdle = FALSE
'' buffers were not allocated? exit
if( (pDDSBack = NULL) or (pDDSFront = NULL) ) then
exit function
end if
'' draw onto back-buffer
doRendering
'' turn it visible (flip)
do
hRet = IDirectDrawSurface_Flip( pDDSFront, NULL, 0 )
'' flip done? exit
if( hRet = DD_OK ) then
exit do
'' surface lost? (user switched to desktop??)
elseif( hRet = DDERR_SURFACELOST ) then
IDirectDrawSurface_Restore( pDDSFront )
'' wait until all current drawing is being done
elseif( hRet <> DDERR_WASSTILLDRAWING ) then
exit do
end if
loop
ProcessIdle = TRUE
end function
'':::::
function WndProc(byval hWnd as integer, byval uMsg as integer, byval wParam as integer, byval lParam as integer) as integer
WndProc = 0
'' process messages
select case uMsg
case WM_CREATE
if( InitDirectDraw( hWnd ) = FALSE ) then
CleanUp
PostMessage hWnd, WM_CLOSE, 0, 0
end if
case WM_DESTROY
PostQuitMessage 0
case WM_KEYDOWN
if( (wParam and &hff) = 27 ) then
CleanUp
PostMessage hWnd, WM_CLOSE, 0, 0
end if
case else
WndProc = DefWindowProc( hWnd, uMsg, wParam, lParam )
end select
end function
'':::::
function WinMain( byval hInstance as integer, byval hPrevInst as integer, lpszCmdLine AS string, byval lCmdShow as integer ) as integer
dim szAppName as string
dim wc as WNDCLASS
dim hWnd as integer
dim msg as MSG
'' metaballs first!
dim ball as byte
redim BallBuffer(9, 0)
'' initialize 10 random balls
randomize
For ball = 0 To 9
'' init by index, radius, red, green, blue
Init_Ball ball, 65, CDbl(Rnd), CDbl(Rnd), CDbl(Rnd)
Next ball
hInst = hInstance
'' create an window
szAppName = "DD test"
with wc
.style = CS_HREDRAW or CS_VREDRAW
.lpfnWndProc = @WndProc
.cbClsExtra = 0
.cbWndExtra = 0
.hInstance = hInst
.hIcon = LoadIcon( hInst, IDI_APPLICATION )
.hCursor = LoadCursor( NULL, IDC_ARROW )
.hbrBackground = GetStockObject( BLACK_BRUSH )
.lpszMenuName = NULL
.lpszClassName = strptr( szAppName )
end with
if( RegisterClass( wc ) = 0 ) then
exit function
end if
hWnd = CreateWindowEx( WS_EX_TOPMOST, szAppName, szAppName, WS_POPUP, NULL, NULL, SCR_WIDTH, SCR_HEIGHT, _
NULL, NULL, hInst, NULL )
if( hWnd = null ) then
exit function
end if
'' show it
ShowWindow hWnd, lCmdShow
UpdateWindow hWnd
SetFocus hWnd
'' check for messages and do the rendering if idle
do while( hWnd )
if( PeekMessage( msg, hWnd, 0, 0, PM_REMOVE ) ) then
if( msg.message = WM_QUIT ) then
exit do
end if
TranslateMessage msg
DispatchMessage msg
else
if( ProcessIdle = FALSE ) then
exit do
end if
end if
loop
''
WinMain = msg.wParam
end function
Thanks to whomever has made the DirectX test code :)