We closed this forum 18 June 2010. It has served us well since 2005 as the ALPHA forum did before it from 2002 to 2005. New discussions are ongoing at the new URL http://forum.processing.org. You'll need to sign up and get a new user account. We're sorry about that inconvenience, but we think it's better in the long run. The content on this forum will remain online.
IndexSuggestions & BugsWebsite,  Documentation,  Book Bugs › Optimization metaball effect
Page Index Toggle Pages: 1
Optimization metaball effect (Read 2779 times)
Optimization metaball effect
Apr 3rd, 2010, 5:09am
 
Hi,

I saw the metaball effect demonstrated by luis2048 in the Topics section in Tutorials and I have a minor optimization improvement, so that the whole square-root calculation is only done once. Luis does the sqrt calculation for each metaball every loop, but that's not needed. You only need to calculate the distance for each pixel relative to the center of the screen and then when you need to know the distance (in calculating the attraction sum) you can simply use an offset in the sqrt buffer (you still need one buffer for the X values and one for the Y of course).

Code:
int[] dx,dy;
int mx=160;
int my=120;
int radius1=100;
int radius2=50;

void setup()
{
 size(640,480,P2D);
 dx=new int[width];
 dy=new int[height];
 
 for(int y=0;y<height;y++)
 {
   dy[y]=int(sq(y)*256);
 }
 for(int x=0;x<width;x++)
 {
   dx[x]=int(sq(x)*256);
 }
 
}

void draw()
{
 background(0);
 for(int y=0;y<height;y++)
 {
   for(int x=0;x<width;x++)
   {
     int sum=0;
     sum+=(radius1<<23)/(dx[abs(width/2-x)]+dy[abs(height/2-y)]+1);
     sum+=(radius2<<23)/(dx[abs(width/2-x+mx)%width]+dy[abs(height/2-y+my)%height]+1);
     set(x,y,color(sum,0,0));
   }
 }
}


Of course an offscreen buffer, which luis uses is better, but that's not the optimization I am talking about, so for clarity reasons I just plot pixels directly to the screen.
The optimization part can be found in the 'sum+=' lines. You can clearly see that the sqrt calculation is only done once, in the setup and that's that  Smiley

Another optimization is to divide the screen up into square boxes (I read that in a Gamedev article) and only calculate the sum for blocks where the sum is rather high (so there must be a blob nearby, as the energy field is high and you can take the center point of the box for determining this, or for instance an average of the four corner points, but you should just fiddle around with this a bit, but of course you can also make it slower again Cool). You can then just skip a block if the energy is below a certain threshold. All of this of course should be done by means of trial and error, but bear in mind that you won't get a smooth blob fading out over the whole screen by doing this. It is only useful if you want to draw the metaball outlines (so only plot pixels if the sum is above a certain threshold). Here you have an example for clarity sake (the above routine rewritten with the box checking):

Code:
int[] dx,dy;
int mx=160;
int my=120;
int radius1=100;
int radius2=50;
int boxsize=10;

void setup()
{
 size(640,480,P2D);
 dx=new int[width];
 dy=new int[height];
 
 for(int y=0;y<height;y++)
 {
   dy[y]=int(sq(y)*256);
 }
 for(int x=0;x<width;x++)
 {
   dx[x]=int(sq(x)*256);
 }
 
}

void draw()
{
 background(0);
 println(frameRate);
 mx=mouseX-width/2;
 my=mouseY-height/2;
 for(int y=0;y<height-boxsize+1;y+=boxsize)
 {
   for(int x=0;x<width-boxsize+1;x+=boxsize)
   {
     int sum=0;
     sum+=(radius1<<23)/(dx[abs(width/2-x+(boxsize/2))]+dy[abs(height/2-(y+boxsize/2))]+1);
     sum+=(radius2<<23)/(dx[abs(width/2-x+(boxsize/2)+mx)%width]+dy[abs(height/2-y+(boxsize/2)+my)%height]+1);
     if(sum>150)
     {
       for(int yy=y;yy<(y+boxsize);yy++)
       {        
         for(int xx=x;xx<(x+boxsize);xx++)
         {
           sum=0;
           sum+=(radius1<<23)/(dx[abs(width/2-xx)]+dy[abs(height/2-yy)]+1);
           sum+=(radius2<<23)/(dx[abs(width/2-xx+mx)%width]+dy[abs(height/2-yy+my)%height]+1);      
           if(sum>(150+boxsize<<2))
           set(xx,yy,color(255,0,0));
         }
       }        
     }            
   }
 }
}


Hopefully someone can benefit from this.

Regards,
Dafith.
Re: Optimization metaball effect
Reply #1 - Apr 11th, 2010, 9:08am
 
