MSAFluid for processing

About

This is a library for solving real-time fluid dynamics simulations based on Navier-Stokes equations and Jos Stam's paper on Real-Time Fluid Dynamics for Games. While I wrote the library primarily for processing it has no dependency on processing libraries and the source can be used with any Java application.

C++ version for openFrameworks can be found here.

The video below is a demo of a processing sketch using MSAFluid, being controlled by MSA Remote on iPhone (You can view the video in HD and download a 1080p version at vimeo).

Superfluid vs Particle from jimi hertz on Vimeo.

MSA Fluids test from den ivanov on Vimeo.

MSAFluid on a MultiTouch table from xTUIO from Sandor Rozsa on Vimeo.

 

You can play with a live interactive version here. (This applet responds to tuio if running locally. If anyone knows how to get this working when embedded in a browser hosted remotely please lemme know!).

A lot of the fluid solving code and algorithms are from Jos Stam's paper. I tried to wrap it up in an easy to use API to provide methods for adding & getting forces and color at any position. Other useful resources and implementations I looked at and borrowed from while building this lib are:
* Mike Ash (C) - http://mikeash.com/?page=pyblog/fluid-simulation-for-dummies.html
* Alexander McKenzie (Java) - http://www.multires.caltech.edu/teaching/demos/java/stablefluids.htm
* Pierluigi Pesenti (AS3 port of Alexander's) - http://blog.oaxoa.com/2008/01/21/actionscript-3-fluids-simulation/
* Gustav Taxen (C) - http://www.nada.kth.se/~gustavt/fluids/
* Dave Wallin (C++) - http://nuigroup.com/touchlib/ (uses portions from Gustav's)

and some other Stam solver implementations I've come across since writing this are:
* Chris Sugrue & Arturo Castro (C++) - http://csugrue.com/code/?p=15
* Alain Ramos (C++) - http://code.google.com/p/ofxding/downloads/list
* Mick West - http://www.gamasutra.com/view/feature/1549/practical_fluid_dynamics_part...
* Mick West - http://www.gamasutra.com/view/feature/1615/practical_fluid_dynamics_part...
* NVidia (C / Cg) - http://developer.download.nvidia.com/SDK/9.5/Samples/gpgpu_samples.html - gpgpu is the future

...and you can find a brilliant AS3 port of this library at blog.inspirit.ru/?p=248

P.S. again, thanks to Plasma Pong for inspiring this!

Download

You can download the library, source, docs and examples from http://memo.tv/files/memotv/MSAFluid.zip.

Installation

Unzip and put the extracted MSAFluid folder into the libraries folder of your processing sketches. Reference and examples are included in the MSAFluid folder.

Reference

Have a look at the javadoc reference here. a copy of the reference is included in the .zip as well.

Tested

Platform mac osx
Processing 1.0.3
Dependencies none

License

This library is released under the new BSD License

Demo Source Code

Find a list of examples in the current distribution of MSAFluid, or see sample code below.

Examples included show how to add forces and dye (using mouse and/or tuio), draw the fluid and read fluid velocities to affect particles. Often it is quite effective to not draw the fluid at all but just use it to affect particles (if using mouse, click to toggle draw mode; if using tuio, doubletap).

Sample processing code to setup, draw and add forces/dye to a fluid.

import msafluid.*;
 
import processing.opengl.*;
import javax.media.opengl.*;
 
final float FLUID_WIDTH = 120;
 
float invWidth, invHeight;    // inverse of screen dimensions
float aspectRatio, aspectRatio2;
 
MSAFluidSolver2D fluidSolver;
 
PImage imgFluid;
 
boolean drawFluid = true;
 
void setup() {
    size(960, 640, OPENGL);    // use OPENGL rendering for bilinear filtering on texture
//    size(screen.width * 49/50, screen.height * 49/50, OPENGL);
    hint( ENABLE_OPENGL_4X_SMOOTH );    // Turn on 4X antialiasing
 
    invWidth = 1.0f/width;
    invHeight = 1.0f/height;
    aspectRatio = width * invHeight;
    aspectRatio2 = aspectRatio * aspectRatio;
 
    // create fluid and set options
    fluidSolver = new MSAFluidSolver2D((int)(FLUID_WIDTH), (int)(FLUID_WIDTH * height/width));
    fluidSolver.enableRGB(true).setFadeSpeed(0.003).setDeltaT(0.5).setVisc(0.0001);
 
    // create image to hold fluid picture
    imgFluid = createImage(fluidSolver.getWidth(), fluidSolver.getHeight(), RGB);
 
}
 
 
void mouseMoved() {
    float mouseNormX = mouseX * invWidth;
    float mouseNormY = mouseY * invHeight;
    float mouseVelX = (mouseX - pmouseX) * invWidth;
    float mouseVelY = (mouseY - pmouseY) * invHeight;
 
    addForce(mouseNormX, mouseNormY, mouseVelX, mouseVelY);
}
 
void draw() {
    fluidSolver.update();
 
    if(drawFluid) {
        for(int i=0; i<fluidSolver.getNumCells(); i++) {
            int d = 2;
            imgFluid.pixels[i] = color(fluidSolver.r[i] * d, fluidSolver.g[i] * d, fluidSolver.b[i] * d);
        }  
        imgFluid.updatePixels();//  fastblur(imgFluid, 2);
        image(imgFluid, 0, 0, width, height);
    } 
}
 
void mousePressed() {
    drawFluid ^= true;
}
 
 
// add force and dye to fluid, and create particles
void addForce(float x, float y, float dx, float dy) {
    float speed = dx * dx  + dy * dy * aspectRatio2;    // balance the x and y components of speed with the screen aspect ratio
 
    if(speed > 0) {
        if(x<0) x = 0; 
        else if(x>1) x = 1;
        if(y<0) y = 0; 
        else if(y>1) y = 1;
 
        float colorMult = 5;
        float velocityMult = 30.0f;
 
        int index = fluidSolver.getIndexForNormalizedPosition(x, y);
 
        color drawColor;
 
        colorMode(HSB, 360, 1, 1);
        float hue = ((x + y) * 180 + frameCount) % 360;
        drawColor = color(hue, 1, 1);
        colorMode(RGB, 1);  
 
        fluidSolver.rOld[index]  += red(drawColor) * colorMult;
        fluidSolver.gOld[index]  += green(drawColor) * colorMult;
        fluidSolver.bOld[index]  += blue(drawColor) * colorMult;
 
        fluidSolver.uOld[index] += dx * velocityMult;
        fluidSolver.vOld[index] += dy * velocityMult;
    }
}