Browsing the archives for the fluids category.


Actionscript 3 – as3 | Rorschach mask (realtime animated inkblots)

actionscript 3, BitmapData, classes, fluids, fun, morphing, perlinNoise, simulation, threshold

Everybody knows about Rorschach inkblot tests, and they always charmed me… It’s like looking clouds and recognizing different shapes.

However I must admit the real turn on came after watching Watchmen the movie. Seeing those animated inkblots on the face of the character symbolically named Rorschach is something really fascinating. So here we are, after about an hour of coding fun this is what I came up with.


Click to start

Here after adding a cool persistence of the ink (call it motion blur if you like), and the face oval:


Click to start

How it’s made is quite simple:

- perlinNoise a bitmapData
- draw a circular gradient mask (I used a beginGradientFill on a Shape/Sprite)
- BitmapData.draw() the mask onto the perlinNoise bitmapData
- threshold
- (optional) a little blur to simulate antialias

Since a picture is worth a thousand words:


Click to see it moving

I’ve built a class to do this easily. Six parameters are optional to instance it:

public function Rorschach(w:uint=400, h:uint=400, mirror:Boolean=true, seed:uint=0, ovalClass:Class=null, ovalScale:Number=1, ovalYOffset:Number=0)

ovalClass is the (optional) reference to the class containing the face background.

So the usage is very simple, here an example:

import com.oaxoa.fx.Rorschach;
addChild(new Rorschach(300,300));
//addChild(new Rorschach(300,300,true,0,blankMaskImage, 1.2, -20));

Download Rorschach class for your experimentation pleasure.

All nice, but the aim of this simple experiment is seeing Rorschach animated face in action so here we go… we will be using this class in a practical example.

To simulate 3D volume and perspective we need flash CS4 (or any 3D engine):
we create a MovieClip which has the Rorschach class instanced and a rectangular solid background for composition reference in it. Then we set layer visibility to wireframe and add another instance to the stage, which is flipped horizontally.
Just position the two instances using 3D rotations and we are ready.

Just build et voilĂ !


Click to start

Rorschach’s Journal. October 12th, 1985. Dog Carcass in alley this morning, tire tread on burst stomach. This city is afraid of me. I have seen its true face. The streets are extended gutters and the gutters are full of blood and when the drains finally scab over, all the vermin will drown. The accumulated filth of all their sex and murder will foam up about their waists and all the whores and politicians will look up and shout ‘Save us!’ And I’ll look down, and whisper ‘no.’

The last (and D-E-F-I-N-I-T-I-V-E! :-D ) usage for this is apply it to a realtime webcam face detection algorithm. I used this originally created by Ohtsuka Masakazu and optimized by Mario Klingemann. The result is totally weird (don’t expect photoreal hollywood effect LOL) but it’s funny, even if the face detection algorithm is not perfectly smooth.

Have fun being the definitive comics anti-hero:


Click to start

6 Comments

Actionscript 3 Lightning class – step 2

actionscript 3, algorithms, BitmapData, fluids, fun, Math, perlinNoise, simulation

The first rule for the good blogger I read 18 months ago (when I started this blog) was: “Never, ever post on Sunday… people coming back to the office on monday morning will find tons of weekend rss notifications and miss your post among them”.

But… since I am not a good blogger and since this blog is mine… I do post whenever I want :-D

Here we are with the second post dedicated to the lightning class I’m working on. It had to force myself to sit down here and write again about this, I am working on other 3 nice experiments and the fun part of this class for me has already gone, now starts the boring part: optimize/parametrize/explainize. But I think it could be interesting for someone, so here we are.

Still no code, I still have to optimize/parametrize but I can start to “explain-ize”. In the first post we just saw a couple of samples running. Some people asked me for the raw logic behind this.

The key here are two perlinNoise, one for simulate the macro behaviour and one for the micro variations. As often a monodimensional (1px height) perlinNoise is perfect to simulate bidimensional effects, since perlinNoise images contain an additional dimension defined by luminosity variations.

So, if from a 2D perlinNoise image you can easily get a 3D voxelTerrain (X+Y+Height), from a 1D image you can easily get for instance 2D mountains (X+Height).
1px height perlinNoise images are very fast to render so they do not kill your cpu even if many of them are used.

I have prepared two “debug” sample movies. The first is to show how the two perlinNoise bitmapDatas affect the movement of the final shape (children are disabled for clarity purpose).


Debug sample #1

As you can see there are 3 different grey bars under the lightning:

  • The first one is the perlinNoise used to displace the discharge on a micro scale
  • the second one for the macro movements (this is really what makes it feel as real)
  • the third one is the image merged onto the second one to smooth the edges.

Obviously the 3 bitmaps are 1 pixel height, I just stretched them here to 30px to make ‘em visible.

Speaking about perlinNoise edges smoothing this is the fact: without any smoothing the extremities of the perlinNoise have a random value between 0×000000 and 0xffffff which stand for black and white. Black value will displace the beam in a negative direction, white value will displace it in a positive direction. To be sure the edges are sticked to the x/y coords of the graphic that simulates the origin of the discharge, we need no displacement at the beginning and at the end.
No displacement=grey value (0×808080).

The easiest way to do this is to create a vector shape, fill it with a gradient (alphas=[1,0,0,1]), adjust ration accordingly to the smooth percentage and draw it onto the macro variations bitmap (Bitmap.draw(…)). Is off course possible to to this directly using bitmap methods. But I found this being the most natural for me. I like the idea to mix vector and bitmaps using the easiest way for what I need.

Naturally you cannot set the last pixels to medium gray without some smooth transition, and so we use the gradient. Check this sample with code snippet:


perlinNoise edges smoothing

const grey:uint=0x808080;
var smooth:Sprite=new Sprite();
var ratioOffset:uint=smoothPercentage/100*128;
smooth.graphics.beginGradientFill(GradientType.LINEAR, [grey, grey, grey, grey], [1, 0, 0, 1], [0, ratioOffset, 255-ratioOffset, 255], matrix);
smooth.graphics.drawRect(0, 0, w, 1);

I used the smoothing only on the macro bitmap, not one the micro too save some cpu cycle on a barely visible detail and because I like the micro variations on the edges too.

The second one shows the same concept applied also to children with a slider to vary the children birth probability:

[demo removed cause I overwrote the files LOL. Don't ask to get them from backups please, simply refer to part 3 post :) ]

The main beam can create bridges of electricity, or “children”. A child is simply another instance of the class with a “generationNumber” parameter to manage descendant probability and avoid infite recursion. All the rules described until now applies to children too. Children need to be sticked to the parent beam so they always have a non-variable smoothing of 50%.

Coming soon with part 3 and the full class.
As always leave a comment if you find something interesting.

12 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