Here is an example, in 2d (there are also examples in 3d)
http://www.youtube.com/watch?v=3SLsB32a_FI
To draw metaballs, the trick is: first of all, imagine a "force field" that is inversely proportional to the squared distance to the center of the metaball: when it drop under a threshold, the surface of the metaball is drawn; if two metaballs are too close, between them the sum of their force fields will be higher than the threshold, and so they will merge.
The simplest way to draw metaballs is to check, for every point on the screen, if its force field (the sum of the field of all metaballs) is above the threshold or not:
Code: Select all
Dim metaballX(5) as integer
Dim metaballY(5) as integer
randomize timer
screenres 800,600,32
dim as ubyte ptr target
for i as integer=1 to 5
metaballX(i)=800*rnd(1)
metaballY(i)=600*rnd(1)
next
do
target = screenPtr
for i as integer=1 to 5
metaballX(i)+=10*rnd(1)-5
metaballY(i)+=10*rnd(1)-5
next
screenLock
for y as integer = 1 to 600
for x as integer = 1 to 800
dim d as single
d=0
for i as integer=1 to 5
d+=500.0/((x-metaballX(i))^2+(y-metaballY(i))^2)
next
if d>.5 then
*target = 255 ' Blue level.
*(target+1) = 255 ' Green level.
*(target+2) = 255 ' Red level.
else
*target = 0 ' Blue level.
*(target+1) = 0 ' Green level.
*(target+2) = 0 ' Red level.
end if
target += 4
next
next
screenUnlock
sleep 1
loop until inkey<>""
sleep
Is there a faster solution? Of course: you can evaluate just some points, and interpolate the rest, with an algorithm called "marching squares" (http://en.wikipedia.org/wiki/Marching_squares):
Here is the result:
Code: Select all
Dim shared metaballX(5) as integer
Dim shared metaballY(5) as integer
randomize timer
screenres 800,600,32,2
ScreenSet 1,0
for i as integer=1 to 5
metaballX(i)=800*rnd(1)
metaballY(i)=600*rnd(1)
next
function scalarfield (x as integer, y as integer) as single
dim d as single
for i as integer=1 to 5
d+=500.0/((x-metaballX(i))^2+(y-metaballY(i))^2)
next
return d
end function
do
cls
for i as integer=1 to 5
metaballX(i)+=2*rnd(1)-1
metaballY(i)+=2*rnd(1)-1
next
for y as integer = 1 to 600 step 20
for x as integer = 1 to 800 step 20
dim as single A,B,C,D
A=scalarfield(x,y)
B=scalarfield(x+20,y)
C=scalarfield(x,y+20)
D=scalarfield(x+20,y+20)
dim as integer side
if A<.5 then side or=1
if B<.5 then side or=2
if C<.5 then side or=4
if D<.5 then side or=8
dim as integer x1,y1,x2,y2
select case side
case 1, 9, 14:
x1=x+20*(.5-A)/(B-A)
y1=y+20*(.5-A)/(C-A)
line(x,y1)-(x1,y)
case 2, 6, 13:
x1=x-20*(.5-B)/(A-B)+20
y1=y+20*(.5-B)/(D-B)
line(x1,y)-(x+20,y1)
case 3, 12:
y1=y+20*(.5-A)/(C-A)
y2=y+20*(.5-B)/(D-B)
line(x,y1)-(x+20,y2)
case 4,11:
x1=x+20*(.5-C)/(D-C)
y1=y-20*(.5-C)/(A-C)+20
line(x,y1)-(x1,y+20)
case 5,10:
x1=x+20*(.5-A)/(B-A)
x2=x+20*(.5-C)/(D-C)
line(x1,y)-(x2,y+20)
case 7,8:
x1=x-20*(.5-D)/(C-D)+20
y1=y-20*(.5-D)/(B-D)+20
line(x+20,y1)-(x1,y+20)
end select
next
next
flip 1,0
sleep 1
loop until inkey<>""
sleep