
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.
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.
