Actionscript 3 fluids simulation

actionscript 3, BitmapData, fluids, simulation

Hi there, sorry for not posting for a while but have been days really full of work.
I find some time now to post a new experiment I did some week ago.

I was searching about fluids simulation and I found a lot of interesting studies from Jos Stam including this document entitled “Real-Time Fluid Dynamics for Games“.

Then I found a post on Nulldesign.de about a java fluid solver he found which is implementing the same routines from Jos Stam.

So I simply trashed my evolving algorythm and translate the java one into AS3 eliminated the walls calculation (I don’t like small spaces) and added a blur filter.

The result is quite interesting and you can see it here.

fluidsolver.jpg
See it in action
.

Simply click and drag for a while to add some fluid into the running simulation. Better looking when adding many times a little quantity in different places.
Press SPACE to reset simulation.


The main delusion was about speed. Ok, the actionscript virtual machine 2 (AVM2) is incredibly faster than AVM1 and the bitmapData class too, but stil not enough to compete with java. The most funny thing is that in the original document “Real-Time Fluid Dynamics for Games” at page 6, there the following loop:

void diffuse ( int N, int b, float * x, float * x0, float diff, float dt ) { 
	int i, j, k; 
	float a=dt*diff*N*N; 
	 
	for ( k=0 ; k<20 ; k++ ) { 
		for ( i=1 ; i<=N ; i++ ) { 
			for ( j=1 ; j<=N ; j++ ) { 
				x[IX(i,j)] = (x0[IX(i,j)] + a*(x[IX(i-1,j)]+x[IX(i+1,j)]+ x[IX(i,j-1)]+x[IX(i,j+1)]))/(1+4*a); 
			} 
		} 
		set_bnd ( N, b, x ); 
	} 
} 

Notice the main for loop starting from k=0 and ending with k=20?
I thought this was used to average the relaxation routine result on 20 samples or something similar but if you look at it better you will see than there are no increments, decrements or averaging functions, but just simple value assignment and not using k index at all.