This one is really cool! Runs really fast and is exactly what I read some minutes before on GameDev Smiley
Re: Optimization metaball effect
Reply #2 - Apr 14th, 2010, 12:43pm
 
What about this one (blackhole metaballs surrounded by metastars):
Code:
/*
Metaball effect created by D.Juurlink
Date: 14-04-2010
*/
interface Metamorph
{
 public int getSum(int x,int y);
 public void move(int angle);
}

class Metaball implements Metamorph
{
 private int _x,_y,_radius;
 private int _sangle;
 private int _fx,_fy;
 private int _domx,_domy;
 
 public Metaball(int x,int y,int radius)
 {
   _x=x;    
   _y=y;
   _radius=radius;
   _sangle=int(random(0,359));
   _fx=int(random(1,4));
   _fy=int(random(1,4));    
   _domx=w/2-_radius*2;
   _domx-=int(random(0,w/2-_radius*2));
   _domy=h/2-_radius*2;
   _domy-=int(random(0,h/2-_radius*2));
 }
 
 public int getSum(int x,int y)
 {
   return int((_radius<<25)/(sqx[abs(x-_x)%w]+sqy[abs(y-_y)%h]+1));      
 }
 
 public int getX(){return _x;}
 public int getY(){return _y;}
 
 public void move(int angle)
 {
   _x=(_domx*sintbl[((angle+_sangle)*_fx)%360])>>11;
   _x+=w/2;
   _y=(_domy*costbl[((angle+_sangle)*_fy)%360])>>11;
   _y+=h/2;        
 }
}

class Metastar implements Metamorph
{
 private int _x,_y,_radius;
 private int _sangle;
 private int _fx,_fy;
 private int _domx,_domy;
 
 public Metastar(int x,int y,int radius)
 {
   _x=x;    
   _y=y;
   _radius=radius;
   _sangle=int(random(0,359));
   _fx=int(random(1,4));
   _fy=int(random(1,4));    
   _domx=w/2-_radius*2;
   _domy=h/2-_radius*2;
 }
 
 public int getSum(int x,int y)
 {
   return (_radius*100)/(abs(x-_x)+abs(y-_y)+1);
 }
 
 public int getX(){return _x;}
 public int getY(){return _y;}
 
 public void move(int angle)
 {
   _x=(_domx*sintbl[((angle+_sangle)*_fx)%360])>>11;
   _x+=w/2;
   _y=(_domy*costbl[((angle+_sangle)*_fy)%360])>>11;
   _y+=h/2;        
 }
}

PImage buffer;
long[] sqx,sqy;
color[] palette;
int angle=0;
int w,h;
int[] sintbl,costbl;

Metamorph[] balls;

void setup()
{
 w=640/4;
 h=480/4;
 size(w*4,h*4,P2D);    
 buffer=createImage(w,h,RGB);
 sqx=new long[w];
 sqy=new long[h];
 for(int x=0;x<w;x++)
 {
   sqx[x]=int(sq(x)*4096);
 }
 for(int y=0;y<h;y++)
 {
   sqy[y]=int(sq(y)*4096);
 }
 palette=new color[256*3];
 for(int i=0;i<256;i++)
 {
   palette[i]=color(0,0,i);
   palette[i+256]=color(i,0,255);
   palette[i+512]=color(255,i,255);
 }
 
 sintbl=new int[360];
 costbl=new int[360];
 
 for(int i=0;i<360;i++)
 {
   sintbl[i]=int(sin(radians(i))*2048);
   costbl[i]=int(cos(radians(i))*2048);
 }
 
 balls=new Metamorph[50];
 for(int i=0;i<10;i++)
 {
   balls[i]=new Metaball(int(random(0,w)),int(random(0,h)),int(random(2,5)));
 }
 for(int i=0;i<40;i++)
 {
   balls[i+10]=new Metastar(int(random(0,w)),int(random(0,h)),int(random(3,8)));    
 }
}

void draw()
{
 renderBalls();
 for(int i=0;i<balls.length;i++)
 {
   balls[i].move(angle);
 }
 angle++;
}

void renderBalls()
{
 buffer.loadPixels();
 for(int x=0;x<w;x++)
 {
   for(int y=0;y<h;y++)
   {
     int sum=0;
     for(int i=0;i<balls.length;i++)
     {
         if(balls[i] instanceof Metastar)
           sum+=balls[i].getSum(x,y);
         else
           sum-=balls[i].getSum(x,y);
     }      
     buffer.pixels[y*w+x]=palette[constrain(sum,0,palette.length-1)];
   }
 }  
 buffer.updatePixels();  
 image(buffer,0,0,w*4,h*4);
}
Page Index Toggle Pages: 1