Browsing the archives for the simulation category.


APE – Actionscript Physics Engine – Test 1

actionscript 3, APE, fun, Math, Physics, simulation

Hi there, I started today playing with APE – Actionscript Physics Engine (Alpha 0.45) and it’s really cool stuff.

My intention would be creating a physical based little funny game, but I need to start with some simple experiments so why not post’em here…

There are issues I still don’t understand so don’t know if are bugs or simply I should read APIs deeper. One thing I cannot realize is why if I change runtime the height or width properties of a RectangleParticle or the radius property of a CircleParticle, collisions are calculated correctly upon the new values, but cannot really manage to force a correct repaint with the new dimensions.
In the code that follows I have trhee rotating RectangleParticles with the alwaysRepaint parameter set to true and I can correctly see the rectangles rotating, but if I change size runtime nothing changes (except the collision). Neither calling the particle.paint() method at everyframe, which is an alternative to alwaysRepaint method, works.

However everything else is really supercool and moving a lot of self colliding particles @60fps!

Here we go with my first test:


ape_test11.gif
Run the test

Simply set the import folders for external classes in your Flash or Flex project pointing to the folder where you downloaded APE and copy these few lines of code to the first frame in you timeline.

import flash.events.Event;
import flash.events.MouseEvent;
import flash.display.Sprite;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.filters.GlowFilter;
import org.cove.ape.*;
import com.oaxoa.components.FrameRater;

stage.frameRate = 60;
addEventListener(Event.ENTER_FRAME, run);

APEngine.init(1/4);
APEngine.container = this;

// masslessforce aka gravity
APEngine.addMasslessForce(new Vector(0,4));

var defaultGroup:Group = new Group();
defaultGroup.collideInternal = true;

// instance the three rectangles
var rp:RectangleParticle = new RectangleParticle(150,250,250,25,0,true);
var rp2:RectangleParticle = new RectangleParticle(400,250,250,25,0,true);
var rp3:RectangleParticle = new RectangleParticle(275,450,150,20,0,true);

// set some graphical style
rp.setFill(0x666666);
rp2.setFill(0x666666);
rp3.setFill(0x666666);
rp.setLine(0, 0xff0000, 0);
rp2.setLine(0, 0xff0000, 0);
rp3.setLine(0, 0xff0000, 0);

// fixed particles are not always repainted for speed issues, but these ones will
// be rotating, so we need to force the repaint at every frame
rp.alwaysRepaint=true;
rp2.alwaysRepaint=true;
rp3.alwaysRepaint=true;

// add rectangles
defaultGroup.addParticle(rp);
defaultGroup.addParticle(rp2);
defaultGroup.addParticle(rp3);

APEngine.addGroup(defaultGroup);

var a:Number=0;
var r:Number=15;
function run(evt:Event):void {
	// check for removal if the particle is offscreen
	for each (var p:AbstractParticle in defaultGroup.particles) {
		if (p.py>550+r || p.px<-r || p.px>550+r) {
			defaultGroup.removeParticle(p);
		}
	}
	
	// some rotating fun
	a+=4;
	var arad:Number=a/180*Math.PI;
	rp.angle=Math.cos(arad)*45;
	rp2.angle=-Math.cos(arad)*45;
	rp3.angle=a*2;
	
	// do the simulation and the render step
	APEngine.step();
	APEngine.paint();
	
}
// timer to add particles
var timer:Timer=new Timer(50);
timer.addEventListener(TimerEvent.TIMER, ontimer);
timer.start();
function ontimer(event:TimerEvent):void {
	var cp:CircleParticle = new CircleParticle(Math.random()*350+100,-20,Math.random()*r+1);
	cp.setFill(0x444444*Math.random()+0xbbbbbb);
	cp.setLine(0, 0xff0000, 0);
	defaultGroup.addParticle(cp);
}

// add the frame rater to check how smooth it is running
var fr:FrameRater=new FrameRater(0xcccccc);
addChild(fr);

Byez

7 Comments

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
Newer Posts »