In the java fluid solver the same loop is implemented (and the result is still faster than mine in as3 :-( ). I simply totally removed the k loop and gained 20x speed with (apparently) no cons.

However the main coclusion is that Java is still a lot faster on some math operations and pixel access as described in this comparison paper i found.

Here is the as3 fluid solver class.

Extract it to your as3 classes dir after creating this path:
com/oaxoa/misc/

This class doesn't render or manage user interaction, just solve the fluids interactions in a linearized matrix. To see something on the screen you just need some lines to manage the data. I am actually to busy/lazy to wrap this into a class, maybe I will do, until then just paste this code to the main timeline:

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Rectangle;
import flash.events.KeyboardEvent;
import flash.filters.BlurFilter;
import com.oaxoa.misc.FluidSolver;
import com.oaxoa.components.FrameRater;

// frame dimensions (dxd pixels)
var d:int = 200;

// solver variables
var n:int = 40;
var dt:Number = 0.52;
var fs:FluidSolver = new FluidSolver();

// mouse position
var xOld:int;
var yOld:int;

// cell index
var tx:int;
var ty:int;

// cell dimensions
var dg:int;
var dg_2:int;

// cell position
var dx:int;
var dy:int;

// fluid velocity
var u:int;
var v:int;
var c:int;

var blur:BlurFilter=new BlurFilter(5,5,5);
var bd:BitmapData=new BitmapData(d,d, false, 0x000000);
var bmp:Bitmap=new Bitmap(bd);
bmp.filters=[blur];
var holder:Sprite=new Sprite();
holder.addChild(bmp);
addChild(holder);

var fr:FrameRater=new FrameRater(0xffffff, true);
fr.y=10;
addChild(fr);

function reset():void {
	dg   = d  / n;
	dg_2 = dg / 2;
	fs.setup(n, dt);
}

function paint():void {
	var c:int;
	// clear screen
	bd.fillRect(new Rectangle(0, 0, d, d), 0x000000);

	fs.velocitySolver();
	fs.densitySolver();
	for (var i:int = n; i >= 1; i--) {
		// x position of current cell
		dx = int(( (i - 0.5) * dg ));
		for (var j:int = n; j >= 1; j--) {
			// y position of current cell
			dy = int(( (j - 0.5) * dg ));
			// draw density
			var dd:Number=fs.d[I(i, j)];
			if (dd > 0) {
				var r:Number=dd*255;
				if(r>255) r=255;
				c = r << 16 | r << 8 | r;
				if (c < 0) {
					c = 0;
				}
				bd.fillRect(new Rectangle(dx-dg_2, dy-dg_2, dg, dg), c);
			}
		}
	}
}

reset();

addEventListener(Event.ENTER_FRAME, onframe);
function onframe(event:Event):void {
	paint();
}

stage.addEventListener(KeyboardEvent.KEY_UP, onkeyup);
function onkeyup(event:KeyboardEvent):void {
	if(event.keyCode==32) reset();
}
var adding:Boolean=false;
holder.addEventListener(MouseEvent.MOUSE_MOVE, onmove);
holder.addEventListener(MouseEvent.MOUSE_DOWN, ondown);
holder.addEventListener(MouseEvent.MOUSE_UP, onup);
function ondown(event:MouseEvent):void {
	adding=true;
}
function onup(event:MouseEvent):void {
	adding=false;
}
function onmove(event:MouseEvent):void {
	if(adding) {
		tx=int(mouseX/dg);
		ty=int(mouseY/dg);
		if(tx>n) tx=n;
		if(tx<1) tx=1;
		if(ty>n) ty=n;
		if(ty<1) ty=1;
		
		fs.dOld[I(tx, ty)]=30;
	}
}

// util function for indexing
function I(i:int, j:int):int {
	return i + (n + 2) * j;
}





If you want to discuss about this just leave a comment.

Anyway, just thought I'd share with you guys, I'm thinking of getting a reseller hosting plan so I can enhance the perfomance of the site and start my own hosting business, maybe in the future.

17 Comments

15 Comments

  1. jesse harshbarger  •  Jan 21, 2008 @6:46 pm

    I am attempting to download the FluidSolver files but the link is giving a 404 error:

    Here is the as3 fluid solver class.http://blog.oaxoa.com/2008/01/21/actionscript-3-fluids-simulation/wp-content/classes/FluidSolver.zip

  2. Pierluigi Pesenti  •  Jan 21, 2008 @8:59 pm

    Sorry, my mistake. Now the link is correct. Cheers

  3. Hazy  •  Jan 23, 2008 @6:35 pm

    Good effort… But, that loop does do something and it’s very important, it repeats the same process 20 times. Each time the assignment is made x changes because the calculation is referencing both x0 and itself and so uses the previous result.

    Without the loop velocity is lost when spreading out because it can’t change direction. This image shows the difference between 12 loops and 1 loop…

    http://img3.freeimagehosting.net/uploads/7659ff0724.jpg

    Having said that though there’s not really time enough left to do it in Actionscript. Stam also has a patented method published based on the FFT which is much faster.

  4. Hazy  •  Jan 23, 2008 @6:38 pm

    Video of that fluid solver in game-play….

    http://www.youtube.com/watch?v=K6tvSH_1wBI

  5. Pierluigi Pesenti  •  Jan 23, 2008 @7:34 pm

    Thanks for the tip Hazy, you are totally right, I didn’t noticed it.
    However why 20 times and not 200? I have tried to raise it to 20 and it’s different but It just look like the fluid is “harder” (I mean more density).

    However flash hasn’t yet enogh computational power to do it.

    That videogame you posted is flash??

  6. Hazy  •  Jan 23, 2008 @8:56 pm

    You’ll have to re-tweek the parameters, lots of density and velocity is lost with lower numbers of passes. On the video I’m doing 10 loops and the discrepancy in accuracy is large enough that the density used for the small flames is exactly the same as for the big explosions… they’re just covering more area by being given faster velocity if you follow me, when they shouldn’t do that at all. So I have to compensate for that by being a bit tricky.

    Somewhere around Stam states around 50 iterations makes the simulation close enough to properly mass conserving. Because each relaxation is half again closer to an accurate solution the difference between 50 and 200 loops is a lot, lot less than 10 and 50.

    The fluid in the video is run on GPU shaders at 512×256 resolution, even at 10 iterations (including arbitrary boundaries) it takes up most of my GeForce 7800′s processing power. The game was originally Flash, if AS3 had the option to use non-managed arrays we would have been able to stick with it, instead we switched to C#/XNA.

    I think my implementation is double the resolution of Plasma Pong if I remember rightly (http://www.greatgamesexperiment.com/game/plasmapong).

  7. Mediakid  •  Jan 29, 2008 @4:22 pm

    [...] Oaxoa Blog 是超強的屌人,他的作品 Actionscript 3 fluids simulation 效果超帥的,按這瞧瞧 Demo 。 [...]

  8. Thomas  •  Nov 18, 2008 @1:30 pm

    Very nice fluid system!

    When I run your example from this Website, I get around 30 FPS, but when i download the FluidSolver class and run the testing code I only get 12-15 FPS.

    I have changed the framerate to 30 FPS in my document properties and I use Flash CS3 IDE, Flash Player 9 and the publish setting (not debug mode).

    Any suggestions?

  9. Flo  •  May 13, 2009 @8:21 am

    Looks very nice!
    unfortunately its getting unbelivable slow when raising up the amount of lines (n= 80 instead of 40). is there an enhancement so far?
    but anyway, great work!!!
    cheers

  10. jairus  •  Oct 26, 2009 @11:46 pm

    just noticed, maybe it might be faster if you performed a bd.lock() at the start of the paint() method, and a bd.unlock() at the end of the paint() method. ?

  11. poco  •  Mar 14, 2010 @5:13 pm

    A very useful article
    thanks for sharing the code!

  12. Candida Thonney  •  Jun 22, 2010 @11:50 am

    Suprisingly enough, I’m drafting out a dissertation like this blog post at this present time. Consequently, thanks for sharing this intreguing resource to be freely accessible to websurfers. I can contact you when I’ve completed it, if you want.

  13. Atila Correia  •  Aug 30, 2010 @8:40 pm

    Hello guys,

    is it possible to make this simulation using Pixel Bender?

    Best Regards,

  14. Pierluigi Pesenti  •  Aug 31, 2010 @6:29 am

    Sure it is. It should also be faster.

  15. Martin  •  Sep 29, 2010 @11:40 pm

    Thanks, Great post. I was wondering is diffuse_bad so bad that it’s unusable (and one has to deal with Gauss-Seidel loops), what if one limits the density to [0,1], would it still be unstable? Another thing if I implement the loops with Kronecker products (i.e. through matrix multiplication), would it be faster for AS3? I know that it makes MATLAB code run way faster in some instances.

2 Trackbacks

Leave a Reply

Allowed tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>